diff --git a/.mock/fern.config.json b/.mock/fern.config.json index 197432e..4471af3 100644 --- a/.mock/fern.config.json +++ b/.mock/fern.config.json @@ -1,4 +1,4 @@ { "organization" : "crossmint", - "version" : "0.31.24" + "version" : "0.33.5" } \ No newline at end of file diff --git a/README.md b/README.md index 1bb89d6..7a511a9 100644 --- a/README.md +++ b/README.md @@ -39,13 +39,7 @@ following namespace: import { Crossmint } from "crossmint"; const request: Crossmint.CheckoutCreateOrderRequest = { - payment: { - method: Crossmint.EvmPaymentMethods.ArbitrumSepolia, - currency: Crossmint.EvmPaymentCurrency.Eth, - }, - lineItems: { - collectionLocator: "crossmint:", - } + ... }; ``` @@ -68,15 +62,6 @@ try { } ``` -Error codes are as followed: - -| Status Code | Error Type | -| ----------- | -------------------------- | -| 400 | `BadRequestError` | -| 403 | `ForbiddenError` | -| 404 | `NotFoundError` | -| 503 | `ServiceUnavailableError` | - ## Advanced ### Retries @@ -132,7 +117,6 @@ runtimes: - Deno v1.25+ - Bun 1.0+ - React Native -- Browser ### Customizing Fetch Client diff --git a/package.json b/package.json index a15ea95..3b4f9f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "crossmint", - "version": "0.1.7", + "version": "0.1.8", "private": false, "repository": "https://github.com/fern-demo/crossmint-typescript-sdk", "main": "./index.js", @@ -13,7 +13,7 @@ }, "dependencies": { "url-join": "4.0.1", - "form-data": "4.0.0", + "form-data": "^4.0.0", "formdata-node": "^6.0.3", "node-fetch": "2.7.0", "qs": "6.11.2" diff --git a/reference.md b/reference.md index f5b7041..6c8d62b 100644 --- a/reference.md +++ b/reference.md @@ -1,42 +1,35 @@ -## Checkout +# Reference -
client.checkout.createOrder({ ...params }) -> Crossmint.CreateOrderResponse +## Checkout +
client.checkout.createOrder({ ...params }) -> Crossmint.CreateOrderResponse
-
#### 📝 Description
-
-
Creates a new order that can be used to complete a headless checkout.
-
-
-
#### 🔌 Usage
-
-
-```ts +```typescript await client.checkout.createOrder({ payment: { method: Crossmint.EvmPaymentMethods.ArbitrumSepolia, @@ -49,228 +42,175 @@ await client.checkout.createOrder({ ```
-
-
-
#### ⚙️ Parameters
-
-
-**request: `Crossmint.CheckoutCreateOrderRequest`** +**request:** `Crossmint.CheckoutCreateOrderRequest`
-
-
-**requestOptions: `Checkout.RequestOptions`** +**requestOptions:** `Checkout.RequestOptions`
-
-
-
-
-
client.checkout.getOrder(orderId) -> Crossmint.OrderObject - +
client.checkout.getOrder(orderId) -> Crossmint.OrderObject
-
#### 📝 Description
-
-
Get specific order by ID
-
-
-
#### 🔌 Usage
-
-
-```ts +```typescript await client.checkout.getOrder("orderId"); ```
-
-
-
#### ⚙️ Parameters
-
-
-**orderId: `string`** +**orderId:** `string` This is the identifier for the order with UUID format. **Example:** `9c82ef99-617f-497d-9abb-fd355291681b`
-
-
-**requestOptions: `Checkout.RequestOptions`** +**requestOptions:** `Checkout.RequestOptions`
-
-
-
-
-
client.checkout.editOrder(orderId, { ...params }) -> Crossmint.OrderObject - +
client.checkout.editOrder(orderId, { ...params }) -> Crossmint.OrderObject
-
#### 📝 Description
-
-
Edit an existing order. You can update the recipient, the payment method, and/or the locale.
-
-
-
#### 🔌 Usage
-
-
-```ts +```typescript await client.checkout.editOrder("orderId"); ```
-
-
-
#### ⚙️ Parameters
-
-
-**orderId: `string`** +**orderId:** `string` This is the identifier for the order with UUID format. **Example:** `9c82ef99-617f-497d-9abb-fd355291681b`
-
-
-**request: `Crossmint.OrderDto`** +**request:** `Crossmint.OrderDto`
-
-
-**requestOptions: `Checkout.RequestOptions`** +**requestOptions:** `Checkout.RequestOptions`
-
-
-
-
diff --git a/src/api/resources/checkout/client/Client.ts b/src/api/resources/checkout/client/Client.ts index db435cb..4547931 100644 --- a/src/api/resources/checkout/client/Client.ts +++ b/src/api/resources/checkout/client/Client.ts @@ -64,12 +64,13 @@ export class Checkout { headers: { "X-Fern-Language": "JavaScript", "X-Fern-SDK-Name": "crossmint", - "X-Fern-SDK-Version": "0.1.7", + "X-Fern-SDK-Version": "0.1.8", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, ...(await this._getCustomAuthorizationHeaders()), }, contentType: "application/json", + requestType: "json", body: serializers.CheckoutCreateOrderRequest.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, maxRetries: requestOptions?.maxRetries, @@ -176,12 +177,13 @@ export class Checkout { headers: { "X-Fern-Language": "JavaScript", "X-Fern-SDK-Name": "crossmint", - "X-Fern-SDK-Version": "0.1.7", + "X-Fern-SDK-Version": "0.1.8", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, ...(await this._getCustomAuthorizationHeaders()), }, contentType: "application/json", + requestType: "json", timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, maxRetries: requestOptions?.maxRetries, abortSignal: requestOptions?.abortSignal, @@ -292,12 +294,13 @@ export class Checkout { headers: { "X-Fern-Language": "JavaScript", "X-Fern-SDK-Name": "crossmint", - "X-Fern-SDK-Version": "0.1.7", + "X-Fern-SDK-Version": "0.1.8", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, ...(await this._getCustomAuthorizationHeaders()), }, contentType: "application/json", + requestType: "json", body: serializers.OrderDto.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, maxRetries: requestOptions?.maxRetries, diff --git a/src/core/fetcher/Fetcher.ts b/src/core/fetcher/Fetcher.ts index d368636..02af7dc 100644 --- a/src/core/fetcher/Fetcher.ts +++ b/src/core/fetcher/Fetcher.ts @@ -20,7 +20,9 @@ export declare namespace Fetcher { maxRetries?: number; withCredentials?: boolean; abortSignal?: AbortSignal; + requestType?: "json" | "file" | "bytes"; responseType?: "json" | "blob" | "streaming" | "text"; + duplex?: "half"; } export type Error = FailedStatusCodeError | NonJsonError | TimeoutError | UnknownError; @@ -62,7 +64,10 @@ export async function fetcherImpl(args: Fetcher.Args): Promise(args: Fetcher.Args): Promise= 200 && response.status < 400) { + if (args.duplex && args.responseType === "streaming") { + responseBody = (await import("stream")).Readable.from(responseBody as any); + } + return { ok: true, body: responseBody as R, diff --git a/src/core/fetcher/getFetchFn.ts b/src/core/fetcher/getFetchFn.ts index 68a2874..9fd9bfc 100644 --- a/src/core/fetcher/getFetchFn.ts +++ b/src/core/fetcher/getFetchFn.ts @@ -1,9 +1,15 @@ import { RUNTIME } from "../runtime"; + /** * Returns a fetch function based on the runtime */ export async function getFetchFn(): Promise { - // In Node.js environments, the SDK always uses`node-fetch`. + // In Node.js 18+ environments, use native fetch + if (RUNTIME.type === "node" && RUNTIME.parsedVersion != null && RUNTIME.parsedVersion >= 18) { + return fetch; + } + + // In Node.js 18 or lower environments, the SDK always uses`node-fetch`. if (RUNTIME.type === "node") { return (await import("node-fetch")).default as any; } diff --git a/src/core/fetcher/getRequestBody.ts b/src/core/fetcher/getRequestBody.ts index 9ad67ca..1138414 100644 --- a/src/core/fetcher/getRequestBody.ts +++ b/src/core/fetcher/getRequestBody.ts @@ -1,34 +1,14 @@ -import { RUNTIME } from "../runtime"; - -export async function getRequestBody(body: any, contentType: string): Promise { - let requestBody: BodyInit; - if (RUNTIME.type === "node") { - if (body instanceof (await import("formdata-node")).FormData) { - // @ts-expect-error - requestBody = body; - } else if (body instanceof (await import("stream")).Readable) { - // @ts-expect-error - requestBody = body; - } else { - requestBody = maybeStringifyBody(body, contentType ?? ""); - } - } else { - if (body instanceof (await import("form-data")).default) { - // @ts-expect-error - requestBody = body; - } else { - requestBody = maybeStringifyBody(body, contentType ?? ""); - } +export declare namespace GetRequestBody { + interface Args { + body: unknown; + type: "json" | "file" | "bytes" | "other"; } - return requestBody; } -export const maybeStringifyBody = (requestBody: any, contentType: string): Uint8Array | string => { - if (requestBody instanceof Uint8Array) { - return requestBody; - } else if (contentType === "application/x-www-form-urlencoded" && typeof requestBody === "string") { - return requestBody; +export async function getRequestBody({ body, type }: GetRequestBody.Args): Promise { + if (type.includes("json")) { + return JSON.stringify(body); } else { - return JSON.stringify(requestBody); + return body as BodyInit; } -}; +} diff --git a/src/core/fetcher/makeRequest.ts b/src/core/fetcher/makeRequest.ts index 1c1702b..8fb4bac 100644 --- a/src/core/fetcher/makeRequest.ts +++ b/src/core/fetcher/makeRequest.ts @@ -8,7 +8,8 @@ export const makeRequest = async ( requestBody: BodyInit | undefined, timeoutMs?: number, abortSignal?: AbortSignal, - withCredentials?: boolean + withCredentials?: boolean, + duplex?: "half" ): Promise => { const signals: AbortSignal[] = []; @@ -31,6 +32,8 @@ export const makeRequest = async ( body: requestBody, signal: newSignals, credentials: withCredentials ? "include" : undefined, + // @ts-ignore + duplex, }); if (timeoutAbortId != null) { diff --git a/src/core/runtime/runtime.ts b/src/core/runtime/runtime.ts index 30fe077..4d0687e 100644 --- a/src/core/runtime/runtime.ts +++ b/src/core/runtime/runtime.ts @@ -69,6 +69,7 @@ export const RUNTIME: Runtime = evaluateRuntime(); export interface Runtime { type: "browser" | "web-worker" | "deno" | "bun" | "node" | "react-native" | "unknown" | "workerd"; version?: string; + parsedVersion?: number; } function evaluateRuntime(): Runtime { @@ -109,6 +110,7 @@ function evaluateRuntime(): Runtime { return { type: "node", version: process.versions.node, + parsedVersion: Number(process.versions.node.split(".")[0]), }; } diff --git a/tests/custom/custom.test.ts b/tests/custom/custom.test.ts deleted file mode 100644 index 89e71bd..0000000 --- a/tests/custom/custom.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { CrossmintClient, CrossmintEnvironment } from "../../src"; - -describe("Integration", () => { - it("Create Order", async () => { - const client = new CrossmintClient({ - apiKey: process.env.CROSSMINT_API_KEY ?? "", - environment: CrossmintEnvironment.Staging - }) - - const create = await client.checkout.createOrder({ - "recipient": { - "email": "jsmith@example.com" - }, - "locale": "en-US", - "payment": { - "receiptEmail": "jsmith@example.com", - "method": "arbitrum-sepolia", - "currency": "eth" - }, - "lineItems": { - "collectionLocator": "crossmint:21251cb5-43b5-406a-9c1e-04342fbb36ee", - "callData": { - "totalPrice": "12.50" - } - } - }) - expect(create).toBeTruthy(); - - const response = await client.checkout.getOrder("08dbee92-1fff-4f5b-8ceb-91226e45631a"); - expect(response).toBeTruthy(); - }); -}); \ No newline at end of file diff --git a/tests/integration/client.test.ts b/tests/integration/client.test.ts deleted file mode 100644 index f9d52fa..0000000 --- a/tests/integration/client.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { CrossmintClient } from "../../src"; - -describe("Integration Tests", () => { - it("Test Create Order", async () => { - const client = new CrossmintClient({ - apiKey: process.env.CROSSMINT_API_KEY ?? "", - environment: process.env.CROSSMINT_BASE_URL ?? process.env.TESTS_BASE_URL, - }); - const response = await client.checkout.createOrder({ - payment: { - method: "arbitrum-sepolia", - currency: "eth", - }, - lineItems: { - collectionLocator: "crossmint:", - }, - }); - const expected = { - clientSecret: "_removed_", - order: { - orderId: "b2959ca5-65e4-466a-bd26-1bd05cb4f837", - phase: "payment", - locale: "en-US", - lineItems: [ - { - chain: "polygon-amoy", - quantity: 1, - }, - ], - quote: { - status: "valid", - quotedAt: "2024-06-07T16:55:44.653Z", - expiresAt: "2024-06-07T17:55:44.653Z", - totalPrice: { - amount: "0.0001375741", - currency: "eth", - }, - }, - payment: { - status: "awaiting-payment", - method: "base-sepolia", - currency: "eth", - preparation: { - chain: "base-sepolia", - payerAddress: "0x1234abcd...", - serializedTransaction: "0x02f90.....", - }, - }, - }, - }; - expect(response).toEqual(expected); - }, 10000); -}); diff --git a/tests/unit/fetcher/Fetcher.test.ts b/tests/unit/fetcher/Fetcher.test.ts deleted file mode 100644 index 0acfdda..0000000 --- a/tests/unit/fetcher/Fetcher.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Fetcher, fetcherImpl } from "../../../src/core/fetcher/Fetcher"; - -describe("Test fetcherImpl", () => { - let mockCreateUrl: jest.Mock; - let mockGetBody: jest.Mock; - let mockGetFetchFn: jest.Mock; - let mockRequestWithRetries: jest.Mock; - let mockGetResponseBody: jest.Mock; - - beforeEach(() => { - mockCreateUrl = jest.fn(); - mockGetBody = jest.fn(); - mockGetFetchFn = jest.fn(); - mockRequestWithRetries = jest.fn(); - mockGetResponseBody = jest.fn(); - - jest.mock("../../../src/core/fetcher/Fetcher", () => ({ - createUrl: mockCreateUrl, - getBody: mockGetBody, - getFetchFn: mockGetFetchFn, - requestWithRetries: mockRequestWithRetries, - getResponseBody: mockGetResponseBody, - })); - }); - - it("should handle successful request", async () => { - const mockArgs: Fetcher.Args = { - url: "https://httpbin.org/post", - method: "POST", - headers: { "X-Test": "x-test-header" }, - body: { data: "test" }, - contentType: "application/json", - }; - - mockCreateUrl.mockReturnValue("https://test.com"); - mockGetBody.mockResolvedValue(JSON.stringify({ data: "test" })); - mockGetFetchFn.mockResolvedValue(() => Promise.resolve()); - mockRequestWithRetries.mockResolvedValue({ status: 200 }); - mockGetResponseBody.mockResolvedValue({ result: "success" }); - - const result = await fetcherImpl(mockArgs); - expect(result.ok).toBe(true); - // @ts-expect-error - expect(result.body.json).toEqual({ data: "test" }); - }); -}); diff --git a/tests/unit/fetcher/createRequestUrl.test.ts b/tests/unit/fetcher/createRequestUrl.test.ts deleted file mode 100644 index f2cd24b..0000000 --- a/tests/unit/fetcher/createRequestUrl.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { createRequestUrl } from "../../../src/core/fetcher/createRequestUrl"; - -describe("Test createRequestUrl", () => { - it("should return the base URL when no query parameters are provided", () => { - const baseUrl = "https://api.example.com"; - expect(createRequestUrl(baseUrl)).toBe(baseUrl); - }); - - it("should append simple query parameters", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { key: "value", another: "param" }; - expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?key=value&another=param"); - }); - - it("should handle array query parameters", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { items: ["a", "b", "c"] }; - expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?items=a&items=b&items=c"); - }); - - it("should handle object query parameters", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { filter: { name: "John", age: 30 } }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?filter%5Bname%5D=John&filter%5Bage%5D=30" - ); - }); - - it("should handle mixed types of query parameters", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { - simple: "value", - array: ["x", "y"], - object: { key: "value" }, - }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?simple=value&array=x&array=y&object%5Bkey%5D=value" - ); - }); - - it("should handle empty query parameters object", () => { - const baseUrl = "https://api.example.com"; - expect(createRequestUrl(baseUrl, {})).toBe(baseUrl); - }); - - it("should encode special characters in query parameters", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { special: "a&b=c d" }; - expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?special=a%26b%3Dc%20d"); - }); -}); diff --git a/tests/unit/fetcher/getFetchFn.test.ts b/tests/unit/fetcher/getFetchFn.test.ts deleted file mode 100644 index 48a3952..0000000 --- a/tests/unit/fetcher/getFetchFn.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { RUNTIME } from "../../../src/core/runtime"; -import { getFetchFn } from "../../../src/core/fetcher/getFetchFn"; - -describe("Test for getFetchFn", () => { - it("should get node-fetch function", async () => { - if (RUNTIME.type == "node") { - expect(await getFetchFn()).toEqual((await import("node-fetch")).default as any); - } - }); - - it("should get fetch function", async () => { - if (RUNTIME.type == "browser") { - const fetchFn = await getFetchFn(); - expect(typeof fetchFn).toBe("function"); - expect(fetchFn.name).toBe("fetch"); - } - }); -}); diff --git a/tests/unit/fetcher/getRequestBody.test.ts b/tests/unit/fetcher/getRequestBody.test.ts deleted file mode 100644 index 8e47b7a..0000000 --- a/tests/unit/fetcher/getRequestBody.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { RUNTIME } from "../../../src/core/runtime"; -import { getRequestBody, maybeStringifyBody } from "../../../src/core/fetcher/getRequestBody"; - -if (RUNTIME.type === "browser") { - require("jest-fetch-mock").enableMocks(); -} - -describe("Test getRequestBody", () => { - it("should return FormData as is in Node environment", async () => { - if (RUNTIME.type === "node") { - const formData = new (await import("formdata-node")).FormData(); - formData.append("key", "value"); - const result = await getRequestBody(formData, "multipart/form-data"); - expect(result).toBe(formData); - } - }); - - it("should stringify body if not FormData in Node environment", async () => { - if (RUNTIME.type === "node") { - const body = { key: "value" }; - const result = await getRequestBody(body, "application/json"); - expect(result).toBe('{"key":"value"}'); - } - }); - - it("should return FormData in browser environment", async () => { - if (RUNTIME.type === "browser") { - const formData = new (await import("form-data")).default(); - formData.append("key", "value"); - const result = await getRequestBody(formData, "multipart/form-data"); - expect(result).toBe(formData); - } - }); - - it("should stringify body if not FormData in browser environment", async () => { - if (RUNTIME.type === "browser") { - const body = { key: "value" }; - const result = await getRequestBody(body, "application/json"); - expect(result).toBe('{"key":"value"}'); - } - }); -}); - -describe("Test maybeStringifyBody", () => { - it("should return the Uint8Array", () => { - const input = new Uint8Array([1, 2, 3]); - const result = maybeStringifyBody(input, "application/octet-stream"); - expect(result).toBe(input); - }); - - it("should return the input for content-type 'application/x-www-form-urlencoded'", () => { - const input = "key=value&another=param"; - const result = maybeStringifyBody(input, "application/x-www-form-urlencoded"); - expect(result).toBe(input); - }); - - it("should JSON stringify objects", () => { - const input = { key: "value" }; - const result = maybeStringifyBody(input, "application/json"); - expect(result).toBe('{"key":"value"}'); - }); -}); diff --git a/tests/unit/fetcher/getResponseBody.test.ts b/tests/unit/fetcher/getResponseBody.test.ts deleted file mode 100644 index f3563ff..0000000 --- a/tests/unit/fetcher/getResponseBody.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { RUNTIME } from "../../../src/core/runtime"; -import { getResponseBody } from "../../../src/core/fetcher/getResponseBody"; - -if (RUNTIME.type === "browser") { - require("jest-fetch-mock").enableMocks(); -} - -describe("Test getResponseBody", () => { - it("should handle blob response type", async () => { - const mockBlob = new Blob(["test"], { type: "text/plain" }); - const mockResponse = new Response(mockBlob); - const result = await getResponseBody(mockResponse, "blob"); - // @ts-expect-error - expect(result.constructor.name).toBe("Blob"); - }); - - it("should handle streaming response type", async () => { - if (RUNTIME.type === "node") { - const mockStream = new ReadableStream(); - const mockResponse = new Response(mockStream); - const result = await getResponseBody(mockResponse, "streaming"); - expect(result).toBe(mockStream); - } - }); - - it("should handle text response type", async () => { - const mockResponse = new Response("test text"); - const result = await getResponseBody(mockResponse, "text"); - expect(result).toBe("test text"); - }); - - it("should handle JSON response", async () => { - const mockJson = { key: "value" }; - const mockResponse = new Response(JSON.stringify(mockJson)); - const result = await getResponseBody(mockResponse); - expect(result).toEqual(mockJson); - }); - - it("should handle empty response", async () => { - const mockResponse = new Response(""); - const result = await getResponseBody(mockResponse); - expect(result).toBeUndefined(); - }); - - it("should handle non-JSON response", async () => { - const mockResponse = new Response("invalid json"); - const result = await getResponseBody(mockResponse); - expect(result).toEqual({ - ok: false, - error: { - reason: "non-json", - statusCode: 200, - rawBody: "invalid json", - }, - }); - }); -}); diff --git a/tests/unit/fetcher/makeRequest.test.ts b/tests/unit/fetcher/makeRequest.test.ts deleted file mode 100644 index 5969d51..0000000 --- a/tests/unit/fetcher/makeRequest.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { RUNTIME } from "../../../src/core/runtime"; -import { makeRequest } from "../../../src/core/fetcher/makeRequest"; - -if (RUNTIME.type === "browser") { - require("jest-fetch-mock").enableMocks(); -} - -describe("Test makeRequest", () => { - const mockPostUrl = "https://httpbin.org/post"; - const mockGetUrl = "https://httpbin.org/get"; - const mockHeaders = { "Content-Type": "application/json" }; - const mockBody = JSON.stringify({ key: "value" }); - - let mockFetch: jest.Mock; - - beforeEach(() => { - mockFetch = jest.fn(); - mockFetch.mockResolvedValue(new Response(JSON.stringify({ test: "successful" }), { status: 200 })); - }); - - it("should handle POST request correctly", async () => { - const response = await makeRequest(mockFetch, mockPostUrl, "POST", mockHeaders, mockBody); - const responseBody = await response.json(); - expect(responseBody).toEqual({ test: "successful" }); - expect(mockFetch).toHaveBeenCalledTimes(1); - const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; - expect(calledUrl).toBe(mockPostUrl); - expect(calledOptions).toEqual( - expect.objectContaining({ - method: "POST", - headers: mockHeaders, - body: mockBody, - credentials: undefined, - }) - ); - expect(calledOptions.signal).toBeDefined(); - expect(calledOptions.signal).toBeInstanceOf(AbortSignal); - }); - - it("should handle GET request correctly", async () => { - const response = await makeRequest(mockFetch, mockGetUrl, "GET", mockHeaders, undefined); - const responseBody = await response.json(); - expect(responseBody).toEqual({ test: "successful" }); - expect(mockFetch).toHaveBeenCalledTimes(1); - const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; - expect(calledUrl).toBe(mockGetUrl); - expect(calledOptions).toEqual( - expect.objectContaining({ - method: "GET", - headers: mockHeaders, - body: undefined, - credentials: undefined, - }) - ); - expect(calledOptions.signal).toBeDefined(); - expect(calledOptions.signal).toBeInstanceOf(AbortSignal); - }); -}); diff --git a/tests/unit/fetcher/requestWithRetries.test.ts b/tests/unit/fetcher/requestWithRetries.test.ts deleted file mode 100644 index b53e043..0000000 --- a/tests/unit/fetcher/requestWithRetries.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { RUNTIME } from "../../../src/core/runtime"; -import { requestWithRetries } from "../../../src/core/fetcher/requestWithRetries"; - -if (RUNTIME.type === "browser") { - require("jest-fetch-mock").enableMocks(); -} - -describe("Test exponential backoff", () => { - let mockFetch: jest.Mock; - let originalSetTimeout: typeof setTimeout; - - beforeEach(() => { - mockFetch = jest.fn(); - originalSetTimeout = global.setTimeout; - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.useRealTimers(); - global.setTimeout = originalSetTimeout; - }); - - it("should retry on 408, 409, 429, 500+", async () => { - mockFetch - .mockResolvedValueOnce(new Response("", { status: 408 })) - .mockResolvedValueOnce(new Response("", { status: 409 })) - .mockResolvedValueOnce(new Response("", { status: 429 })) - .mockResolvedValueOnce(new Response("", { status: 500 })) - .mockResolvedValueOnce(new Response("", { status: 502 })) - .mockResolvedValueOnce(new Response("", { status: 200 })) - .mockResolvedValueOnce(new Response("", { status: 408 })); - - const responsePromise = requestWithRetries(() => mockFetch(), 10); - - await jest.advanceTimersByTimeAsync(10000); - const response = await responsePromise; - - expect(mockFetch).toHaveBeenCalledTimes(6); - expect(response.status).toBe(200); - }); - - it("should retry max 3 times", async () => { - mockFetch - .mockResolvedValueOnce(new Response("", { status: 408 })) - .mockResolvedValueOnce(new Response("", { status: 409 })) - .mockResolvedValueOnce(new Response("", { status: 429 })) - .mockResolvedValueOnce(new Response("", { status: 429 })); - - const responsePromise = requestWithRetries(() => mockFetch(), 3); - - await jest.advanceTimersByTimeAsync(10000); - const response = await responsePromise; - - expect(mockFetch).toHaveBeenCalledTimes(4); - expect(response.status).toBe(429); - }); - it("should not retry on 200", async () => { - mockFetch - .mockResolvedValueOnce(new Response("", { status: 200 })) - .mockResolvedValueOnce(new Response("", { status: 409 })); - - const responsePromise = requestWithRetries(() => mockFetch(), 3); - - await jest.advanceTimersByTimeAsync(10000); - const response = await responsePromise; - - expect(mockFetch).toHaveBeenCalledTimes(1); - expect(response.status).toBe(200); - }); - - it("should retry with exponential backoff timing", async () => { - mockFetch.mockResolvedValue(new Response("", { status: 500 })); - const maxRetries = 7; - const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); - expect(mockFetch).toHaveBeenCalledTimes(1); - - const delays = [1, 2, 4, 8, 16, 32, 64]; - for (let i = 0; i < delays.length; i++) { - await jest.advanceTimersByTimeAsync(delays[i] as number); - expect(mockFetch).toHaveBeenCalledTimes(Math.min(i + 2, maxRetries + 1)); - } - const response = await responsePromise; - expect(response.status).toBe(500); - }); -}); diff --git a/tests/unit/fetcher/signals.test.ts b/tests/unit/fetcher/signals.test.ts deleted file mode 100644 index 9cabfa0..0000000 --- a/tests/unit/fetcher/signals.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { anySignal, getTimeoutSignal } from "../../../src/core/fetcher/signals"; - -describe("Test getTimeoutSignal", () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - it("should return an object with signal and abortId", () => { - const { signal, abortId } = getTimeoutSignal(1000); - - expect(signal).toBeDefined(); - expect(abortId).toBeDefined(); - expect(signal).toBeInstanceOf(AbortSignal); - expect(signal.aborted).toBe(false); - }); - - it("should create a signal that aborts after the specified timeout", () => { - const timeoutMs = 5000; - const { signal } = getTimeoutSignal(timeoutMs); - - expect(signal.aborted).toBe(false); - - jest.advanceTimersByTime(timeoutMs - 1); - expect(signal.aborted).toBe(false); - - jest.advanceTimersByTime(1); - expect(signal.aborted).toBe(true); - }); -}); - -describe("Test anySignal", () => { - it("should return an AbortSignal", () => { - const signal = anySignal(new AbortController().signal); - expect(signal).toBeInstanceOf(AbortSignal); - }); - - it("should abort when any of the input signals is aborted", () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - const signal = anySignal(controller1.signal, controller2.signal); - - expect(signal.aborted).toBe(false); - controller1.abort(); - expect(signal.aborted).toBe(true); - }); - - it("should handle an array of signals", () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - const signal = anySignal([controller1.signal, controller2.signal]); - - expect(signal.aborted).toBe(false); - controller2.abort(); - expect(signal.aborted).toBe(true); - }); - - it("should abort immediately if one of the input signals is already aborted", () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - controller1.abort(); - - const signal = anySignal(controller1.signal, controller2.signal); - expect(signal.aborted).toBe(true); - }); -}); diff --git a/tests/unit/serializer/date/date.test.ts b/tests/unit/serializer/date/date.test.ts deleted file mode 100644 index 7a76629..0000000 --- a/tests/unit/serializer/date/date.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { itSchema } from "../itSchema"; -import { itValidateJson, itValidateParse } from "../itValidate"; -import { date } from "../../../../src/core/schemas"; - -describe("date", () => { - itSchema("converts between raw ISO string and parsed Date", date(), { - raw: "2022-09-29T05:41:21.939Z", - parsed: new Date("2022-09-29T05:41:21.939Z"), - }); - - itValidateParse("non-string", date(), 42, [ - { - message: "Expected string. Received 42.", - path: [], - }, - ]); - - itValidateParse("non-ISO", date(), "hello world", [ - { - message: 'Expected ISO 8601 date string. Received "hello world".', - path: [], - }, - ]); - - itValidateJson("non-Date", date(), "hello", [ - { - message: 'Expected Date object. Received "hello".', - path: [], - }, - ]); -}); diff --git a/tests/unit/serializer/enum/enum.test.ts b/tests/unit/serializer/enum/enum.test.ts deleted file mode 100644 index 1619ab3..0000000 --- a/tests/unit/serializer/enum/enum.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { itSchemaIdentity } from "../itSchema"; -import { itValidate } from "../itValidate"; -import { enum_ } from "../../../../src/core/schemas"; - -describe("enum", () => { - itSchemaIdentity(enum_(["A", "B", "C"]), "A"); - - itSchemaIdentity(enum_(["A", "B", "C"]), "D" as any, { - opts: { allowUnrecognizedEnumValues: true }, - }); - - itValidate("invalid enum", enum_(["A", "B", "C"]), "D", [ - { - message: 'Expected enum. Received "D".', - path: [], - }, - ]); - - itValidate( - "non-string", - enum_(["A", "B", "C"]), - [], - [ - { - message: "Expected string. Received list.", - path: [], - }, - ] - ); -}); diff --git a/tests/unit/serializer/itSchema.ts b/tests/unit/serializer/itSchema.ts deleted file mode 100644 index 255bae4..0000000 --- a/tests/unit/serializer/itSchema.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* eslint-disable jest/no-export */ -import { Schema, SchemaOptions } from "../../../src/core/schemas/Schema"; - -export function itSchemaIdentity( - schema: Schema, - value: T, - { title = "functions as identity", opts }: { title?: string; opts?: SchemaOptions } = {} -): void { - itSchema(title, schema, { raw: value, parsed: value, opts }); -} - -export function itSchema( - title: string, - schema: Schema, - { - raw, - parsed, - opts, - only = false, - }: { - raw: Raw; - parsed: Parsed; - opts?: SchemaOptions; - only?: boolean; - } -): void { - // eslint-disable-next-line jest/valid-title - (only ? describe.only : describe)(title, () => { - itParse("parse()", schema, { raw, parsed, opts }); - itJson("json()", schema, { raw, parsed, opts }); - }); -} - -export function itParse( - title: string, - schema: Schema, - { - raw, - parsed, - opts, - }: { - raw: Raw; - parsed: Parsed; - opts?: SchemaOptions; - } -): void { - // eslint-disable-next-line jest/valid-title - it(title, () => { - const maybeValid = schema.parse(raw, opts); - if (!maybeValid.ok) { - throw new Error("Failed to parse() " + JSON.stringify(maybeValid.errors, undefined, 4)); - } - expect(maybeValid.value).toStrictEqual(parsed); - }); -} - -export function itJson( - title: string, - schema: Schema, - { - raw, - parsed, - opts, - }: { - raw: Raw; - parsed: Parsed; - opts?: SchemaOptions; - } -): void { - // eslint-disable-next-line jest/valid-title - it(title, () => { - const maybeValid = schema.json(parsed, opts); - if (!maybeValid.ok) { - throw new Error("Failed to json() " + JSON.stringify(maybeValid.errors, undefined, 4)); - } - expect(maybeValid.value).toStrictEqual(raw); - }); -} diff --git a/tests/unit/serializer/itValidate.ts b/tests/unit/serializer/itValidate.ts deleted file mode 100644 index e806578..0000000 --- a/tests/unit/serializer/itValidate.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* eslint-disable jest/no-export */ -import { Schema, SchemaOptions, ValidationError } from "../../../src/core/schemas/Schema"; - -export function itValidate( - title: string, - schema: Schema, - input: unknown, - errors: ValidationError[], - opts?: SchemaOptions -): void { - // eslint-disable-next-line jest/valid-title - describe("parse()", () => { - itValidateParse(title, schema, input, errors, opts); - }); - describe("json()", () => { - itValidateJson(title, schema, input, errors, opts); - }); -} - -export function itValidateParse( - title: string, - schema: Schema, - raw: unknown, - errors: ValidationError[], - opts?: SchemaOptions -): void { - describe("parse", () => { - // eslint-disable-next-line jest/valid-title - it(title, async () => { - const maybeValid = await schema.parse(raw, opts); - if (maybeValid.ok) { - throw new Error("Value passed validation"); - } - expect(maybeValid.errors).toStrictEqual(errors); - }); - }); -} - -export function itValidateJson( - title: string, - schema: Schema, - parsed: unknown, - errors: ValidationError[], - opts?: SchemaOptions -): void { - describe("json", () => { - // eslint-disable-next-line jest/valid-title - it(title, async () => { - const maybeValid = await schema.json(parsed, opts); - if (maybeValid.ok) { - throw new Error("Value passed validation"); - } - expect(maybeValid.errors).toStrictEqual(errors); - }); - }); -} diff --git a/tests/unit/serializer/lazy/lazy.test.ts b/tests/unit/serializer/lazy/lazy.test.ts deleted file mode 100644 index dca4e2e..0000000 --- a/tests/unit/serializer/lazy/lazy.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Schema } from "../../../../src/core/schemas"; -import { itSchemaIdentity } from "../itSchema"; -import { list } from "../../../../src/core/schemas"; -import { object } from "../../../../src/core/schemas"; -import { string } from "../../../../src/core/schemas"; -import { lazy } from "../../../../src/core/schemas"; - -describe("lazy", () => { - it("doesn't run immediately", () => { - let wasRun = false; - lazy(() => { - wasRun = true; - return string(); - }); - expect(wasRun).toBe(false); - }); - - it("only runs first time", async () => { - let count = 0; - const schema = lazy(() => { - count++; - return string(); - }); - await schema.parse("hello"); - await schema.json("world"); - expect(count).toBe(1); - }); - - itSchemaIdentity( - lazy(() => object({})), - { foo: "hello" }, - { - title: "passes opts through", - opts: { unrecognizedObjectKeys: "passthrough" }, - } - ); - - itSchemaIdentity( - lazy(() => object({ foo: string() })), - { foo: "hello" } - ); - - // eslint-disable-next-line jest/expect-expect - it("self-referencial schema doesn't compile", () => { - () => { - // @ts-expect-error - const a = lazy(() => object({ foo: a })); - }; - }); - - // eslint-disable-next-line jest/expect-expect - it("self-referencial compiles with explicit type", () => { - () => { - interface TreeNode { - children: TreeNode[]; - } - const TreeNode: Schema = lazy(() => object({ children: list(TreeNode) })); - }; - }); -}); diff --git a/tests/unit/serializer/lazy/lazyObject.test.ts b/tests/unit/serializer/lazy/lazyObject.test.ts deleted file mode 100644 index e010a0d..0000000 --- a/tests/unit/serializer/lazy/lazyObject.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { itSchemaIdentity } from "../itSchema"; -import { object } from "../../../../src/core/schemas"; -import { number, string } from "../../../../src/core/schemas"; -import { lazyObject } from "../../../../src/core/schemas"; - -describe("lazy", () => { - itSchemaIdentity( - lazyObject(() => object({ foo: string() })), - { foo: "hello" } - ); - - itSchemaIdentity( - lazyObject(() => object({ foo: string() })).extend(object({ bar: number() })), - { - foo: "hello", - bar: 42, - }, - { title: "returned schema has object utils" } - ); -}); diff --git a/tests/unit/serializer/lazy/recursive/a.ts b/tests/unit/serializer/lazy/recursive/a.ts deleted file mode 100644 index a88702b..0000000 --- a/tests/unit/serializer/lazy/recursive/a.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { object } from "../../../../object"; -import { schemaB } from "./b"; - -// @ts-expect-error -export const schemaA = object({ - b: schemaB, -}); diff --git a/tests/unit/serializer/lazy/recursive/b.ts b/tests/unit/serializer/lazy/recursive/b.ts deleted file mode 100644 index 0bbed91..0000000 --- a/tests/unit/serializer/lazy/recursive/b.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { object } from "../../../../object"; -import { optional } from "../../../../schema-utils"; -import { schemaA } from "./a"; - -// @ts-expect-error -export const schemaB = object({ - a: optional(schemaA), -}); diff --git a/tests/unit/serializer/list/list.test.ts b/tests/unit/serializer/list/list.test.ts deleted file mode 100644 index 46033da..0000000 --- a/tests/unit/serializer/list/list.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { itSchema, itSchemaIdentity } from "../itSchema"; -import { itValidate } from "../itValidate"; -import { object, property } from "../../../../src/core/schemas"; -import { string } from "../../../../src/core/schemas"; -import { list } from "../../../../src/core/schemas"; - -describe("list", () => { - itSchemaIdentity(list(string()), ["hello", "world"], { - title: "functions as identity when item type is primitive", - }); - - itSchema( - "converts objects correctly", - list( - object({ - helloWorld: property("hello_world", string()), - }) - ), - { - raw: [{ hello_world: "123" }], - parsed: [{ helloWorld: "123" }], - } - ); - - itValidate("not a list", list(string()), 42, [ - { - path: [], - message: "Expected list. Received 42.", - }, - ]); - - itValidate( - "invalid item type", - list(string()), - [42], - [ - { - path: ["[0]"], - message: "Expected string. Received 42.", - }, - ] - ); -}); diff --git a/tests/unit/serializer/literals/stringLiteral.test.ts b/tests/unit/serializer/literals/stringLiteral.test.ts deleted file mode 100644 index ed7b31f..0000000 --- a/tests/unit/serializer/literals/stringLiteral.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { itSchemaIdentity } from "../itSchema"; -import { itValidate } from "../itValidate"; -import { stringLiteral } from "../../../../src/core/schemas"; - -describe("stringLiteral", () => { - itSchemaIdentity(stringLiteral("A"), "A"); - - itValidate("incorrect string", stringLiteral("A"), "B", [ - { - path: [], - message: 'Expected "A". Received "B".', - }, - ]); - - itValidate("non-string", stringLiteral("A"), 42, [ - { - path: [], - message: 'Expected "A". Received 42.', - }, - ]); -}); diff --git a/tests/unit/serializer/object-like/withParsedProperties.test.ts b/tests/unit/serializer/object-like/withParsedProperties.test.ts deleted file mode 100644 index 90109c2..0000000 --- a/tests/unit/serializer/object-like/withParsedProperties.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { stringLiteral } from "../../../../src/core/schemas"; -import { object } from "../../../../src/core/schemas"; -import { property } from "../../../../src/core/schemas"; -import { string } from "../../../../src/core/schemas"; - -describe("withParsedProperties", () => { - it("Added properties included on parsed object", async () => { - const schema = object({ - foo: property("raw_foo", string()), - bar: stringLiteral("bar"), - }).withParsedProperties({ - printFoo: (parsed) => () => parsed.foo, - printHelloWorld: () => () => "Hello world", - helloWorld: "Hello world", - }); - - const parsed = await schema.parse({ raw_foo: "value of foo", bar: "bar" }); - if (!parsed.ok) { - throw new Error("Failed to parse"); - } - expect(parsed.value.printFoo()).toBe("value of foo"); - expect(parsed.value.printHelloWorld()).toBe("Hello world"); - expect(parsed.value.helloWorld).toBe("Hello world"); - }); - - it("Added property is removed on raw object", async () => { - const schema = object({ - foo: property("raw_foo", string()), - bar: stringLiteral("bar"), - }).withParsedProperties({ - printFoo: (parsed) => () => parsed.foo, - }); - - const original = { raw_foo: "value of foo", bar: "bar" } as const; - const parsed = await schema.parse(original); - if (!parsed.ok) { - throw new Error("Failed to parse()"); - } - - const raw = await schema.json(parsed.value); - - if (!raw.ok) { - throw new Error("Failed to json()"); - } - - expect(raw.value).toEqual(original); - }); - - describe("compile", () => { - // eslint-disable-next-line jest/expect-expect - it("doesn't compile with non-object schema", () => { - () => - object({ - foo: string(), - }) - // @ts-expect-error - .withParsedProperties(42); - }); - }); -}); diff --git a/tests/unit/serializer/object/extend.test.ts b/tests/unit/serializer/object/extend.test.ts deleted file mode 100644 index 1987207..0000000 --- a/tests/unit/serializer/object/extend.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { itSchema, itSchemaIdentity } from "../itSchema"; -import { stringLiteral } from "../../../../src/core/schemas"; -import { boolean, string } from "../../../../src/core/schemas"; -import { object } from "../../../../src/core/schemas"; -import { property } from "../../../../src/core/schemas"; - -describe("extend", () => { - itSchemaIdentity( - object({ - foo: string(), - }).extend( - object({ - bar: stringLiteral("bar"), - }) - ), - { - foo: "", - bar: "bar", - } as const, - { - title: "extended properties are included in schema", - } - ); - - itSchemaIdentity( - object({ - foo: string(), - }) - .extend( - object({ - bar: stringLiteral("bar"), - }) - ) - .extend( - object({ - baz: boolean(), - }) - ), - { - foo: "", - bar: "bar", - baz: true, - } as const, - { - title: "extensions can be extended", - } - ); - - itSchema( - "converts nested object", - object({ - item: object({ - helloWorld: property("hello_world", string()), - }), - }).extend( - object({ - goodbye: property("goodbye_raw", string()), - }) - ), - { - raw: { item: { hello_world: "yo" }, goodbye_raw: "peace" }, - parsed: { item: { helloWorld: "yo" }, goodbye: "peace" }, - } - ); - - itSchema( - "extensions work with raw/parsed property name conversions", - object({ - item: property("item_raw", string()), - }).extend( - object({ - goodbye: property("goodbye_raw", string()), - }) - ), - { - raw: { item_raw: "hi", goodbye_raw: "peace" }, - parsed: { item: "hi", goodbye: "peace" }, - } - ); - - describe("compile", () => { - // eslint-disable-next-line jest/expect-expect - it("doesn't compile with non-object schema", () => { - () => - object({ - foo: string(), - }) - // @ts-expect-error - .extend([]); - }); - }); -}); diff --git a/tests/unit/serializer/object/object.test.ts b/tests/unit/serializer/object/object.test.ts deleted file mode 100644 index eb726a1..0000000 --- a/tests/unit/serializer/object/object.test.ts +++ /dev/null @@ -1,258 +0,0 @@ -import { itJson, itParse, itSchema, itSchemaIdentity } from "../itSchema"; -import { itValidate } from "../itValidate"; -import { stringLiteral } from "../../../../src/core/schemas"; -import { any, number, string, unknown } from "../../../../src/core/schemas"; -import { object } from "../../../../src/core/schemas"; -import { property } from "../../../../src/core/schemas"; - -describe("object", () => { - itSchemaIdentity( - object({ - foo: string(), - bar: stringLiteral("bar"), - }), - { - foo: "", - bar: "bar", - }, - { - title: "functions as identity when values are primitives and property() isn't used", - } - ); - - itSchema( - "uses raw key from property()", - object({ - foo: property("raw_foo", string()), - bar: stringLiteral("bar"), - }), - { - raw: { raw_foo: "foo", bar: "bar" }, - parsed: { foo: "foo", bar: "bar" }, - } - ); - - itSchema( - "keys with unknown type can be omitted", - object({ - foo: unknown(), - }), - { - raw: {}, - parsed: {}, - } - ); - - itSchema( - "keys with any type can be omitted", - object({ - foo: any(), - }), - { - raw: {}, - parsed: {}, - } - ); - - describe("unrecognizedObjectKeys", () => { - describe("parse", () => { - itParse( - 'includes unknown values when unrecognizedObjectKeys === "passthrough"', - object({ - foo: property("raw_foo", string()), - bar: stringLiteral("bar"), - }), - { - raw: { - raw_foo: "foo", - bar: "bar", - // @ts-expect-error - baz: "yoyo", - }, - parsed: { - foo: "foo", - bar: "bar", - // @ts-expect-error - baz: "yoyo", - }, - opts: { - unrecognizedObjectKeys: "passthrough", - }, - } - ); - - itParse( - 'strips unknown values when unrecognizedObjectKeys === "strip"', - object({ - foo: property("raw_foo", string()), - bar: stringLiteral("bar"), - }), - { - raw: { - raw_foo: "foo", - bar: "bar", - // @ts-expect-error - baz: "yoyo", - }, - parsed: { - foo: "foo", - bar: "bar", - }, - opts: { - unrecognizedObjectKeys: "strip", - }, - } - ); - }); - - describe("json", () => { - itJson( - 'includes unknown values when unrecognizedObjectKeys === "passthrough"', - object({ - foo: property("raw_foo", string()), - bar: stringLiteral("bar"), - }), - { - raw: { - raw_foo: "foo", - bar: "bar", - // @ts-expect-error - baz: "yoyo", - }, - parsed: { - foo: "foo", - bar: "bar", - // @ts-expect-error - baz: "yoyo", - }, - opts: { - unrecognizedObjectKeys: "passthrough", - }, - } - ); - - itJson( - 'strips unknown values when unrecognizedObjectKeys === "strip"', - object({ - foo: property("raw_foo", string()), - bar: stringLiteral("bar"), - }), - { - raw: { - raw_foo: "foo", - bar: "bar", - }, - parsed: { - foo: "foo", - bar: "bar", - // @ts-expect-error - baz: "yoyo", - }, - opts: { - unrecognizedObjectKeys: "strip", - }, - } - ); - }); - }); - - describe("nullish properties", () => { - itSchema("missing properties are not added", object({ foo: property("raw_foo", string().optional()) }), { - raw: {}, - parsed: {}, - }); - - itSchema("undefined properties are not dropped", object({ foo: property("raw_foo", string().optional()) }), { - raw: { raw_foo: null }, - parsed: { foo: undefined }, - }); - - itSchema("null properties are not dropped", object({ foo: property("raw_foo", string().optional()) }), { - raw: { raw_foo: null }, - parsed: { foo: undefined }, - }); - - describe("extensions", () => { - itSchema( - "undefined properties are not dropped", - object({}).extend(object({ foo: property("raw_foo", string().optional()) })), - { - raw: { raw_foo: null }, - parsed: { foo: undefined }, - } - ); - - describe("parse()", () => { - itParse( - "null properties are not dropped", - object({}).extend(object({ foo: property("raw_foo", string().optional()) })), - { - raw: { raw_foo: null }, - parsed: { foo: undefined }, - } - ); - }); - }); - }); - - itValidate( - "missing property", - object({ - foo: string(), - bar: stringLiteral("bar"), - }), - { foo: "hello" }, - [ - { - path: [], - message: 'Missing required key "bar"', - }, - ] - ); - - itValidate( - "extra property", - object({ - foo: string(), - bar: stringLiteral("bar"), - }), - { foo: "hello", bar: "bar", baz: 42 }, - [ - { - path: ["baz"], - message: 'Unexpected key "baz"', - }, - ] - ); - - itValidate( - "not an object", - object({ - foo: string(), - bar: stringLiteral("bar"), - }), - [], - [ - { - path: [], - message: "Expected object. Received list.", - }, - ] - ); - - itValidate( - "nested validation error", - object({ - foo: object({ - bar: number(), - }), - }), - { foo: { bar: "hello" } }, - [ - { - path: ["foo", "bar"], - message: 'Expected number. Received "hello".', - }, - ] - ); -}); diff --git a/tests/unit/serializer/object/objectWithoutOptionalProperties.test.ts b/tests/unit/serializer/object/objectWithoutOptionalProperties.test.ts deleted file mode 100644 index 1f298bd..0000000 --- a/tests/unit/serializer/object/objectWithoutOptionalProperties.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { itSchema } from "../itSchema"; -import { stringLiteral } from "../../../../src/core/schemas"; -import { string } from "../../../../src/core/schemas"; -import { objectWithoutOptionalProperties } from "../../../../src/core/schemas"; - -describe("objectWithoutOptionalProperties", () => { - itSchema( - "all properties are required", - objectWithoutOptionalProperties({ - foo: string(), - bar: stringLiteral("bar").optional(), - }), - { - raw: { - foo: "hello", - }, - // @ts-expect-error - parsed: { - foo: "hello", - }, - } - ); -}); diff --git a/tests/unit/serializer/primitives/any.test.ts b/tests/unit/serializer/primitives/any.test.ts deleted file mode 100644 index 8d3b473..0000000 --- a/tests/unit/serializer/primitives/any.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { itSchemaIdentity } from "../itSchema"; -import { any } from "../../../../src/core/schemas"; - -describe("any", () => { - itSchemaIdentity(any(), true); -}); diff --git a/tests/unit/serializer/primitives/boolean.test.ts b/tests/unit/serializer/primitives/boolean.test.ts deleted file mode 100644 index 3bf62e0..0000000 --- a/tests/unit/serializer/primitives/boolean.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { itSchemaIdentity } from "../itSchema"; -import { itValidate } from "../itValidate"; -import { boolean } from "../../../../src/core/schemas"; - -describe("boolean", () => { - itSchemaIdentity(boolean(), true); - - itValidate("non-boolean", boolean(), {}, [ - { - path: [], - message: "Expected boolean. Received object.", - }, - ]); -}); diff --git a/tests/unit/serializer/primitives/number.test.ts b/tests/unit/serializer/primitives/number.test.ts deleted file mode 100644 index ad004f5..0000000 --- a/tests/unit/serializer/primitives/number.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { itSchemaIdentity } from "../itSchema"; -import { itValidate } from "../itValidate"; -import { number } from "../../../../src/core/schemas"; - -describe("number", () => { - itSchemaIdentity(number(), 42); - - itValidate("non-number", number(), "hello", [ - { - path: [], - message: 'Expected number. Received "hello".', - }, - ]); -}); diff --git a/tests/unit/serializer/primitives/string.test.ts b/tests/unit/serializer/primitives/string.test.ts deleted file mode 100644 index dfb6fbd..0000000 --- a/tests/unit/serializer/primitives/string.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { itSchemaIdentity } from "../itSchema"; -import { itValidate } from "../itValidate"; -import { string } from "../../../../src/core/schemas"; - -describe("string", () => { - itSchemaIdentity(string(), "hello"); - - itValidate("non-string", string(), 42, [ - { - path: [], - message: "Expected string. Received 42.", - }, - ]); -}); diff --git a/tests/unit/serializer/primitives/unknown.test.ts b/tests/unit/serializer/primitives/unknown.test.ts deleted file mode 100644 index 09457d6..0000000 --- a/tests/unit/serializer/primitives/unknown.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { itSchemaIdentity } from "../itSchema"; -import { unknown } from "../../../../src/core/schemas"; - -describe("unknown", () => { - itSchemaIdentity(unknown(), true); -}); diff --git a/tests/unit/serializer/record/record.test.ts b/tests/unit/serializer/record/record.test.ts deleted file mode 100644 index 79bbfff..0000000 --- a/tests/unit/serializer/record/record.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { itSchemaIdentity } from "../itSchema"; -import { itValidate } from "../itValidate"; -import { number, string } from "../../../../src/core/schemas"; -import { record } from "../../../../src/core/schemas"; - -describe("record", () => { - itSchemaIdentity(record(string(), string()), { hello: "world" }); - itSchemaIdentity(record(number(), string()), { 42: "world" }); - - itValidate( - "non-record", - record(number(), string()), - [], - [ - { - path: [], - message: "Expected object. Received list.", - }, - ] - ); - - itValidate("invalid key type", record(number(), string()), { hello: "world" }, [ - { - path: ["hello (key)"], - message: 'Expected number. Received "hello".', - }, - ]); - - itValidate("invalid value type", record(string(), number()), { hello: "world" }, [ - { - path: ["hello"], - message: 'Expected number. Received "world".', - }, - ]); -}); diff --git a/tests/unit/serializer/schema-utils/getSchemaUtils.test.ts b/tests/unit/serializer/schema-utils/getSchemaUtils.test.ts deleted file mode 100644 index 378f701..0000000 --- a/tests/unit/serializer/schema-utils/getSchemaUtils.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { itSchema } from "../itSchema"; -import { object } from "../../../../src/core/schemas"; -import { string } from "../../../../src/core/schemas"; - -describe("getSchemaUtils", () => { - describe("optional()", () => { - itSchema("optional fields allow original schema", string().optional(), { - raw: "hello", - parsed: "hello", - }); - - itSchema("optional fields are not required", string().optional(), { - raw: null, - parsed: undefined, - }); - }); - - describe("transform()", () => { - itSchema( - "transorm and untransform run correctly", - string().transform({ - transform: (x) => x + "X", - untransform: (x) => (x as string).slice(0, -1), - }), - { - raw: "hello", - parsed: "helloX", - } - ); - }); - - describe("parseOrThrow()", () => { - it("parses valid value", async () => { - const value = string().parseOrThrow("hello"); - expect(value).toBe("hello"); - }); - - it("throws on invalid value", async () => { - const value = () => object({ a: string(), b: string() }).parseOrThrow({ a: 24 }); - expect(value).toThrowError(new Error('a: Expected string. Received 24.; Missing required key "b"')); - }); - }); - - describe("jsonOrThrow()", () => { - it("serializes valid value", async () => { - const value = string().jsonOrThrow("hello"); - expect(value).toBe("hello"); - }); - - it("throws on invalid value", async () => { - const value = () => object({ a: string(), b: string() }).jsonOrThrow({ a: 24 }); - expect(value).toThrowError(new Error('a: Expected string. Received 24.; Missing required key "b"')); - }); - }); -}); diff --git a/tests/unit/serializer/schema.test.ts b/tests/unit/serializer/schema.test.ts deleted file mode 100644 index 94089a9..0000000 --- a/tests/unit/serializer/schema.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - boolean, - discriminant, - list, - number, - object, - string, - stringLiteral, - union, -} from "../../../src/core/schemas/builders"; -import { booleanLiteral } from "../../../src/core/schemas/builders/literals/booleanLiteral"; -import { property } from "../../../src/core/schemas/builders/object/property"; -import { itSchema } from "./utils/itSchema"; - -describe("Schema", () => { - itSchema( - "large nested object", - object({ - a: string(), - b: stringLiteral("b value"), - c: property( - "raw_c", - list( - object({ - animal: union(discriminant("type", "_type"), { - dog: object({ value: boolean() }), - cat: object({ value: property("raw_cat", number()) }), - }), - }) - ) - ), - d: property("raw_d", boolean()), - e: booleanLiteral(true), - }), - { - raw: { - a: "hello", - b: "b value", - raw_c: [ - { - animal: { - _type: "dog", - value: true, - }, - }, - { - animal: { - _type: "cat", - raw_cat: 42, - }, - }, - ], - raw_d: false, - e: true, - }, - parsed: { - a: "hello", - b: "b value", - c: [ - { - animal: { - type: "dog", - value: true, - }, - }, - { - animal: { - type: "cat", - value: 42, - }, - }, - ], - d: false, - e: true, - }, - } - ); -}); diff --git a/tests/unit/serializer/set/set.test.ts b/tests/unit/serializer/set/set.test.ts deleted file mode 100644 index 2cad2d6..0000000 --- a/tests/unit/serializer/set/set.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { itSchema } from "../itSchema"; -import { itValidateJson, itValidateParse } from "../itValidate"; -import { string } from "../../../../src/core/schemas"; -import { set } from "../../../../src/core/schemas"; - -describe("set", () => { - itSchema("converts between raw list and parsed Set", set(string()), { - raw: ["A", "B"], - parsed: new Set(["A", "B"]), - }); - - itValidateParse("not a list", set(string()), 42, [ - { - path: [], - message: "Expected list. Received 42.", - }, - ]); - - itValidateJson( - "not a Set", - set(string()), - [], - [ - { - path: [], - message: "Expected Set. Received list.", - }, - ] - ); - - itValidateParse( - "invalid item type", - set(string()), - [42], - [ - { - path: ["[0]"], - message: "Expected string. Received 42.", - }, - ] - ); - - itValidateJson("invalid item type", set(string()), new Set([42]), [ - { - path: ["[0]"], - message: "Expected string. Received 42.", - }, - ]); -}); diff --git a/tests/unit/serializer/skipValidation.test.ts b/tests/unit/serializer/skipValidation.test.ts deleted file mode 100644 index 5dc8809..0000000 --- a/tests/unit/serializer/skipValidation.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable no-console */ - -import { boolean, number, object, property, string, undiscriminatedUnion } from "../../../src/core/schemas/builders"; - -describe("skipValidation", () => { - it("allows data that doesn't conform to the schema", async () => { - const warningLogs: string[] = []; - const originalConsoleWarn = console.warn; - console.warn = (...args) => warningLogs.push(args.join(" ")); - - const schema = object({ - camelCase: property("snake_case", string()), - numberProperty: number(), - requiredProperty: boolean(), - anyPrimitive: undiscriminatedUnion([string(), number(), boolean()]), - }); - - const parsed = await schema.parse( - { - snake_case: "hello", - numberProperty: "oops", - anyPrimitive: true, - }, - { - skipValidation: true, - } - ); - - expect(parsed).toEqual({ - ok: true, - value: { - camelCase: "hello", - numberProperty: "oops", - anyPrimitive: true, - }, - }); - - expect(warningLogs).toEqual([ - `Failed to validate. - - numberProperty: Expected number. Received "oops".`, - ]); - - console.warn = originalConsoleWarn; - }); -}); diff --git a/tests/unit/serializer/undiscriminated-union/undiscriminatedUnion.test.ts b/tests/unit/serializer/undiscriminated-union/undiscriminatedUnion.test.ts deleted file mode 100644 index b7eea32..0000000 --- a/tests/unit/serializer/undiscriminated-union/undiscriminatedUnion.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { itSchema, itSchemaIdentity } from "../itSchema"; -import { object, property } from "../../../../src/core/schemas"; -import { number, string } from "../../../../src/core/schemas"; -import { undiscriminatedUnion } from "../../../../src/core/schemas"; - -describe("undiscriminatedUnion", () => { - itSchemaIdentity(undiscriminatedUnion([string(), number()]), "hello world"); - - itSchemaIdentity(undiscriminatedUnion([object({ hello: string() }), object({ goodbye: string() })]), { - goodbye: "foo", - }); - - itSchema( - "Correctly transforms", - undiscriminatedUnion([object({ hello: string() }), object({ helloWorld: property("hello_world", string()) })]), - { - raw: { hello_world: "foo " }, - parsed: { helloWorld: "foo " }, - } - ); - - it("Returns errors for all variants", async () => { - const result = await undiscriminatedUnion([string(), number()]).parse(true); - if (result.ok) { - throw new Error("Unexpectedly passed validation"); - } - expect(result.errors).toEqual([ - { - message: "[Variant 0] Expected string. Received true.", - path: [], - }, - { - message: "[Variant 1] Expected number. Received true.", - path: [], - }, - ]); - }); - - describe("compile", () => { - // eslint-disable-next-line jest/expect-expect - it("doesn't compile with zero members", () => { - // @ts-expect-error - () => undiscriminatedUnion([]); - }); - }); -}); diff --git a/tests/unit/serializer/union/union.test.ts b/tests/unit/serializer/union/union.test.ts deleted file mode 100644 index f782a8e..0000000 --- a/tests/unit/serializer/union/union.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { itSchema, itSchemaIdentity } from "../itSchema"; -import { itValidate } from "../itValidate"; -import { object } from "../../../../src/core/schemas"; -import { boolean, number, string } from "../../../../src/core/schemas"; -import { discriminant } from "../../../../src/core/schemas"; -import { union } from "../../../../src/core/schemas"; - -describe("union", () => { - itSchemaIdentity( - union("type", { - lion: object({ - meows: boolean(), - }), - giraffe: object({ - heightInInches: number(), - }), - }), - { type: "lion", meows: true }, - { title: "doesn't transform discriminant when it's a string" } - ); - - itSchema( - "transforms discriminant when it's a discriminant()", - union(discriminant("type", "_type"), { - lion: object({ meows: boolean() }), - giraffe: object({ heightInInches: number() }), - }), - { - raw: { _type: "lion", meows: true }, - parsed: { type: "lion", meows: true }, - } - ); - - describe("allowUnrecognizedUnionMembers", () => { - itSchema( - "transforms discriminant & passes through values when discriminant value is unrecognized", - union(discriminant("type", "_type"), { - lion: object({ meows: boolean() }), - giraffe: object({ heightInInches: number() }), - }), - { - // @ts-expect-error - raw: { _type: "moose", isAMoose: true }, - // @ts-expect-error - parsed: { type: "moose", isAMoose: true }, - opts: { - allowUnrecognizedUnionMembers: true, - }, - } - ); - }); - - describe("withParsedProperties", () => { - it("Added property is included on parsed object", async () => { - const schema = union("type", { - lion: object({}), - tiger: object({ value: string() }), - }).withParsedProperties({ - printType: (parsed) => () => parsed.type, - }); - - const parsed = await schema.parse({ type: "lion" }); - if (!parsed.ok) { - throw new Error("Failed to parse"); - } - expect(parsed.value.printType()).toBe("lion"); - }); - }); - - itValidate( - "non-object", - union("type", { - lion: object({}), - tiger: object({ value: string() }), - }), - [], - [ - { - path: [], - message: "Expected object. Received list.", - }, - ] - ); - - itValidate( - "missing discriminant", - union("type", { - lion: object({}), - tiger: object({ value: string() }), - }), - {}, - [ - { - path: [], - message: 'Missing discriminant ("type")', - }, - ] - ); - - itValidate( - "unrecognized discriminant value", - union("type", { - lion: object({}), - tiger: object({ value: string() }), - }), - { - type: "bear", - }, - [ - { - path: ["type"], - message: 'Expected enum. Received "bear".', - }, - ] - ); -}); diff --git a/tests/unit/serializer/utils/itSchema.ts b/tests/unit/serializer/utils/itSchema.ts deleted file mode 100644 index 67b6c92..0000000 --- a/tests/unit/serializer/utils/itSchema.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* eslint-disable jest/no-export */ -import { Schema, SchemaOptions } from "../../../../src/core/schemas/Schema"; - -export function itSchemaIdentity( - schema: Schema, - value: T, - { title = "functions as identity", opts }: { title?: string; opts?: SchemaOptions } = {} -): void { - itSchema(title, schema, { raw: value, parsed: value, opts }); -} - -export function itSchema( - title: string, - schema: Schema, - { - raw, - parsed, - opts, - only = false, - }: { - raw: Raw; - parsed: Parsed; - opts?: SchemaOptions; - only?: boolean; - } -): void { - // eslint-disable-next-line jest/valid-title - (only ? describe.only : describe)(title, () => { - itParse("parse()", schema, { raw, parsed, opts }); - itJson("json()", schema, { raw, parsed, opts }); - }); -} - -export function itParse( - title: string, - schema: Schema, - { - raw, - parsed, - opts, - }: { - raw: Raw; - parsed: Parsed; - opts?: SchemaOptions; - } -): void { - // eslint-disable-next-line jest/valid-title - it(title, () => { - const maybeValid = schema.parse(raw, opts); - if (!maybeValid.ok) { - throw new Error("Failed to parse() " + JSON.stringify(maybeValid.errors, undefined, 4)); - } - expect(maybeValid.value).toStrictEqual(parsed); - }); -} - -export function itJson( - title: string, - schema: Schema, - { - raw, - parsed, - opts, - }: { - raw: Raw; - parsed: Parsed; - opts?: SchemaOptions; - } -): void { - // eslint-disable-next-line jest/valid-title - it(title, () => { - const maybeValid = schema.json(parsed, opts); - if (!maybeValid.ok) { - throw new Error("Failed to json() " + JSON.stringify(maybeValid.errors, undefined, 4)); - } - expect(maybeValid.value).toStrictEqual(raw); - }); -} diff --git a/tests/unit/serializer/utils/itValidate.ts b/tests/unit/serializer/utils/itValidate.ts deleted file mode 100644 index 75b2c08..0000000 --- a/tests/unit/serializer/utils/itValidate.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* eslint-disable jest/no-export */ -import { Schema, SchemaOptions, ValidationError } from "../../../../src/core/schemas/Schema"; - -export function itValidate( - title: string, - schema: Schema, - input: unknown, - errors: ValidationError[], - opts?: SchemaOptions -): void { - // eslint-disable-next-line jest/valid-title - describe("parse()", () => { - itValidateParse(title, schema, input, errors, opts); - }); - describe("json()", () => { - itValidateJson(title, schema, input, errors, opts); - }); -} - -export function itValidateParse( - title: string, - schema: Schema, - raw: unknown, - errors: ValidationError[], - opts?: SchemaOptions -): void { - describe("parse", () => { - // eslint-disable-next-line jest/valid-title - it(title, async () => { - const maybeValid = await schema.parse(raw, opts); - if (maybeValid.ok) { - throw new Error("Value passed validation"); - } - expect(maybeValid.errors).toStrictEqual(errors); - }); - }); -} - -export function itValidateJson( - title: string, - schema: Schema, - parsed: unknown, - errors: ValidationError[], - opts?: SchemaOptions -): void { - describe("json", () => { - // eslint-disable-next-line jest/valid-title - it(title, async () => { - const maybeValid = await schema.json(parsed, opts); - if (maybeValid.ok) { - throw new Error("Value passed validation"); - } - expect(maybeValid.errors).toStrictEqual(errors); - }); - }); -} diff --git a/yarn.lock b/yarn.lock index 4b2afd2..cc6e63a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -912,9 +912,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001640: - version "1.0.30001642" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz#6aa6610eb24067c246d30c57f055a9d0a7f8d05f" - integrity sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA== + version "1.0.30001643" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz#9c004caef315de9452ab970c3da71085f8241dbd" + integrity sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg== chalk@^2.4.2: version "2.4.2" @@ -1110,9 +1110,9 @@ domexception@^4.0.0: webidl-conversions "^7.0.0" electron-to-chromium@^1.4.820: - version "1.4.829" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.829.tgz#3034a865b5eac9064c9db8b38ba99b60a446bb73" - integrity sha512-5qp1N2POAfW0u1qGAxXEtz6P7bO1m6gpZr5hdf5ve6lxpLM7MpiM4jIPz7xcrNlClQMafbyUDDWjlIQZ1Mw0Rw== + version "1.5.0" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz#0d3123a9f09189b9c7ab4b5d6848d71b3c1fd0e8" + integrity sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA== emittery@^0.13.1: version "0.13.1" @@ -1247,7 +1247,7 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -form-data@4.0.0, form-data@^4.0.0: +form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== @@ -1412,9 +1412,9 @@ iconv-lite@0.6.3: safer-buffer ">= 2.1.2 < 3.0.0" import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" @@ -1443,9 +1443,9 @@ is-arrayish@^0.2.1: integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-core-module@^2.13.0: - version "2.14.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1" - integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A== + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea" + integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA== dependencies: hasown "^2.0.2" @@ -2077,9 +2077,9 @@ node-int64@^0.4.0: integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-releases@^2.0.14: - version "2.0.17" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.17.tgz#d74bc4fec38d839eec5db2a3c9c963d4f33cb366" - integrity sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA== + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== normalize-path@^3.0.0: version "3.0.0"