diff --git a/.changeset/nice-penguins-grow.md b/.changeset/nice-penguins-grow.md new file mode 100644 index 0000000000..678e32714d --- /dev/null +++ b/.changeset/nice-penguins-grow.md @@ -0,0 +1,10 @@ +--- +"uploadthing": minor +"@uploadthing/svelte": minor +"@uploadthing/react": minor +"@uploadthing/solid": minor +"@uploadthing/expo": minor +"@uploadthing/vue": minor +--- + +feat: allow custom fetch override diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8f56638a5d..ec64b47cb4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -31,6 +31,10 @@ jobs: - name: Build run: pnpm turbo build --filter "./packages/*" + + - name: Install Playwright + run: pnpm exec playwright install chromium + - name: Test run: pnpm run test env: diff --git a/.nvmrc b/.nvmrc index a3d2332c18..e70b3aebd5 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.11 \ No newline at end of file +22.12 \ No newline at end of file diff --git a/docs/package.json b/docs/package.json index 34d4b9b119..65ce3b9d7e 100644 --- a/docs/package.json +++ b/docs/package.json @@ -19,6 +19,7 @@ "@mdx-js/react": "^3.0.1", "@next/mdx": "^14.2.11", "@scalar/api-reference-react": "^0.3.37", + "@shikijs/transformers": "^1.17.5", "@sindresorhus/slugify": "^2.1.1", "@tailwindcss/typography": "^0.5.10", "@types/mdast": "^4.0.4", @@ -49,6 +50,7 @@ "remark-gfm": "^4.0.0", "remark-mdx": "^3.0.1", "remark-unwrap-images": "^4.0.0", + "sharp": "0.33.1", "shiki": "^1.17.5", "simple-functional-loader": "^1.2.1", "tailwindcss": "^3.4.16", @@ -59,14 +61,5 @@ "uploadthing": "workspace:*", "zod": "^3.23.8", "zustand": "^4.3.2" - }, - "devDependencies": { - "@shikijs/transformers": "^1.17.5", - "eslint": "^8.57.0", - "eslint-config-next": "^14.2.1", - "prettier": "^3.3.2", - "prettier-plugin-tailwindcss": "^0.6.5", - "sharp": "0.33.1" - }, - "packageManager": "pnpm@9.6.0" + } } diff --git a/examples/backend-adapters/package.json b/examples/backend-adapters/package.json index 22a591cef6..40d58cc3be 100644 --- a/examples/backend-adapters/package.json +++ b/examples/backend-adapters/package.json @@ -23,7 +23,7 @@ "test": "playwright test" }, "dependencies": { - "@playwright/test": "1.45.0", + "@playwright/test": "1.49.1", "@uploadthing/react": "7.1.5", "concurrently": "^8.2.2", "typescript": "^5.5.2", diff --git a/examples/backend-adapters/server/package.json b/examples/backend-adapters/server/package.json index 4ff00168b2..e13b282824 100644 --- a/examples/backend-adapters/server/package.json +++ b/examples/backend-adapters/server/package.json @@ -12,15 +12,15 @@ "dev:effect": "NODE_ENV=development PORT=3003 tsx watch src/effect-platform.ts" }, "dependencies": { - "@effect/platform": "0.70.7", - "@effect/platform-node": "0.65.7", + "@effect/platform": "0.72.0", + "@effect/platform-node": "0.68.0", "@elysiajs/cors": "^1.1.1", "@fastify/cors": "^9.0.1", "@hono/node-server": "^1.8.2", "@sinclair/typebox": "^0.34.3", "cors": "^2.8.5", "dotenv": "^16.4.5", - "effect": "3.11.5", + "effect": "3.12.0", "elysia": "^1.1.16", "express": "^4.18.2", "fastify": "^4.26.1", diff --git a/examples/backend-adapters/test/smoke.test.ts b/examples/backend-adapters/test/smoke.e2e.test.ts similarity index 100% rename from examples/backend-adapters/test/smoke.test.ts rename to examples/backend-adapters/test/smoke.e2e.test.ts diff --git a/package.json b/package.json index 23eaf1c127..605aaeb1fe 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "engines": { - "node": ">=20.x", + "node": ">=22.x", "pnpm": "9.x" }, "packageManager": "pnpm@9.4.0", @@ -40,32 +40,26 @@ "@actions/github": "^6.0.0", "@changesets/changelog-github": "^0.5.0", "@changesets/cli": "^2.27.1", - "@effect/vitest": "0.13.15", - "@ianvs/prettier-plugin-sort-imports": "^4.2.1", + "@effect/vitest": "0.16.0", + "@ianvs/prettier-plugin-sort-imports": "^4.4.0", "@manypkg/cli": "^0.21.3", - "@playwright/test": "1.45.0", "@prettier/sync": "^0.5.2", - "@testing-library/dom": "^10.4.0", - "@testing-library/jest-dom": "^6.4.8", - "@testing-library/react": "^16.0.0", "@types/bun": "^1.1.5", "@types/node": "^20.14.0", "@uploadthing/eslint-config": "workspace:*", - "@vitest/coverage-v8": "^2.1.2", - "happy-dom": "^13.6.2", - "msw": "2.2.13", - "prettier": "^3.3.2", - "prettier-plugin-tailwindcss": "^0.6.5", + "@vitest/browser": "^2.1.8", + "@vitest/coverage-v8": "^2.1.8", + "msw": "2.7.0", + "playwright": "1.49.1", + "prettier": "^3.4.2", + "prettier-plugin-tailwindcss": "^0.6.9", "turbo": "2.3.3", "typescript": "^5.5.2", "uploadthing": "workspace:*", - "vite-tsconfig-paths": "^4.3.2", - "vitest": "^2.1.2" + "vitest": "^2.1.8" }, "pnpm": { "patchedDependencies": { - "msw@2.2.13": "patches/msw@2.2.13.patch", - "@mswjs/interceptors@0.26.15": "patches/@mswjs__interceptors@0.26.15.patch", "bunchee@6.1.2": "patches/bunchee@6.1.2.patch" } } diff --git a/packages/expo/src/document-picker.ts b/packages/expo/src/document-picker.ts index ea05538739..1f2f43fe57 100644 --- a/packages/expo/src/document-picker.ts +++ b/packages/expo/src/document-picker.ts @@ -4,7 +4,11 @@ import * as DocumentPicker from "expo-document-picker"; import type { UseUploadthingProps } from "@uploadthing/react"; import { __useUploadThingInternal } from "@uploadthing/react/native"; import { generatePermittedFileTypes } from "@uploadthing/shared"; -import type { ExpandedRouteConfig, ExtendObjectIf } from "@uploadthing/shared"; +import type { + ExpandedRouteConfig, + ExtendObjectIf, + FetchEsque, +} from "@uploadthing/shared"; import type { FileRouter } from "uploadthing/server"; import type { inferEndpointInput } from "uploadthing/types"; @@ -33,6 +37,7 @@ export const GENERATE_useDocumentUploader = < TRouter extends FileRouter, >(initOpts: { url: URL; + fetch: FetchEsque; }) => { const useDocumentUploader = ( endpoint: TEndpoint, @@ -41,6 +46,7 @@ export const GENERATE_useDocumentUploader = < const { routeConfig, startUpload, isUploading } = __useUploadThingInternal( initOpts.url, endpoint, + initOpts.fetch, opts, ); const { mimeTypes, multiple } = useMemo( diff --git a/packages/expo/src/image-picker.ts b/packages/expo/src/image-picker.ts index e88603e5ee..19677d743c 100644 --- a/packages/expo/src/image-picker.ts +++ b/packages/expo/src/image-picker.ts @@ -4,7 +4,11 @@ import * as ImagePicker from "expo-image-picker"; import type { UseUploadthingProps } from "@uploadthing/react"; import { __useUploadThingInternal } from "@uploadthing/react/native"; import { generatePermittedFileTypes } from "@uploadthing/shared"; -import type { ExpandedRouteConfig, ExtendObjectIf } from "@uploadthing/shared"; +import type { + ExpandedRouteConfig, + ExtendObjectIf, + FetchEsque, +} from "@uploadthing/shared"; import type { FileRouter } from "uploadthing/server"; import type { inferEndpointInput } from "uploadthing/types"; @@ -25,6 +29,7 @@ export const GENERATE_useImageUploader = < TRouter extends FileRouter, >(initOpts: { url: URL; + fetch: FetchEsque; }) => { const useImageUploader = ( endpoint: TEndpoint, @@ -33,6 +38,7 @@ export const GENERATE_useImageUploader = < const { routeConfig, startUpload, isUploading } = __useUploadThingInternal( initOpts.url, endpoint, + initOpts.fetch, opts, ); const { mediaTypes, multiple } = useMemo( diff --git a/packages/expo/src/index.ts b/packages/expo/src/index.ts index 72ce22f685..c7816d63e2 100644 --- a/packages/expo/src/index.ts +++ b/packages/expo/src/index.ts @@ -1,6 +1,7 @@ import Constants from "expo-constants"; import { generateReactHelpers } from "@uploadthing/react/native"; +import type { FetchEsque } from "@uploadthing/shared"; import { warnIfInvalidPeerDependency } from "@uploadthing/shared"; import { version as uploadthingClientVersion } from "uploadthing/client"; import type { FileRouter } from "uploadthing/types"; @@ -20,6 +21,25 @@ export interface GenerateTypedHelpersOptions { * @default (process.env.EXPO_PUBLIC_SERVER_ORIGIN ?? ExpoConstants.debuggerHost) + "/api/uploadthing" */ url?: URL | string; + /** + * Provide a custom fetch implementation. + * @default `globalThis.fetch` + * @example + * ```ts + * fetch: (input, init) => { + * if (input.toString().startsWith(MY_SERVER_URL)) { + * // Include cookies in the request to your API + * return fetch(input, { + * ...init, + * credentials: "include", + * }); + * } + * + * return fetch(input, init); + * } + * ``` + */ + fetch?: FetchEsque | undefined; } export const generateReactNativeHelpers = ( @@ -48,9 +68,16 @@ export const generateReactNativeHelpers = ( ); } - const vanillaHelpers = generateReactHelpers({ ...initOpts, url }); - const useImageUploader = GENERATE_useImageUploader({ url }); - const useDocumentUploader = GENERATE_useDocumentUploader({ url }); + const fetch = initOpts?.fetch ?? globalThis.fetch; + const opts = { + ...initOpts, + url, + fetch, + }; + + const vanillaHelpers = generateReactHelpers(opts); + const useImageUploader = GENERATE_useImageUploader(opts); + const useDocumentUploader = GENERATE_useDocumentUploader(opts); return { ...vanillaHelpers, useImageUploader, useDocumentUploader }; }; diff --git a/packages/react/package.json b/packages/react/package.json index ef3754c9c1..7cefe9169d 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -81,7 +81,6 @@ "@types/react-dom": "18.3.0", "@uploadthing/eslint-config": "workspace:*", "@uploadthing/tsconfig": "workspace:*", - "@vitest/browser": "2.1.2", "bunchee": "^6.1.2", "concurrently": "^8.2.2", "eslint": "^8.57.0", @@ -90,12 +89,21 @@ "tailwindcss": "^3.4.16", "typescript": "^5.5.2", "uploadthing": "workspace:*", + "vitest-browser-react": "0.0.4", "wait-on": "^7.2.0", "zod": "^3.23.8" }, "eslintConfig": { "root": true, "rules": { + "no-console": "error", + "no-restricted-globals": [ + "error", + { + "name": "fetch", + "message": "fetch should be passed as parameter to support overriding default behaviors" + } + ], "no-restricted-imports": [ "error", { diff --git a/packages/react/src/components/button.tsx b/packages/react/src/components/button.tsx index f34304d32c..7cc600213b 100644 --- a/packages/react/src/components/button.tsx +++ b/packages/react/src/components/button.tsx @@ -111,6 +111,7 @@ export function UploadButton< const { startUpload, isUploading, routeConfig } = __useUploadThingInternal( resolveMaybeUrlArg($props.url), $props.endpoint, + $props.fetch ?? globalThis.fetch, { signal: acRef.current.signal, headers: $props.headers, diff --git a/packages/react/src/components/dropzone.tsx b/packages/react/src/components/dropzone.tsx index 06cca2f59a..4179359f66 100644 --- a/packages/react/src/components/dropzone.tsx +++ b/packages/react/src/components/dropzone.tsx @@ -146,6 +146,7 @@ export function UploadDropzone< const { startUpload, isUploading, routeConfig } = __useUploadThingInternal( resolveMaybeUrlArg($props.url), $props.endpoint, + $props.fetch ?? globalThis.fetch, { signal: acRef.current.signal, headers: $props.headers, diff --git a/packages/react/src/components/index.tsx b/packages/react/src/components/index.tsx index 77c78f4735..c9c0626d27 100644 --- a/packages/react/src/components/index.tsx +++ b/packages/react/src/components/index.tsx @@ -28,13 +28,20 @@ export const generateUploadButton = ( ); const url = resolveMaybeUrlArg(opts?.url); + const fetch = opts?.fetch ?? globalThis.fetch; const TypedButton = ( props: Omit< UploadButtonProps, keyof GenerateTypedHelpersOptions >, - ) => {...(props as any)} url={url} />; + ) => ( + + {...(props as any)} + url={url} + fetch={fetch} + /> + ); return TypedButton; }; @@ -48,13 +55,20 @@ export const generateUploadDropzone = ( ); const url = resolveMaybeUrlArg(opts?.url); + const fetch = opts?.fetch ?? globalThis.fetch; const TypedDropzone = ( props: Omit< UploadDropzoneProps, keyof GenerateTypedHelpersOptions >, - ) => {...(props as any)} url={url} />; + ) => ( + + {...(props as any)} + url={url} + fetch={fetch} + /> + ); return TypedDropzone; }; @@ -68,12 +82,15 @@ export const generateUploader = ( ); const url = resolveMaybeUrlArg(opts?.url); + const fetch = opts?.fetch ?? globalThis.fetch; const TypedUploader = ( props: Omit< UploadthingComponentProps, keyof GenerateTypedHelpersOptions >, - ) => {...(props as any)} url={url} />; + ) => ( + {...(props as any)} url={url} fetch={fetch} /> + ); return TypedUploader; }; diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index e215624d2a..68ffe205d2 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -2,6 +2,7 @@ import type { ClassListMerger, ErrorMessage, ExtendObjectIf, + FetchEsque, MaybePromise, UploadThingError, } from "@uploadthing/shared"; @@ -26,6 +27,25 @@ export interface GenerateTypedHelpersOptions { * @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing" */ url?: string | URL; + /** + * Provide a custom fetch implementation. + * @default `globalThis.fetch` + * @example + * ```ts + * fetch: (input, init) => { + * if (input.toString().startsWith(MY_SERVER_URL)) { + * // Include cookies in the request to your API + * return fetch(input, { + * ...init, + * credentials: "include", + * }); + * } + * + * return fetch(input, init); + * } + * ``` + */ + fetch?: FetchEsque | undefined; } export type UseUploadthingProps< @@ -122,6 +142,25 @@ export type UploadthingComponentProps< * @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing" */ url?: string | URL; + /** + * Provide a custom fetch implementation. + * @default `globalThis.fetch` + * @example + * ```ts + * fetch: (input, init) => { + * if (input.toString().startsWith(MY_SERVER_URL)) { + * // Include cookies in the request to your API + * return fetch(input, { + * ...init, + * credentials: "include", + * }); + * } + * + * return fetch(input, init); + * } + * ``` + */ + fetch?: FetchEsque | undefined; config?: { mode?: "auto" | "manual"; appendOnPaste?: boolean; diff --git a/packages/react/src/useUploadThing.ts b/packages/react/src/useUploadThing.ts index 4615e287bd..42d629ca1c 100644 --- a/packages/react/src/useUploadThing.ts +++ b/packages/react/src/useUploadThing.ts @@ -3,6 +3,7 @@ import { useRef, useState } from "react"; import type { EndpointMetadata, ExpandedRouteConfig, + FetchEsque, } from "@uploadthing/shared"; import { INTERNAL_DO_NOT_USE__fatalClientError, @@ -30,14 +31,17 @@ import useFetch from "./utils/useFetch"; declare const globalThis: { __UPLOADTHING?: EndpointMetadata; + fetch: FetchEsque; }; const useRouteConfig = ( + fetch: FetchEsque, url: URL, endpoint: string, ): ExpandedRouteConfig | undefined => { const maybeServerData = globalThis.__UPLOADTHING; const { data } = useFetch( + fetch, // Don't fetch if we already have the data maybeServerData ? undefined : url.href, ); @@ -55,9 +59,11 @@ export function __useUploadThingInternal< >( url: URL, endpoint: EndpointArg, + fetch: FetchEsque, opts?: UseUploadthingProps, ) { const { uploadFiles, routeRegistry } = genUploader({ + fetch, url, package: "@uploadthing/react", }); @@ -120,6 +126,7 @@ export function __useUploadThingInternal< error = e as UploadThingError>; } else { error = INTERNAL_DO_NOT_USE__fatalClientError(e as Error); + // eslint-disable-next-line no-console console.error( "Something went wrong. Please contact UploadThing and provide the following cause:", error.cause instanceof Error ? error.cause.toString() : error.cause, @@ -134,7 +141,7 @@ export function __useUploadThingInternal< }); const _endpoint = unwrap(endpoint, routeRegistry); - const routeConfig = useRouteConfig(url, _endpoint as string); + const routeConfig = useRouteConfig(fetch, url, _endpoint as string); return { startUpload, @@ -152,9 +159,11 @@ export const generateReactHelpers = ( uploadthingClientVersion, ); + const fetch = initOpts?.fetch ?? globalThis.fetch; const url = resolveMaybeUrlArg(initOpts?.url); const clientHelpers = genUploader({ + fetch, url, package: "@uploadthing/react", }); @@ -163,7 +172,7 @@ export const generateReactHelpers = ( endpoint: EndpointArg, opts?: UseUploadthingProps, ) { - return __useUploadThingInternal(url, endpoint, opts); + return __useUploadThingInternal(url, endpoint, fetch, opts); } function getRouteConfig(slug: EndpointArg) { diff --git a/packages/react/src/utils/useFetch.ts b/packages/react/src/utils/useFetch.ts index af7353e9ce..41e424939f 100644 --- a/packages/react/src/utils/useFetch.ts +++ b/packages/react/src/utils/useFetch.ts @@ -1,6 +1,7 @@ // Ripped from https://usehooks-ts.com/react-hook/use-fetch import { useEffect, useReducer, useRef } from "react"; +import type { FetchEsque } from "@uploadthing/shared"; import { safeParseJSON } from "@uploadthing/shared"; interface State { @@ -16,7 +17,11 @@ type Action = | { type: "fetched"; payload: T } | { type: "error"; payload: Error }; -function useFetch(url?: string, options?: RequestInit): State { +function useFetch( + fetch: FetchEsque, + url?: string, + options?: RequestInit, +): State { const cache = useRef>({}); // Used to prevent state update if the component is unmounted diff --git a/packages/react/test/client-generator.test.ts b/packages/react/test/client-generator.test.ts index 82530e9776..2f158502f7 100644 --- a/packages/react/test/client-generator.test.ts +++ b/packages/react/test/client-generator.test.ts @@ -1,4 +1,4 @@ -import { describe, expectTypeOf, it } from "vitest"; +import { expectTypeOf, it } from "vitest"; import * as z from "zod"; import { createUploadthing } from "uploadthing/server"; @@ -16,8 +16,6 @@ const router = { exampleRoute: f(["image"]) .middleware(() => ({ foo: "bar" })) .onUploadComplete(({ metadata }) => { - console.log(metadata); - return { foo: "bar" as const }; }), @@ -25,8 +23,6 @@ const router = { .input(z.object({ foo: z.string() })) .middleware((opts) => ({ number: opts.input.foo.length })) .onUploadComplete(({ metadata }) => { - console.log(metadata); - return { baz: "qux" as const }; }), @@ -34,7 +30,7 @@ const router = { .input(z.object({ bar: z.number() })) .middleware((opts) => ({ square: opts.input.bar * opts.input.bar })) .onUploadComplete(({ metadata }) => { - console.log(metadata); + // }), withTransformedInput: f(["image"], { awaitServerData: false }) @@ -44,7 +40,7 @@ const router = { return { string: opts.input.bar }; }) .onUploadComplete(({ metadata }) => { - console.log(metadata); + // }), // Should technically block returning undefined but there was diff --git a/packages/react/test/upload-button.test.tsx b/packages/react/test/upload-button.browser.test.tsx similarity index 76% rename from packages/react/test/upload-button.test.tsx rename to packages/react/test/upload-button.browser.test.tsx index 5ecfe5e41c..d4a6bbc8d3 100644 --- a/packages/react/test/upload-button.test.tsx +++ b/packages/react/test/upload-button.browser.test.tsx @@ -1,18 +1,8 @@ -// @vitest-environment happy-dom -/// - -import { cleanup, fireEvent, render, waitFor } from "@testing-library/react"; +import { page, userEvent } from "@vitest/browser/context"; import { http, HttpResponse } from "msw"; -import { setupServer } from "msw/node"; -import { - afterAll, - afterEach, - beforeAll, - describe, - expect, - it, - vi, -} from "vitest"; +import { setupWorker } from "msw/browser"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { render } from "vitest-browser-react"; import { createRouteHandler, @@ -44,7 +34,7 @@ const utGet = vi.fn<(req: Request) => void>(); const utPost = vi.fn<({ request, body }: { request: Request; body: any }) => void>(); -const server = setupServer( +const worker = setupWorker( http.get("/api/uploadthing", ({ request }) => { utGet(request); return routeHandler(request); @@ -62,12 +52,8 @@ const server = setupServer( ), ); -beforeAll(() => server.listen()); -afterEach(() => { - server.resetHandlers(); - cleanup(); -}); -afterAll(() => server.close()); +beforeAll(() => worker.start({ quiet: true })); +afterAll(() => worker.stop()); /** * This is a basic suite of tests for the UploadButton component. @@ -109,12 +95,14 @@ describe("UploadButton - basic", () => { // expect(label).toHaveTextContent("Loading..."); // then eventually we load in the data, and we should be in the ready state - await waitFor(() => expect(label).toHaveAttribute("data-state", "ready")); + await vi.waitFor(() => + expect(label).toHaveAttribute("data-state", "ready"), + ); expect(label).toHaveTextContent("Choose File"); expect(label).not.toHaveTextContent("(s)"); expect(utGet).toHaveBeenCalledOnce(); - expect(utils.getByText("Image (4MB)")).toBeInTheDocument(); + await expect.element(page.getByText("Image (4MB)")).toBeVisible(); }); it("fetches and displays route config (with callback endpoint arg)", async () => { @@ -129,12 +117,14 @@ describe("UploadButton - basic", () => { // expect(label).toHaveTextContent("Loading..."); // then eventually we load in the data, and we should be in the ready state - await waitFor(() => expect(label).toHaveAttribute("data-state", "ready")); + await vi.waitFor(() => + expect(label).toHaveAttribute("data-state", "ready"), + ); expect(label).toHaveTextContent("Choose File"); expect(label).not.toHaveTextContent("(s)"); expect(utGet).toHaveBeenCalledOnce(); - expect(utils.getByText("Image (4MB)")).toBeInTheDocument(); + await expect.element(page.getByText("Image (4MB)")).toBeVisible(); }); it("shows plural when maxFileCount is > 1", async () => { @@ -147,7 +137,9 @@ describe("UploadButton - basic", () => { // expect(label).toHaveTextContent("Loading..."); // then eventually we load in the data, and we should be in the ready state - await waitFor(() => expect(label).toHaveAttribute("data-state", "ready")); + await vi.waitFor(() => + expect(label).toHaveAttribute("data-state", "ready"), + ); expect(label).toHaveTextContent("Choose File(s)"); }); @@ -155,7 +147,7 @@ describe("UploadButton - basic", () => { (window as any).__UPLOADTHING = extractRouterConfig(testRouter); const utils = render(); - expect(utils.getByText("Image (4MB)")).toBeInTheDocument(); + await expect.element(page.getByText("Image (4MB)")).toBeVisible(); expect(utGet).not.toHaveBeenCalled(); delete (window as any).__UPLOADTHING; @@ -164,12 +156,14 @@ describe("UploadButton - basic", () => { it("requests URLs when a file is selected", async () => { const utils = render(); const label = utils.container.querySelector("label"); - await waitFor(() => expect(label).toHaveAttribute("data-state", "ready")); + await vi.waitFor(() => + expect(label).toHaveAttribute("data-state", "ready"), + ); - fireEvent.change(utils.getByLabelText("Choose File"), { - target: { files: [new File(["foo"], "foo.txt", { type: "text/plain" })] }, - }); - await waitFor(() => { + await userEvent.upload(utils.getByLabelText("Choose File"), [ + new File(["foo"], "foo.txt", { type: "text/plain" }), + ]); + await vi.waitFor(() => { expect(utPost).toHaveBeenCalledWith( expect.objectContaining({ body: { @@ -177,7 +171,7 @@ describe("UploadButton - basic", () => { { name: "foo.txt", type: "text/plain", - size: 3, + size: 18, lastModified: expect.any(Number), }, ], @@ -192,15 +186,17 @@ describe("UploadButton - basic", () => { , ); const label = utils.container.querySelector("label"); - await waitFor(() => expect(label).toHaveAttribute("data-state", "ready")); + await vi.waitFor(() => + expect(label).toHaveAttribute("data-state", "ready"), + ); - fireEvent.change(utils.getByLabelText("Choose File"), { - target: { files: [new File([""], "foo.txt")] }, - }); + await userEvent.upload(utils.getByLabelText("Choose File"), [ + new File([""], "foo.txt"), + ]); expect(label).toHaveTextContent("Upload 1 file"); - fireEvent.click(label!); - await waitFor(() => { + await userEvent.click(label!); + await vi.waitFor(() => { expect(utPost).toHaveBeenCalledWith( expect.objectContaining({ body: { @@ -215,7 +211,7 @@ describe("UploadButton - basic", () => { it.skip("disabled", async () => { const utils = render(); const label = utils.container.querySelector("label"); - await waitFor(() => expect(label).toHaveTextContent("Choose File")); + await vi.waitFor(() => expect(label).toHaveTextContent("Choose File")); expect(label).toHaveAttribute("disabled"); }); }); @@ -230,14 +226,13 @@ describe("UploadButton - lifecycle hooks", () => { }} />, ); - await waitFor(() => { - expect(utils.getByText("Choose File")).toBeInTheDocument(); - }); - fireEvent.change(utils.getByLabelText("Choose File"), { - target: { files: [new File([""], "foo.txt")] }, - }); - await waitFor(() => { + await expect.element(page.getByText("Choose File")).toBeVisible(); + + await userEvent.upload(utils.getByLabelText("Choose File"), [ + new File([""], "foo.txt"), + ]); + await vi.waitFor(() => { expect(utPost).toHaveBeenCalledWith( expect.objectContaining({ body: { @@ -253,14 +248,14 @@ describe("UploadButton - lifecycle hooks", () => { const utils = render( , ); - await waitFor(() => { - expect(utils.getByText("Choose File(s)")).toBeInTheDocument(); - }); - fireEvent.change(utils.getByLabelText("Choose File(s)"), { - target: { files: [new File([""], "foo.png"), new File([""], "bar.png")] }, - }); - await waitFor(() => { + await expect.element(page.getByText("Choose File(s)")).toBeVisible(); + + await userEvent.upload(utils.getByLabelText("Choose File(s)"), [ + new File([""], "foo.png"), + new File([""], "bar.png"), + ]); + await vi.waitFor(() => { expect(onUploadBegin).toHaveBeenCalledTimes(2); }); expect(onUploadBegin).toHaveBeenCalledWith("foo.png"); @@ -278,17 +273,18 @@ describe("UploadButton - Theming", () => { }} />, ); - await waitFor(() => { - expect(utils.getByText("Choose File")).toHaveStyle({ - backgroundColor: "red", - }); + + await vi.waitFor(async () => { + await expect + .element(page.getByText("Choose File").element()) + .toHaveStyle("background-color: rgb(255, 0, 0);"); }); }); }); describe("UploadButton - Content Customization", () => { it("renders custom content", async () => { - const utils = render( + render( { }} />, ); - await waitFor(() => { - expect(utils.getByText("Allowed content")).toBeInTheDocument(); - }); + await expect.element(page.getByText("Allowed content")).toBeVisible(); }); }); diff --git a/packages/react/test/upload-dropzone.test.tsx b/packages/react/test/upload-dropzone.browser.test.tsx similarity index 75% rename from packages/react/test/upload-dropzone.test.tsx rename to packages/react/test/upload-dropzone.browser.test.tsx index 0253ef7453..e5b4e0c422 100644 --- a/packages/react/test/upload-dropzone.test.tsx +++ b/packages/react/test/upload-dropzone.browser.test.tsx @@ -1,24 +1,10 @@ -// @vitest-environment happy-dom -/// - -import { cleanup, render, waitFor } from "@testing-library/react"; +import { page } from "@vitest/browser/context"; import { http, HttpResponse } from "msw"; -import { setupServer } from "msw/node"; -import { - afterAll, - afterEach, - beforeAll, - describe, - expect, - it, - vi, -} from "vitest"; - -import { - createRouteHandler, - createUploadthing, - extractRouterConfig, -} from "uploadthing/server"; +import { setupWorker } from "msw/browser"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { render } from "vitest-browser-react"; + +import { createRouteHandler, createUploadthing } from "uploadthing/server"; import { generateUploadDropzone } from "../src"; @@ -44,7 +30,7 @@ const utGet = vi.fn<(req: Request) => void>(); const utPost = vi.fn<({ request, body }: { request: Request; body: any }) => void>(); -const server = setupServer( +const worker = setupWorker( http.get("/api/uploadthing", ({ request }) => { utGet(request); return routeHandler(request); @@ -62,12 +48,8 @@ const server = setupServer( ), ); -beforeAll(() => server.listen()); -afterEach(() => { - server.resetHandlers(); - cleanup(); -}); -afterAll(() => server.close()); +beforeAll(() => worker.start({ quiet: true })); +afterAll(() => worker.stop()); describe("UploadDropzone - basic", () => { it("fetches and displays route config", async () => { @@ -80,12 +62,14 @@ describe("UploadDropzone - basic", () => { // expect(label).toHaveTextContent("Loading..."); // then eventually we load in the data, and we should be in the ready state - await waitFor(() => expect(label).toHaveAttribute("data-state", "ready")); + await vi.waitFor(() => + expect(label).toHaveAttribute("data-state", "ready"), + ); expect(label).toHaveTextContent("Choose a file or drag and drop"); expect(label).not.toHaveTextContent("(s)"); expect(utGet).toHaveBeenCalledOnce(); - expect(utils.getByText("Image (4MB)")).toBeInTheDocument(); + await expect.element(page.getByText("Image (4MB)")).toBeVisible(); }); it("shows plural when maxFileCount is > 1", async () => { @@ -98,7 +82,9 @@ describe("UploadDropzone - basic", () => { // expect(label).toHaveTextContent("Loading..."); // then eventually we load in the data, and we should be in the ready state - await waitFor(() => expect(label).toHaveAttribute("data-state", "ready")); + await vi.waitFor(() => + expect(label).toHaveAttribute("data-state", "ready"), + ); expect(label).toHaveTextContent("Choose file(s) or drag and drop"); }); }); diff --git a/packages/react/vitest.config.ts b/packages/react/vitest.config.ts deleted file mode 100644 index 8d2e96948f..0000000000 --- a/packages/react/vitest.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { mergeConfig } from "vitest/config"; - -import baseConfig from "../../vitest.config"; - -export default mergeConfig(baseConfig, { - test: { - setupFiles: ["@testing-library/jest-dom/vitest"], - }, -}); diff --git a/packages/shared/package.json b/packages/shared/package.json index 1e43229c57..702a382aeb 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "@uploadthing/mime-types": "workspace:*", - "effect": "3.11.5", + "effect": "3.12.0", "sqids": "^0.3.0" }, "devDependencies": { diff --git a/packages/solid/package.json b/packages/solid/package.json index 3f66a66180..187801406c 100644 --- a/packages/solid/package.json +++ b/packages/solid/package.json @@ -134,6 +134,14 @@ "eslintConfig": { "root": true, "rules": { + "no-console": "error", + "no-restricted-globals": [ + "error", + { + "name": "fetch", + "message": "fetch should be passed as parameter to support overriding default behaviors" + } + ], "no-restricted-imports": [ "error", { diff --git a/packages/solid/src/components/button.tsx b/packages/solid/src/components/button.tsx index b506cad932..3676c65c91 100644 --- a/packages/solid/src/components/button.tsx +++ b/packages/solid/src/components/button.tsx @@ -19,7 +19,7 @@ import type { } from "@uploadthing/shared"; import type { FileRouter } from "uploadthing/types"; -import { INTERNAL_createUploadThingGen } from "../create-uploadthing"; +import { __createUploadThingInternal } from "../create-uploadthing"; import type { UploadthingComponentProps } from "../types"; import { Cancel, progressWidths, Spinner } from "./shared"; @@ -79,9 +79,6 @@ export function UploadButton< : UploadButtonProps, ) { const $props = props as UploadButtonProps; - const createUploadThing = INTERNAL_createUploadThingGen({ - url: resolveMaybeUrlArg($props.url), - }); const { mode = "auto", @@ -94,23 +91,28 @@ export function UploadButton< const [uploadProgress, setUploadProgress] = createSignal(0); const [files, setFiles] = createSignal([]); - const uploadThing = createUploadThing($props.endpoint, { - signal: acRef.signal, - headers: $props.headers, - onClientUploadComplete: (res) => { - setFiles([]); - inputRef.value = ""; - void $props.onClientUploadComplete?.(res); - setUploadProgress(0); - }, - onUploadProgress: (p) => { - setUploadProgress(p); - $props.onUploadProgress?.(p); + const uploadThing = __createUploadThingInternal( + resolveMaybeUrlArg($props.url), + $props.endpoint, + $props.fetch ?? globalThis.fetch, + { + signal: acRef.signal, + headers: $props.headers, + onClientUploadComplete: (res) => { + setFiles([]); + inputRef.value = ""; + void $props.onClientUploadComplete?.(res); + setUploadProgress(0); + }, + onUploadProgress: (p) => { + setUploadProgress(p); + $props.onUploadProgress?.(p); + }, + onUploadError: $props.onUploadError, + onUploadBegin: $props.onUploadBegin, + onBeforeUploadBegin: $props.onBeforeUploadBegin, }, - onUploadError: $props.onUploadError, - onUploadBegin: $props.onUploadBegin, - onBeforeUploadBegin: $props.onBeforeUploadBegin, - }); + ); const fileInfo = () => generatePermittedFileTypes(uploadThing.routeConfig()); diff --git a/packages/solid/src/components/dropzone.tsx b/packages/solid/src/components/dropzone.tsx index d79df58545..7f00b7c046 100644 --- a/packages/solid/src/components/dropzone.tsx +++ b/packages/solid/src/components/dropzone.tsx @@ -42,7 +42,7 @@ import type { } from "@uploadthing/shared"; import type { FileRouter } from "uploadthing/types"; -import { INTERNAL_createUploadThingGen } from "../create-uploadthing"; +import { __createUploadThingInternal } from "../create-uploadthing"; import type { UploadthingComponentProps } from "../types"; import { Cancel, progressWidths, Spinner } from "./shared"; @@ -105,9 +105,6 @@ export const UploadDropzone = < : UploadDropzoneProps, ) => { const $props = props as UploadDropzoneProps; - const createUploadThing = INTERNAL_createUploadThingGen({ - url: resolveMaybeUrlArg($props.url), - }); const { mode = "manual", @@ -120,22 +117,27 @@ export const UploadDropzone = < const [files, setFiles] = createSignal([]); const [uploadProgress, setUploadProgress] = createSignal(0); - const uploadThing = createUploadThing($props.endpoint, { - signal: acRef.signal, - headers: $props.headers, - onClientUploadComplete: (res) => { - setFiles([]); - void $props.onClientUploadComplete?.(res); - setUploadProgress(0); - }, - onUploadProgress: (p) => { - setUploadProgress(p); - $props.onUploadProgress?.(p); + const uploadThing = __createUploadThingInternal( + resolveMaybeUrlArg($props.url), + $props.endpoint, + $props.fetch ?? globalThis.fetch, + { + signal: acRef.signal, + headers: $props.headers, + onClientUploadComplete: (res) => { + setFiles([]); + void $props.onClientUploadComplete?.(res); + setUploadProgress(0); + }, + onUploadProgress: (p) => { + setUploadProgress(p); + $props.onUploadProgress?.(p); + }, + onUploadError: $props.onUploadError, + onUploadBegin: $props.onUploadBegin, + onBeforeUploadBegin: $props.onBeforeUploadBegin, }, - onUploadError: $props.onUploadError, - onUploadBegin: $props.onUploadBegin, - onBeforeUploadBegin: $props.onBeforeUploadBegin, - }); + ); const fileInfo = () => generatePermittedFileTypes(uploadThing.routeConfig()); const state = () => { diff --git a/packages/solid/src/components/index.tsx b/packages/solid/src/components/index.tsx index 4d1d1948c9..ac424496eb 100644 --- a/packages/solid/src/components/index.tsx +++ b/packages/solid/src/components/index.tsx @@ -17,13 +17,20 @@ export const generateUploadButton = ( opts?: GenerateTypedHelpersOptions, ) => { const url = resolveMaybeUrlArg(opts?.url); + const fetch = opts?.fetch ?? globalThis.fetch; const TypedButton = ( props: Omit< UploadButtonProps, keyof GenerateTypedHelpersOptions >, - ) => {...(props as any)} url={url} />; + ) => ( + + {...(props as any)} + url={url} + fetch={fetch} + /> + ); return TypedButton; }; @@ -31,13 +38,20 @@ export const generateUploadDropzone = ( opts?: GenerateTypedHelpersOptions, ) => { const url = resolveMaybeUrlArg(opts?.url); + const fetch = opts?.fetch ?? globalThis.fetch; const TypedDropzone = ( props: Omit< UploadDropzoneProps, keyof GenerateTypedHelpersOptions >, - ) => {...(props as any)} url={url} />; + ) => ( + + {...(props as any)} + url={url} + fetch={fetch} + /> + ); return TypedDropzone; }; @@ -45,12 +59,15 @@ export const generateUploader = ( opts?: GenerateTypedHelpersOptions, ) => { const url = resolveMaybeUrlArg(opts?.url); + const fetch = opts?.fetch ?? globalThis.fetch; const TypedUploader = ( props: Omit< UploadthingComponentProps, keyof GenerateTypedHelpersOptions >, - ) => {...(props as any)} url={url} />; + ) => ( + {...(props as any)} url={url} fetch={fetch} /> + ); return TypedUploader; }; diff --git a/packages/solid/src/create-uploadthing.ts b/packages/solid/src/create-uploadthing.ts index 7bd00fafd1..2d9684ab74 100644 --- a/packages/solid/src/create-uploadthing.ts +++ b/packages/solid/src/create-uploadthing.ts @@ -1,14 +1,22 @@ import { createSignal } from "solid-js"; +import type { + EndpointMetadata, + ExpandedRouteConfig, + FetchEsque, +} from "@uploadthing/shared"; import { INTERNAL_DO_NOT_USE__fatalClientError, resolveMaybeUrlArg, unwrap, UploadAbortedError, UploadThingError, + warnIfInvalidPeerDependency, } from "@uploadthing/shared"; -import type { EndpointMetadata } from "@uploadthing/shared"; -import { genUploader } from "uploadthing/client"; +import { + genUploader, + version as uploadthingClientVersion, +} from "uploadthing/client"; import type { EndpointArg, FileRouter, @@ -16,137 +24,144 @@ import type { inferErrorShape, } from "uploadthing/types"; +import { peerDependencies } from "../package.json"; import type { CreateUploadthingProps, GenerateTypedHelpersOptions, } from "./types"; import { createFetch } from "./utils/createFetch"; -const createRouteConfig = (url: URL, endpoint: string) => { - const dataGetter = createFetch(url.href); +const createRouteConfig = ( + fetchFn: FetchEsque, + url: URL, + endpoint: string, +): (() => ExpandedRouteConfig | undefined) => { + const dataGetter = createFetch(fetchFn, url.href); return () => dataGetter()?.data?.find((x) => x.slug === endpoint)?.config; }; -export const INTERNAL_createUploadThingGen = < +export function __createUploadThingInternal< TRouter extends FileRouter, ->(initOpts: { - /** - * URL to the UploadThing API endpoint - * @example URL { http://localhost:3000/api/uploadthing } - * @example URL { https://www.example.com/api/uploadthing } - */ - url: URL; -}) => { + TEndpoint extends keyof TRouter, +>( + url: URL, + endpoint: EndpointArg, + fetch: FetchEsque, + opts?: CreateUploadthingProps, +) { const { uploadFiles, routeRegistry } = genUploader({ - url: initOpts.url, + fetch, + url, package: "@uploadthing/solid", }); - const createUploadThing = ( - endpoint: EndpointArg, - opts?: CreateUploadthingProps, - ) => { - const [isUploading, setUploading] = createSignal(false); - let uploadProgress = 0; - let fileProgress = new Map(); - - type InferredInput = inferEndpointInput; - type FuncInput = undefined extends InferredInput - ? [files: File[], input?: undefined] - : [files: File[], input: InferredInput]; - - const startUpload = async (...args: FuncInput) => { - const files = (await opts?.onBeforeUploadBegin?.(args[0])) ?? args[0]; - const input = args[1]; - - setUploading(true); - opts?.onUploadProgress?.(0); - files.forEach((f) => fileProgress.set(f, 0)); - try { - const res = await uploadFiles(endpoint, { - signal: opts?.signal, - headers: opts?.headers, - files, - onUploadProgress: (progress) => { - if (!opts?.onUploadProgress) return; - fileProgress.set(progress.file, progress.progress); - let sum = 0; - fileProgress.forEach((p) => { - sum += p; - }); - const averageProgress = - Math.floor(sum / fileProgress.size / 10) * 10; - if (averageProgress !== uploadProgress) { - opts?.onUploadProgress?.(averageProgress); - uploadProgress = averageProgress; - } - }, - onUploadBegin({ file }) { - if (!opts?.onUploadBegin) return; - - opts.onUploadBegin(file); - }, - // @ts-expect-error - input may not be defined on the type - input, - }); - - await opts?.onClientUploadComplete?.(res); - return res; - } catch (e) { - /** - * This is the only way to introduce this as a non-breaking change - * TODO: Consider refactoring API in the next major version - */ - if (e instanceof UploadAbortedError) throw e; - - let error: UploadThingError>; - if (e instanceof UploadThingError) { - error = e as UploadThingError>; - } else { - error = INTERNAL_DO_NOT_USE__fatalClientError(e as Error); - console.error( - "Something went wrong. Please contact UploadThing and provide the following cause:", - error.cause instanceof Error ? error.cause.toString() : error.cause, - ); - } - await opts?.onUploadError?.(error); - return; - } finally { - setUploading(false); - fileProgress = new Map(); - uploadProgress = 0; - } - }; - - const _endpoint = unwrap(endpoint, routeRegistry); - const routeConfig = createRouteConfig(initOpts.url, _endpoint as string); - - return { - startUpload, - isUploading, - routeConfig, + const [isUploading, setUploading] = createSignal(false); + let uploadProgress = 0; + let fileProgress = new Map(); + + type InferredInput = inferEndpointInput; + type FuncInput = undefined extends InferredInput + ? [files: File[], input?: undefined] + : [files: File[], input: InferredInput]; + + const startUpload = async (...args: FuncInput) => { + const files = (await opts?.onBeforeUploadBegin?.(args[0])) ?? args[0]; + const input = args[1]; + + setUploading(true); + opts?.onUploadProgress?.(0); + files.forEach((f) => fileProgress.set(f, 0)); + try { + const res = await uploadFiles(endpoint, { + signal: opts?.signal, + headers: opts?.headers, + files, + onUploadProgress: (progress) => { + if (!opts?.onUploadProgress) return; + fileProgress.set(progress.file, progress.progress); + let sum = 0; + fileProgress.forEach((p) => { + sum += p; + }); + const averageProgress = Math.floor(sum / fileProgress.size / 10) * 10; + if (averageProgress !== uploadProgress) { + opts?.onUploadProgress?.(averageProgress); + uploadProgress = averageProgress; + } + }, + onUploadBegin({ file }) { + if (!opts?.onUploadBegin) return; + opts.onUploadBegin(file); + }, + // @ts-expect-error - input may not be defined on the type + input, + }); + + await opts?.onClientUploadComplete?.(res); + return res; + } catch (e) { /** - * @deprecated Use `routeConfig` instead + * This is the only way to introduce this as a non-breaking change + * TODO: Consider refactoring API in the next major version */ - permittedFileInfo: routeConfig - ? { slug: _endpoint, config: routeConfig } - : undefined, - } as const; + if (e instanceof UploadAbortedError) throw e; + + let error: UploadThingError>; + if (e instanceof UploadThingError) { + error = e as UploadThingError>; + } else { + error = INTERNAL_DO_NOT_USE__fatalClientError(e as Error); + // eslint-disable-next-line no-console + console.error( + "Something went wrong. Please contact UploadThing and provide the following cause:", + error.cause instanceof Error ? error.cause.toString() : error.cause, + ); + } + await opts?.onUploadError?.(error); + } finally { + setUploading(false); + fileProgress = new Map(); + uploadProgress = 0; + } }; - return createUploadThing; -}; + const _endpoint = unwrap(endpoint, routeRegistry); + const routeConfig = createRouteConfig(fetch, url, _endpoint as string); + + return { + startUpload, + isUploading, + routeConfig, + } as const; +} export const generateSolidHelpers = ( initOpts?: GenerateTypedHelpersOptions, ) => { + warnIfInvalidPeerDependency( + "@uploadthing/solid", + peerDependencies.uploadthing, + uploadthingClientVersion, + ); + + const fetch = initOpts?.fetch ?? globalThis.fetch; const url = resolveMaybeUrlArg(initOpts?.url); + const clientHelpers = genUploader({ + fetch, + url, + package: "@uploadthing/solid", + }); + + function useUploadThing( + endpoint: EndpointArg, + opts?: CreateUploadthingProps, + ) { + return __createUploadThingInternal(url, endpoint, fetch, opts); + } + return { - createUploadThing: INTERNAL_createUploadThingGen({ url }), - ...genUploader({ - url, - package: "@uploadthing/solid", - }), + useUploadThing, + ...clientHelpers, } as const; }; diff --git a/packages/solid/src/types.ts b/packages/solid/src/types.ts index 1f7ebe580b..9dba26dfac 100644 --- a/packages/solid/src/types.ts +++ b/packages/solid/src/types.ts @@ -2,6 +2,7 @@ import type { ClassListMerger, ErrorMessage, ExtendObjectIf, + FetchEsque, MaybePromise, UploadThingError, } from "@uploadthing/shared"; @@ -26,6 +27,25 @@ export interface GenerateTypedHelpersOptions { * @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing" */ url?: string | URL; + /** + * Provide a custom fetch implementation. + * @default `globalThis.fetch` + * @example + * ```ts + * fetch: (input, init) => { + * if (input.toString().startsWith(MY_SERVER_URL)) { + * // Include cookies in the request to your API + * return fetch(input, { + * ...init, + * credentials: "include", + * }); + * } + * + * return fetch(input, init); + * } + * ``` + */ + fetch?: FetchEsque | undefined; } export type CreateUploadthingProps< @@ -130,6 +150,25 @@ export type UploadthingComponentProps< * @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing" */ url?: string | URL; + /** + * Provide a custom fetch implementation. + * @default `globalThis.fetch` + * @example + * ```ts + * fetch: (input, init) => { + * if (input.toString().startsWith(MY_SERVER_URL)) { + * // Include cookies in the request to your API + * return fetch(input, { + * ...init, + * credentials: "include", + * }); + * } + * + * return fetch(input, init); + * } + * ``` + */ + fetch?: FetchEsque | undefined; config?: { mode?: "manual" | "auto"; appendOnPaste?: boolean; diff --git a/packages/solid/src/utils/createFetch.ts b/packages/solid/src/utils/createFetch.ts index 143ee451fe..37c578a984 100644 --- a/packages/solid/src/utils/createFetch.ts +++ b/packages/solid/src/utils/createFetch.ts @@ -1,6 +1,7 @@ import type { Resource } from "solid-js"; import { createResource } from "solid-js"; +import type { FetchEsque } from "@uploadthing/shared"; import { safeParseJSON } from "@uploadthing/shared"; interface State { @@ -11,7 +12,11 @@ interface State { type Cache = Record; -export function createFetch(url?: string, options?: RequestInit) { +export function createFetch( + fetchFn: FetchEsque, + url?: string, + options?: RequestInit, +) { const cache: Cache = {}; const [res] = createResource(async () => { if (!url) @@ -23,7 +28,7 @@ export function createFetch(url?: string, options?: RequestInit) { return { type: "fetched", data: cache[url] }; } try { - const response = await fetch(url, options); + const response = await fetchFn(url, options); if (!response.ok) { throw new Error(response.statusText); } diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 282d18cab6..71df348c7d 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -65,6 +65,14 @@ "*.config.*" ], "rules": { + "no-console": "error", + "no-restricted-globals": [ + "error", + { + "name": "fetch", + "message": "fetch should be passed as parameter to support overriding default behaviors" + } + ], "no-restricted-imports": [ "error", { diff --git a/packages/svelte/src/lib/component/UploadButton.svelte b/packages/svelte/src/lib/component/UploadButton.svelte index a05d0699da..e75883de3a 100644 --- a/packages/svelte/src/lib/component/UploadButton.svelte +++ b/packages/svelte/src/lib/component/UploadButton.svelte @@ -26,7 +26,7 @@ generatePermittedFileTypes, } from "uploadthing/client"; - import { INTERNAL_createUploadThingGen } from "../create-uploadthing"; + import { __createUploadThingInternal } from "../create-uploadthing"; import type { UploadthingComponentProps } from "../types"; import Cancel from "./Cancel.svelte"; import { getFilesFromClipboardEvent, progressWidths } from "./shared"; @@ -60,16 +60,14 @@ } = uploader.config ?? {}); let acRef = new AbortController(); - const createUploadThing = INTERNAL_createUploadThingGen({ - url: resolveMaybeUrlArg(uploader.url), - }); - let fileInputRef: HTMLInputElement; let uploadProgress = 0; let files: File[] = []; - const { startUpload, isUploading, routeConfig } = createUploadThing( + const { startUpload, isUploading, routeConfig } = __createUploadThingInternal( + resolveMaybeUrlArg(uploader.url), uploader.endpoint, + uploader.fetch ?? globalThis.fetch, { signal: acRef.signal, headers: uploader.headers, diff --git a/packages/svelte/src/lib/component/UploadDropzone.svelte b/packages/svelte/src/lib/component/UploadDropzone.svelte index 4e0aba2c9e..e5670f748d 100644 --- a/packages/svelte/src/lib/component/UploadDropzone.svelte +++ b/packages/svelte/src/lib/component/UploadDropzone.svelte @@ -26,7 +26,7 @@ generatePermittedFileTypes, } from "uploadthing/client"; - import { INTERNAL_createUploadThingGen } from "../create-uploadthing"; + import { __createUploadThingInternal } from "../create-uploadthing"; import type { UploadthingComponentProps } from "../types"; import Cancel from "./Cancel.svelte"; import { createDropzone } from "./create-dropzone"; @@ -80,11 +80,10 @@ $: uploadProgress = 0; let rootRef: HTMLElement; - const createUploadThing = INTERNAL_createUploadThingGen({ - url: resolveMaybeUrlArg(uploader.url), - }); - const { startUpload, isUploading, routeConfig } = createUploadThing( + const { startUpload, isUploading, routeConfig } = __createUploadThingInternal( + resolveMaybeUrlArg(uploader.url), uploader.endpoint, + uploader.fetch ?? globalThis.fetch, { signal: acRef.signal, headers: uploader.headers, diff --git a/packages/svelte/src/lib/create-uploadthing.ts b/packages/svelte/src/lib/create-uploadthing.ts index b282599f17..6ad843ec6e 100644 --- a/packages/svelte/src/lib/create-uploadthing.ts +++ b/packages/svelte/src/lib/create-uploadthing.ts @@ -1,14 +1,18 @@ import { derived, readonly, writable } from "svelte/store"; -import type { EndpointMetadata } from "@uploadthing/shared"; +import type { EndpointMetadata, FetchEsque } from "@uploadthing/shared"; import { INTERNAL_DO_NOT_USE__fatalClientError, resolveMaybeUrlArg, unwrap, UploadAbortedError, UploadThingError, + warnIfInvalidPeerDependency, } from "@uploadthing/shared"; -import { genUploader } from "uploadthing/client"; +import { + genUploader, + version as uploadthingClientVersion, +} from "uploadthing/client"; import type { FileRouter } from "uploadthing/server"; import type { EndpointArg, @@ -16,6 +20,7 @@ import type { inferErrorShape, } from "uploadthing/types"; +import { peerDependencies } from "../../package.json"; import type { GenerateTypedHelpersOptions, UploadthingComponentProps, @@ -23,121 +28,115 @@ import type { } from "./types"; import { createFetch } from "./utils/createFetch"; -const createRouteConfig = (url: URL, endpoint: string) => { - const dataGetter = createFetch(url.href); +const createRouteConfig = (fetch: FetchEsque, url: URL, endpoint: string) => { + const dataGetter = createFetch(fetch, url.href); return derived( dataGetter, ($data) => $data.data?.find((x) => x.slug === endpoint)?.config, ); }; -export const INTERNAL_createUploadThingGen = < +export function __createUploadThingInternal< TRouter extends FileRouter, ->(initOpts: { - /** - * URL to the UploadThing API endpoint - * @example URL { http://localhost:3000/api/uploadthing } - * @example URL { https://www.example.com/api/uploadthing } - */ - url: URL; -}) => { + TEndpoint extends keyof TRouter, +>( + url: URL, + endpoint: EndpointArg, + fetch: FetchEsque, + opts?: UseUploadthingProps, +) { const { uploadFiles, routeRegistry } = genUploader({ - url: initOpts.url, + fetch, + url, package: "@uploadthing/svelte", }); - const useUploadThing = ( - endpoint: EndpointArg, - opts?: UseUploadthingProps, - ) => { - const isUploading = writable(false); - let uploadProgress = 0; - let fileProgress = new Map(); - - type InferredInput = inferEndpointInput; - type FuncInput = undefined extends InferredInput - ? [files: File[], input?: undefined] - : [files: File[], input: InferredInput]; - - const startUpload = async (...args: FuncInput) => { - const files = (await opts?.onBeforeUploadBegin?.(args[0])) ?? args[0]; - const input = args[1]; - - isUploading.set(true); - opts?.onUploadProgress?.(0); - files.forEach((f) => fileProgress.set(f, 0)); - try { - const res = await uploadFiles(endpoint, { - signal: opts?.signal, - headers: opts?.headers, - files, - onUploadProgress: (progress) => { - if (!opts?.onUploadProgress) return; - fileProgress.set(progress.file, progress.progress); - let sum = 0; - fileProgress.forEach((p) => { - sum += p; - }); - const averageProgress = - Math.floor(sum / fileProgress.size / 10) * 10; - if (averageProgress !== uploadProgress) { - opts?.onUploadProgress?.(averageProgress); - uploadProgress = averageProgress; - } - }, - onUploadBegin({ file }) { - if (!opts?.onUploadBegin) return; - - opts.onUploadBegin(file); - }, - // @ts-expect-error - input may not be defined on the type - input, - }); - - await opts?.onClientUploadComplete?.(res); - return res; - } catch (e) { - /** - * This is the only way to introduce this as a non-breaking change - * TODO: Consider refactoring API in the next major version - */ - if (e instanceof UploadAbortedError) throw e; - - let error: UploadThingError>; - if (e instanceof UploadThingError) { - error = e as UploadThingError>; - } else { - error = INTERNAL_DO_NOT_USE__fatalClientError(e as Error); - console.error( - "Something went wrong. Please contact UploadThing and provide the following cause:", - error.cause instanceof Error ? error.cause.toString() : error.cause, - ); - } - await opts?.onUploadError?.(error); - } finally { - isUploading.set(false); - fileProgress = new Map(); - uploadProgress = 0; - } - }; - - const _endpoint = unwrap(endpoint, routeRegistry); - const routeConfig = createRouteConfig(initOpts.url, _endpoint as string); - - return { - startUpload, - isUploading: readonly(isUploading), - routeConfig, + const isUploading = writable(false); + let uploadProgress = 0; + let fileProgress = new Map(); + + type InferredInput = inferEndpointInput; + type FuncInput = undefined extends InferredInput + ? [files: File[], input?: undefined] + : [files: File[], input: InferredInput]; + + const startUpload = async (...args: FuncInput) => { + const files = (await opts?.onBeforeUploadBegin?.(args[0])) ?? args[0]; + const input = args[1]; + + isUploading.set(true); + opts?.onUploadProgress?.(0); + files.forEach((f) => fileProgress.set(f, 0)); + try { + const res = await uploadFiles(endpoint, { + signal: opts?.signal, + headers: opts?.headers, + files, + onUploadProgress: (progress) => { + if (!opts?.onUploadProgress) return; + fileProgress.set(progress.file, progress.progress); + let sum = 0; + fileProgress.forEach((p) => { + sum += p; + }); + const averageProgress = Math.floor(sum / fileProgress.size / 10) * 10; + if (averageProgress !== uploadProgress) { + opts?.onUploadProgress?.(averageProgress); + uploadProgress = averageProgress; + } + }, + onUploadBegin({ file }) { + if (!opts?.onUploadBegin) return; + + opts.onUploadBegin(file); + }, + // @ts-expect-error - input may not be defined on the type + input, + }); + + await opts?.onClientUploadComplete?.(res); + return res; + } catch (e) { /** - * @deprecated Use `routeConfig` instead + * This is the only way to introduce this as a non-breaking change + * TODO: Consider refactoring API in the next major version */ - permittedFileInfo: routeConfig - ? { slug: _endpoint, config: readonly(routeConfig) } - : undefined, - } as const; + if (e instanceof UploadAbortedError) throw e; + + let error: UploadThingError>; + if (e instanceof UploadThingError) { + error = e as UploadThingError>; + } else { + error = INTERNAL_DO_NOT_USE__fatalClientError(e as Error); + // eslint-disable-next-line no-console + console.error( + "Something went wrong. Please contact UploadThing and provide the following cause:", + error.cause instanceof Error ? error.cause.toString() : error.cause, + ); + } + await opts?.onUploadError?.(error); + } finally { + isUploading.set(false); + fileProgress = new Map(); + uploadProgress = 0; + } }; - return useUploadThing; -}; + + const _endpoint = unwrap(endpoint, routeRegistry); + const routeConfig = createRouteConfig(fetch, url, _endpoint as string); + + return { + startUpload, + isUploading: readonly(isUploading), + routeConfig, + /** + * @deprecated Use `routeConfig` instead + */ + permittedFileInfo: routeConfig + ? { slug: _endpoint, config: readonly(routeConfig) } + : undefined, + } as const; +} const generateUploader = () => { return ( @@ -149,14 +148,33 @@ const generateUploader = () => { export const generateSvelteHelpers = ( initOpts?: GenerateTypedHelpersOptions, ) => { + warnIfInvalidPeerDependency( + "@uploadthing/svelte", + peerDependencies.uploadthing, + uploadthingClientVersion, + ); + const url = resolveMaybeUrlArg(initOpts?.url); + const fetch = initOpts?.fetch ?? globalThis.fetch; + + const clientHelpers = genUploader({ + fetch, + url, + package: "@uploadthing/svelte", + }); + + const createUploadThing = ( + endpoint: EndpointArg, + props: UploadthingComponentProps, + ) => { + return __createUploadThingInternal(url, endpoint, fetch, props); + }; + + const createUploader = generateUploader(); return { - createUploadThing: INTERNAL_createUploadThingGen({ url }), - createUploader: generateUploader(), - ...genUploader({ - url, - package: "@uploadthing/svelte", - }), + createUploadThing, + createUploader, + ...clientHelpers, } as const; }; diff --git a/packages/svelte/src/lib/types.ts b/packages/svelte/src/lib/types.ts index 76b84febff..3844946038 100644 --- a/packages/svelte/src/lib/types.ts +++ b/packages/svelte/src/lib/types.ts @@ -2,6 +2,7 @@ import type { ClassListMerger, ErrorMessage, ExtendObjectIf, + FetchEsque, MaybePromise, UploadThingError, } from "@uploadthing/shared"; @@ -26,6 +27,25 @@ export interface GenerateTypedHelpersOptions { * @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing" */ url?: string | URL; + /** + * Provide a custom fetch implementation. + * @default `globalThis.fetch` + * @example + * ```ts + * fetch: (input, init) => { + * if (input.toString().startsWith(MY_SERVER_URL)) { + * // Include cookies in the request to your API + * return fetch(input, { + * ...init, + * credentials: "include", + * }); + * } + * + * return fetch(input, init); + * } + * ``` + */ + fetch?: FetchEsque | undefined; } export type UseUploadthingProps< @@ -122,6 +142,25 @@ export type UploadthingComponentProps< * @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing" */ url?: string | URL; + /** + * Provide a custom fetch implementation. + * @default `globalThis.fetch` + * @example + * ```ts + * fetch: (input, init) => { + * if (input.toString().startsWith(MY_SERVER_URL)) { + * // Include cookies in the request to your API + * return fetch(input, { + * ...init, + * credentials: "include", + * }); + * } + * + * return fetch(input, init); + * } + * ``` + */ + fetch?: FetchEsque | undefined; config?: { mode?: "auto" | "manual"; appendOnPaste?: boolean; diff --git a/packages/svelte/src/lib/utils/createFetch.ts b/packages/svelte/src/lib/utils/createFetch.ts index 765842f75f..9b2e7bc5e1 100644 --- a/packages/svelte/src/lib/utils/createFetch.ts +++ b/packages/svelte/src/lib/utils/createFetch.ts @@ -1,6 +1,8 @@ import { onMount } from "svelte"; import { readonly, writable } from "svelte/store"; +import type { FetchEsque } from "@uploadthing/shared"; + interface State { data?: T; error?: Error; @@ -9,7 +11,11 @@ interface State { type Cache = Record; -export function createFetch(url?: string, options?: RequestInit) { +export function createFetch( + fetchFn: FetchEsque, + url?: string, + options?: RequestInit, +) { const cache: Cache = {}; const initialState: State = { type: "loading", @@ -27,7 +33,7 @@ export function createFetch(url?: string, options?: RequestInit) { return store.set({ type: "fetched", data: cache[url] }); } try { - const response = await fetch(url, options); + const response = await fetchFn(url, options); if (!response.ok) { throw new Error(response.statusText); } diff --git a/packages/uploadthing/package.json b/packages/uploadthing/package.json index 003630e253..78cd22df07 100644 --- a/packages/uploadthing/package.json +++ b/packages/uploadthing/package.json @@ -151,11 +151,11 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@effect/platform": "0.70.7", + "@effect/platform": "0.72.0", "@standard-schema/spec": "1.0.0-beta.3", "@uploadthing/mime-types": "workspace:*", "@uploadthing/shared": "workspace:*", - "effect": "3.11.5" + "effect": "3.12.0" }, "devDependencies": { "@remix-run/server-runtime": "^2.12.0", diff --git a/packages/uploadthing/src/client.ts b/packages/uploadthing/src/client.ts index c9b1de5b9f..37a8571c7e 100644 --- a/packages/uploadthing/src/client.ts +++ b/packages/uploadthing/src/client.ts @@ -1,7 +1,7 @@ import * as Arr from "effect/Array"; import * as Micro from "effect/Micro"; -import type { ExpandedRouteConfig } from "@uploadthing/shared"; +import type { ExpandedRouteConfig, FetchEsque } from "@uploadthing/shared"; import { createIdentityProxy, FetchContext, @@ -112,6 +112,7 @@ export const genUploader = ( url: resolveMaybeUrlArg(initOpts?.url), headers: opts.headers, }); + const fetchFn: FetchEsque = initOpts.fetch ?? window.fetch; const presigneds = await Micro.runPromise( utReporter("upload", { @@ -123,7 +124,7 @@ export const genUploader = ( type: f.type, lastModified: f.lastModified, })), - }).pipe(Micro.provideService(FetchContext, window.fetch)), + }).pipe(Micro.provideService(FetchContext, fetchFn)), ); const totalSize = opts.files.reduce((acc, f) => acc + f.size, 0); @@ -141,7 +142,7 @@ export const genUploader = ( totalProgress: Math.round((totalLoaded / totalSize) * 100), }); }, - }).pipe(Micro.provideService(FetchContext, window.fetch)); + }).pipe(Micro.provideService(FetchContext, fetchFn)); for (const [i, p] of presigneds.entries()) { const file = opts.files[i]; @@ -254,6 +255,7 @@ export const genUploader = ( >, ) => { const endpoint = typeof slug === "function" ? slug(routeRegistry) : slug; + const fetchFn: FetchEsque = initOpts.fetch ?? window.fetch; return uploadFilesInternal(endpoint, { ...opts, @@ -263,7 +265,7 @@ export const genUploader = ( // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access input: (opts as any).input as inferEndpointInput, }) - .pipe((effect) => + .pipe(Micro.provideService(FetchContext, fetchFn), (effect) => Micro.runPromiseExit(effect, opts.signal && { signal: opts.signal }), ) .then((exit) => { diff --git a/packages/uploadthing/src/internal/upload.browser.ts b/packages/uploadthing/src/internal/upload.browser.ts index 1d9d53f083..dc11850da6 100644 --- a/packages/uploadthing/src/internal/upload.browser.ts +++ b/packages/uploadthing/src/internal/upload.browser.ts @@ -1,8 +1,8 @@ import { unsafeCoerce } from "effect/Function"; import * as Micro from "effect/Micro"; -import type { FetchError } from "@uploadthing/shared"; -import { FetchContext, fetchEff, UploadThingError } from "@uploadthing/shared"; +import type { FetchContext, FetchError } from "@uploadthing/shared"; +import { fetchEff, UploadThingError } from "@uploadthing/shared"; import { version } from "../../package.json"; import type { @@ -119,7 +119,8 @@ export const uploadFilesInternal = < opts: UploadFilesOptions, ): Micro.Micro< ClientUploadedFileData[], - UploadThingError | FetchError + UploadThingError | FetchError, + FetchContext > => { // classic service right here const reportEventToUT = createUTReporter({ @@ -132,16 +133,17 @@ export const uploadFilesInternal = < const totalSize = opts.files.reduce((acc, f) => acc + f.size, 0); let totalLoaded = 0; - return reportEventToUT("upload", { - input: "input" in opts ? opts.input : null, - files: opts.files.map((f) => ({ - name: f.name, - size: f.size, - type: f.type, - lastModified: f.lastModified, - })), - }).pipe( - Micro.flatMap((presigneds) => + return Micro.flatMap( + reportEventToUT("upload", { + input: "input" in opts ? opts.input : null, + files: opts.files.map((f) => ({ + name: f.name, + size: f.size, + type: f.type, + lastModified: f.lastModified, + })), + }), + (presigneds) => Micro.forEach( presigneds, (presigned, i) => @@ -174,7 +176,5 @@ export const uploadFilesInternal = < ), { concurrency: 6 }, ), - ), - Micro.provideService(FetchContext, window.fetch), ); }; diff --git a/packages/uploadthing/src/types.ts b/packages/uploadthing/src/types.ts index e1f01f8468..12b6171c5d 100644 --- a/packages/uploadthing/src/types.ts +++ b/packages/uploadthing/src/types.ts @@ -207,6 +207,25 @@ export type GenerateUploaderOptions = { * @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing" */ url?: string | URL; + /** + * Provide a custom fetch implementation. + * @default `globalThis.fetch` + * @example + * ```ts + * fetch: (input, init) => { + * if (input.toString().startsWith(MY_SERVER_URL)) { + * // Include cookies in the request to your API + * return fetch(input, { + * ...init, + * credentials: "include", + * }); + * } + * + * return fetch(input, init); + * } + * ``` + */ + fetch?: FetchEsque | undefined; /** * The uploadthing package that is making this request * @example "@uploadthing/react" diff --git a/packages/uploadthing/test/__test-helpers.ts b/packages/uploadthing/test/__test-helpers.ts index 99f5ef0e68..d2bd375ca6 100644 --- a/packages/uploadthing/test/__test-helpers.ts +++ b/packages/uploadthing/test/__test-helpers.ts @@ -1,10 +1,8 @@ -import { createHash } from "crypto"; import * as Redacted from "effect/Redacted"; import * as S from "effect/Schema"; import type { StrictRequest } from "msw"; import { http, HttpResponse } from "msw"; -import { setupServer } from "msw/node"; -import { afterAll, beforeAll, it as itBase, vi } from "vitest"; +import { vi } from "vitest"; import { UPLOADTHING_VERSION } from "../src/internal/config"; import { ParsedToken, UploadThingToken } from "../src/internal/shared-schemas"; @@ -30,13 +28,17 @@ export const testToken = { }; export const API_URL = - process.env.UPLOADTHING_API_URL ?? "https://api.uploadthing.com"; -export const UTFS_IO_URL = process.env.UPLOADTHING_API_URL - ? "https://staging.utfs.io" - : "https://utfs.io"; -export const INGEST_URL = process.env.UPLOADTHING_API_URL - ? "https://fra1.ingest.ut-staging.com" - : "https://fra1.ingest.uploadthing.com"; + typeof process !== "undefined" && process.env.UPLOADTHING_API_URL + ? process.env.UPLOADTHING_API_URL + : "https://api.uploadthing.com"; +export const UTFS_IO_URL = + typeof process !== "undefined" && process.env.UPLOADTHING_API_URL + ? "https://staging.utfs.io" + : "https://utfs.io"; +export const INGEST_URL = + typeof process !== "undefined" && process.env.UPLOADTHING_API_URL + ? "https://fra1.ingest.ut-staging.com" + : "https://fra1.ingest.uploadthing.com"; export const fileUrlPattern = new RegExp(`^${UTFS_IO_URL}/f/.+$`); export const appUrlPattern = (appId = testToken.decoded.appId) => @@ -76,92 +78,47 @@ const callRequestSpy = async (request: StrictRequest) => })(), }); -export const msw = setupServer(); -beforeAll(() => { - msw.listen({ onUnhandledRequest: "bypass" }); -}); -afterAll(() => msw.close()); - -/** - * Extend the base `it` function to provide a `db` instance to our tests - * and extend the MSW handlers to mock the UploadThing API - * - * NOTE:: Tests **must** destruct the `db` instance from the test context for it to be used - * @example it("should do something", ({ db }) => { ... }) - */ -export const it = itBase.extend({ - // eslint-disable-next-line no-empty-pattern - db: async ({}, use) => { - const files: any[] = []; - const db = { - files, - insertFile: (file: any) => files.push(file), - getFileByKey: (key: string) => files.find((f) => f.key === key), - }; - msw.use( - /** - * Static Assets - */ - http.get("https://cdn.foo.com/:fileKey", async ({ request }) => { - await callRequestSpy(request); - return HttpResponse.text("Lorem ipsum doler sit amet"); - }), - http.get(`${UTFS_IO_URL}/f/:key`, async ({ request }) => { - await callRequestSpy(request); - return HttpResponse.text("Lorem ipsum doler sit amet"); - }), - /** - * UploadThing Ingest - */ - http.all<{ key: string }>( - `${INGEST_URL}/:key`, - async ({ request, params }) => { - await callRequestSpy(request); - const appId = new URLSearchParams(request.url).get("x-ut-identifier"); - return HttpResponse.json({ - url: `${UTFS_IO_URL}/f/${params.key}`, - appUrl: `${UTFS_IO_URL}/a/${appId}/${params.key}`, - serverData: null, - fileHash: createHash("md5") - .update(new Uint8Array(await request.arrayBuffer())) - .digest("hex"), - }); - }, - ), - /** - * UploadThing API - */ - http.post(`${API_URL}/v6/requestFileAccess`, async ({ request }) => { - await callRequestSpy(request); - return HttpResponse.json({ - url: `${UTFS_IO_URL}/f/someFileKey?x-some-amz=query-param`, - }); - }), - http.post(`${API_URL}/v6/updateACL`, async ({ request }) => { - await callRequestSpy(request); - return HttpResponse.json({ success: true }); - }), - ); - await use(db); // provide test context - files.length = 0; // clear files after each test - }, -}); - -/** - * Call this in your test to make the ingest request fail - */ -export const useBadIngestServer = () => - msw.use( - http.put(`${INGEST_URL}/f/:key`, async ({ request, params }) => { +export const handlers = [ + /** + * Static Assets + */ + http.get("https://cdn.foo.com/:fileKey", async ({ request }) => { + await callRequestSpy(request); + return HttpResponse.text("Lorem ipsum doler sit amet"); + }), + http.get(`${UTFS_IO_URL}/f/:key`, async ({ request }) => { + await callRequestSpy(request); + return HttpResponse.text("Lorem ipsum doler sit amet"); + }), + /** + * UploadThing Ingest + */ + http.all<{ key: string }>( + `${INGEST_URL}/:key`, + async ({ request, params }) => { await callRequestSpy(request); - - return new HttpResponse(null, { status: 403 }); - }), - ); - -export const useBadUTApi = () => - msw.use( - http.post(`${API_URL}/*`, async () => { - return HttpResponse.json({ error: "Not found" }, { status: 404 }); - }), - ); + const appId = new URLSearchParams(request.url).get("x-ut-identifier"); + return HttpResponse.json({ + url: `${UTFS_IO_URL}/f/${params.key}`, + appUrl: `${UTFS_IO_URL}/a/${appId}/${params.key}`, + serverData: null, + fileHash: Array.from(new Uint8Array(await request.arrayBuffer())) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""), + }); + }, + ), + /** + * UploadThing API + */ + http.post(`${API_URL}/v6/requestFileAccess`, async ({ request }) => { + await callRequestSpy(request); + return HttpResponse.json({ + url: `${UTFS_IO_URL}/f/someFileKey?x-some-amz=query-param`, + }); + }), + http.post(`${API_URL}/v6/updateACL`, async ({ request }) => { + await callRequestSpy(request); + return HttpResponse.json({ success: true }); + }), +]; diff --git a/packages/uploadthing/test/adapters.test.ts b/packages/uploadthing/test/adapters.test.ts index 46bd4cbd52..dc0c8216b6 100644 --- a/packages/uploadthing/test/adapters.test.ts +++ b/packages/uploadthing/test/adapters.test.ts @@ -3,7 +3,6 @@ import type { NextApiRequest, NextApiResponse } from "next"; import { NextRequest } from "next/server"; import { FetchHttpClient, - HttpClient, HttpServerRequest, HttpServerResponse, } from "@effect/platform"; @@ -13,14 +12,22 @@ import * as Layer from "effect/Layer"; import * as express from "express"; import * as fastify from "fastify"; import { createApp, H3Event, toWebHandler } from "h3"; -import { describe, expect, expectTypeOf, vi } from "vitest"; +import { setupServer } from "msw/node"; +import { + afterAll, + beforeAll, + describe, + expect, + expectTypeOf, + it, + vi, +} from "vitest"; -import { configProvider } from "../src/internal/config"; import { baseHeaders, createApiUrl, + handlers, INGEST_URL, - it, middlewareMock, requestSpy, requestsToDomain, @@ -28,6 +35,10 @@ import { uploadCompleteMock, } from "./__test-helpers"; +const server = setupServer(...handlers); +beforeAll(() => server.listen({ onUnhandledRequest: "bypass" })); +afterAll(() => server.close()); + describe("adapters:h3", async () => { const { createUploadthing, createRouteHandler } = await import("../src/h3"); const f = createUploadthing(); @@ -46,7 +57,7 @@ describe("adapters:h3", async () => { .onUploadComplete(uploadCompleteMock), }; - it("returns router config on GET requests", async ({ db }) => { + it("returns router config on GET requests", async () => { const eventHandler = createRouteHandler({ router, config: { token: testToken.encoded }, @@ -68,7 +79,7 @@ describe("adapters:h3", async () => { ]); }); - it("gets H3Event in middleware args", async ({ db }) => { + it("gets H3Event in middleware args", async () => { const eventHandler = createRouteHandler({ router, config: { token: testToken.encoded }, @@ -157,7 +168,7 @@ describe("adapters:server", async () => { .onUploadComplete(uploadCompleteMock), }; - it("returns router config on GET requests", async ({ db }) => { + it("returns router config on GET requests", async () => { const eventHandler = createRouteHandler({ router, config: { token: testToken.encoded }, @@ -177,7 +188,7 @@ describe("adapters:server", async () => { ]); }); - it("gets Request in middleware args", async ({ db }) => { + it("gets Request in middleware args", async () => { const handler = createRouteHandler({ router, config: { token: testToken.encoded }, @@ -235,7 +246,7 @@ describe("adapters:server", async () => { }); }); - it("accepts object with request in", async ({ db }) => { + it("accepts object with request in", async () => { const handler = createRouteHandler({ router, config: { token: testToken.encoded }, @@ -312,7 +323,7 @@ describe("adapters:next", async () => { .onUploadComplete(uploadCompleteMock), }; - it("returns router config on GET requests", async ({ db }) => { + it("returns router config on GET requests", async () => { const eventHandler = createRouteHandler({ router, config: { token: testToken.encoded }, @@ -334,7 +345,7 @@ describe("adapters:next", async () => { ]); }); - it("gets NextRequest in middleware args", async ({ db }) => { + it("gets NextRequest in middleware args", async () => { const handlers = createRouteHandler({ router, config: { token: testToken.encoded }, @@ -447,7 +458,7 @@ describe("adapters:next-legacy", async () => { return { res, json, setHeader, status }; } - it("returns router config on GET requests", async ({ db }) => { + it("returns router config on GET requests", async () => { const eventHandler = createRouteHandler({ router, config: { token: testToken.encoded }, @@ -471,9 +482,7 @@ describe("adapters:next-legacy", async () => { ]); }); - it("gets NextApiRequest and NextApiResponse in middleware args", async ({ - db, - }) => { + it("gets NextApiRequest and NextApiResponse in middleware args", async () => { const handler = createRouteHandler({ router, config: { token: testToken.encoded }, @@ -569,11 +578,11 @@ describe("adapters:express", async () => { const server = app.listen(); const url = `http://localhost:${(server.address() as { port: number }).port}`; - return { url, close: () => server.close() }; + return { url, [Symbol.dispose]: () => server.close() }; }; - it("returns router config on GET requests", async ({ db }) => { - const server = startServer(); + it("returns router config on GET requests", async () => { + using server = startServer(); const url = `${server.url}/api/uploadthing/`; const res = await fetch(url); @@ -590,10 +599,8 @@ describe("adapters:express", async () => { ]); }); - it("gets express.Request and express.Response in middleware args", async ({ - db, - }) => { - const server = startServer(); + it("gets express.Request and express.Response in middleware args", async () => { + using server = startServer(); const url = `${server.url}/api/uploadthing/`; const res = await fetch(`${url}?slug=middleware&actionType=upload`, { @@ -648,12 +655,10 @@ describe("adapters:express", async () => { }), method: "POST", }); - - server.close(); }); - it("works with some standard built-in middlewares", async ({ db }) => { - const server = startServer((app) => { + it("works with some standard built-in middlewares", async () => { + using server = startServer((app) => { app.use(express.json()); app.use(express.urlencoded({ extended: true })); }); @@ -668,13 +673,11 @@ describe("adapters:express", async () => { }); expect(res.status).toBe(200); expect(middlewareMock).toHaveBeenCalledOnce(); - - server.close(); }); - it("works with body-parser middleware", async ({ db }) => { + it("works with body-parser middleware", async () => { const bodyParser = await import("body-parser"); - const server = startServer((app) => { + using server = startServer((app) => { app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); }); @@ -689,8 +692,6 @@ describe("adapters:express", async () => { }); expect(res.status).toBe(200); expect(middlewareMock).toHaveBeenCalledOnce(); - - server.close(); }); }); @@ -725,11 +726,11 @@ describe("adapters:fastify", async () => { const port = addr.split(":").pop(); const url = `http://localhost:${port}/`; - return { url, close: () => app.close() }; + return { url, [Symbol.asyncDispose]: () => app.close() }; }; - it("returns router config on GET requests", async ({ db }) => { - const server = await startServer(); + it("returns router config on GET requests", async () => { + await using server = await startServer(); const url = `${server.url}api/uploadthing`; const res = await fetch(url); @@ -746,10 +747,8 @@ describe("adapters:fastify", async () => { ]); }); - it("gets fastify.FastifyRequest and fastify.FastifyReply in middleware args", async ({ - db, - }) => { - const server = await startServer(); + it("gets fastify.FastifyRequest and fastify.FastifyReply in middleware args", async () => { + await using server = await startServer(); const url = `${server.url}api/uploadthing`; const res = await fetch(`${url}?slug=middleware&actionType=upload`, { @@ -804,8 +803,6 @@ describe("adapters:fastify", async () => { }), method: "POST", }); - - await server.close(); }); }); diff --git a/packages/uploadthing/test/client.test.ts b/packages/uploadthing/test/client.browser.test.ts similarity index 66% rename from packages/uploadthing/test/client.test.ts rename to packages/uploadthing/test/client.browser.test.ts index 748a876f17..df6dec63e6 100644 --- a/packages/uploadthing/test/client.test.ts +++ b/packages/uploadthing/test/client.browser.test.ts @@ -1,26 +1,40 @@ -// @vitest-environment happy-dom - -import type { AddressInfo } from "node:net"; -import express from "express"; -import { describe, expect, expectTypeOf, it as rawIt, vi } from "vitest"; +import { http } from "msw"; +import { setupWorker } from "msw/browser"; +import { + afterAll, + beforeAll, + describe, + expect, + expectTypeOf, + it, + vi, +} from "vitest"; import { genUploader } from "../src/client"; -import { createRouteHandler, createUploadthing } from "../src/express"; -import type { ClientUploadedFileData } from "../src/types"; +import { createRouteHandler, createUploadthing } from "../src/server"; +import type { + ClientUploadedFileData, + GenerateUploaderOptions, +} from "../src/types"; import { appUrlPattern, doNotExecute, fileUrlPattern, - it, + handlers, middlewareMock, onErrorMock, requestSpy, testToken, uploadCompleteMock, - UTFS_IO_URL, } from "./__test-helpers"; -export const setupUTServer = async () => { +const worker = setupWorker(...handlers); +beforeAll(() => worker.start({ quiet: true, onUnhandledRequest: "bypass" })); +afterAll(() => worker.stop()); + +export const setupUTServer = async ( + clientOpts?: Partial, +) => { const f = createUploadthing({ errorFormatter(err) { return { message: err.message }; @@ -71,58 +85,53 @@ export const setupUTServer = async () => { }), }; - const app = express(); - app.use( - "/api/uploadthing", - createRouteHandler({ - router, - config: { - token: testToken.encoded, - isDev: true, - }, - }), - ); + const handler = createRouteHandler({ + router, + config: { + token: testToken.encoded, + isDev: true, + }, + }); - const server = app.listen(0); - const port = (server.address() as AddressInfo).port; - await new Promise((res) => server.once("listening", res)); + const port = Math.floor(Math.random() * 10000) + 10000; + const url = `http://localhost:${port}`; + worker.use(...handlers, http.all(`${url}/api/uploadthing`, handler)); const { uploadFiles } = genUploader({ package: "vitest", - url: `http://localhost:${port}`, + url, + ...clientOpts, }); return { uploadFiles, - close: () => new Promise((res) => server.close(res)), + [Symbol.dispose]: () => { + // Restore to original handlers + worker.use(...handlers); + }, }; }; -rawIt( - "propagates awaitServerData config on server to the client `serverData` property", - async () => { - const { uploadFiles, close } = await setupUTServer(); - const file = new File(["foo"], "foo.txt", { type: "text/plain" }); - - doNotExecute(async () => { - const res1 = await uploadFiles("withServerData", { files: [file] }); - expectTypeOf[]>(res1); +it("propagates awaitServerData config on server to the client `serverData` property", async () => { + using $ = await setupUTServer(); + const file = new File(["foo"], "foo.txt", { type: "text/plain" }); - const res2 = await uploadFiles("noServerData", { files: [file] }); - expectTypeOf[]>(res2); - }); + doNotExecute(async () => { + const res1 = await $.uploadFiles("withServerData", { files: [file] }); + expectTypeOf[]>(res1); - await close(); - }, -); + const res2 = await $.uploadFiles("noServerData", { files: [file] }); + expectTypeOf[]>(res2); + }); +}); describe("uploadFiles", () => { - it("uploads file using presigned PUT", async ({ db }) => { - const { uploadFiles, close } = await setupUTServer(); + it("uploads file using presigned PUT", async () => { + using $ = await setupUTServer(); const file = new File(["foo"], "foo.txt", { type: "text/plain" }); await expect( - uploadFiles((routeRegistry) => routeRegistry.foo, { files: [file] }), + $.uploadFiles((routeRegistry) => routeRegistry.foo, { files: [file] }), ).resolves.toEqual([ { name: "foo.txt", @@ -154,16 +163,14 @@ describe("uploadFiles", () => { // await vi.waitUntil(() => uploadCompleteMock.mock.calls.length > 0, { // timeout: 5000, // }); - - await close(); }); - it("sends custom headers if set (static object)", async ({ db }) => { - const { uploadFiles, close } = await setupUTServer(); + it("sends custom headers if set (static object)", async () => { + using $ = await setupUTServer(); const file = new File(["foo"], "foo.txt", { type: "text/plain" }); await expect( - uploadFiles((routeRegistry) => routeRegistry.foo, { + $.uploadFiles((routeRegistry) => routeRegistry.foo, { files: [file], headers: { authorization: "Bearer my-auth-token", @@ -184,21 +191,21 @@ describe("uploadFiles", () => { }, ]); - expect(middlewareMock.mock.calls[0][0].req.headers).toMatchObject({ + expect( + Object.fromEntries(middlewareMock.mock.calls[0][0].req.headers), + ).toMatchObject({ authorization: "Bearer my-auth-token", "x-uploadthing-package": "vitest", "x-uploadthing-version": expect.stringMatching(/\d+\.\d+\.\d+/), }); - - await close(); }); - it("sends custom headers if set (async function)", async ({ db }) => { - const { uploadFiles, close } = await setupUTServer(); + it("sends custom headers if set (async function)", async () => { + using $ = await setupUTServer(); const file = new File(["foo"], "foo.txt", { type: "text/plain" }); await expect( - uploadFiles("foo", { + $.uploadFiles("foo", { files: [file], headers: async () => ({ authorization: "Bearer my-auth-token", @@ -219,25 +226,41 @@ describe("uploadFiles", () => { }, ]); - expect(middlewareMock.mock.calls[0][0].req.headers).toMatchObject({ + expect( + Object.fromEntries(middlewareMock.mock.calls[0][0].req.headers), + ).toMatchObject({ authorization: "Bearer my-auth-token", "x-uploadthing-package": "vitest", "x-uploadthing-version": expect.stringMatching(/\d+\.\d+\.\d+/), }); + }); + + it("uses custom fetch implementation if set", async () => { + const fetchFn = vi.fn(); + + using $ = await setupUTServer({ + fetch: (input, init) => { + fetchFn(input, init); + return window.fetch(input, init); + }, + }); + + const file = new File(["foo"], "foo.txt", { type: "text/plain" }); + await $.uploadFiles("foo", { files: [file] }); - await close(); + expect(fetchFn).toHaveBeenCalled(); }); // Should we retry? - // it("succeeds after retries", { timeout: 15e3 }, async ({ db }) => { - // const { uploadFiles, close } = await setupUTServer(); + // it("succeeds after retries", { timeout: 15e3 }, async () => { + // using $ = await setupUTServer(); // useHalfBadS3(); // const bigFile = new File([new ArrayBuffer(10 * 1024 * 1024)], "foo.txt", { // type: "text/plain", // }); - // await expect(uploadFiles("foo", { files: [bigFile] })).resolves.toEqual([ + // await expect($.uploadFiles("foo", { files: [bigFile] })).resolves.toEqual([ // { // name: "foo.txt", // size: 10485760, @@ -252,12 +275,10 @@ describe("uploadFiles", () => { // await vi.waitUntil(() => uploadCompleteMock.mock.calls.length > 0, { // timeout: 5000, // }); - - // await close(); // }); - it("handles too big file size errors", async ({ db }) => { - const { uploadFiles, close } = await setupUTServer(); + it("handles too big file size errors", async () => { + using $ = await setupUTServer(); const tooBigFile = new File( [new ArrayBuffer(20 * 1024 * 1024)], @@ -268,74 +289,64 @@ describe("uploadFiles", () => { ); await expect( - uploadFiles("foo", { files: [tooBigFile] }), + $.uploadFiles("foo", { files: [tooBigFile] }), ).rejects.toThrowErrorMatchingInlineSnapshot( `[UploadThingError: Invalid config: FileSizeMismatch]`, ); - - await close(); }); - it("handles too many files errors", async ({ db }) => { - const { uploadFiles, close } = await setupUTServer(); + it("handles too many files errors", async () => { + using $ = await setupUTServer(); const file1 = new File(["foo"], "foo.txt", { type: "text/plain" }); const file2 = new File(["bar"], "bar.txt", { type: "text/plain" }); await expect( - uploadFiles("foo", { files: [file1, file2] }), + $.uploadFiles("foo", { files: [file1, file2] }), ).rejects.toThrowErrorMatchingInlineSnapshot( `[UploadThingError: Invalid config: FileCountMismatch]`, ); - - await close(); }); - it("handles invalid file type errors", async ({ db }) => { - const { uploadFiles, close } = await setupUTServer(); + it("handles invalid file type errors", async () => { + using $ = await setupUTServer(); const file = new File(["foo"], "foo.png", { type: "image/png" }); await expect( - uploadFiles("foo", { files: [file] }), + $.uploadFiles("foo", { files: [file] }), ).rejects.toThrowErrorMatchingInlineSnapshot( `[UploadThingError: Invalid config: InvalidFileType]`, ); - - await close(); }); it("runs onUploadBegin before uploading (single file)", async () => { - const { uploadFiles, close } = await setupUTServer(); + using $ = await setupUTServer(); const file = new File(["foo"], "foo.txt", { type: "text/plain" }); const onUploadBegin = vi.fn(); - await uploadFiles("foo", { + await $.uploadFiles("foo", { files: [file], onUploadBegin, }); expect(onUploadBegin).toHaveBeenCalledWith({ file: "foo.txt" }); - - await close(); }); it("runs onUploadBegin before uploading (multi file)", async () => { - const { uploadFiles, close } = await setupUTServer(); + using $ = await setupUTServer(); const file1 = new File(["foo"], "foo.txt", { type: "text/plain" }); const file2 = new File(["bar"], "bar.txt", { type: "text/plain" }); const onUploadBegin = vi.fn(); - await uploadFiles("multi", { + await $.uploadFiles("multi", { files: [file1, file2], onUploadBegin, }); expect(onUploadBegin).toHaveBeenCalledWith({ file: "foo.txt" }); expect(onUploadBegin).toHaveBeenCalledWith({ file: "bar.txt" }); - - await close(); }); }); diff --git a/packages/uploadthing/test/request-handler.test.ts b/packages/uploadthing/test/request-handler.test.ts index 45bcef7b43..c993077275 100644 --- a/packages/uploadthing/test/request-handler.test.ts +++ b/packages/uploadthing/test/request-handler.test.ts @@ -1,6 +1,6 @@ import * as Effect from "effect/Effect"; import * as Redacted from "effect/Redacted"; -import { describe, expect } from "vitest"; +import { describe, expect, it } from "vitest"; import { z } from "zod"; import { signPayload } from "@uploadthing/shared"; @@ -10,7 +10,6 @@ import { createRouteHandler, createUploadthing } from "../src/server"; import { baseHeaders, createApiUrl, - it, middlewareMock, requestSpy, testToken, @@ -78,7 +77,7 @@ const handler = createRouteHandler({ }); describe("errors for invalid request input", () => { - it("404s for invalid slugs", async ({ db }) => { + it("404s for invalid slugs", async () => { const res = await handler( new Request(createApiUrl("i-dont-exist", "upload"), { method: "POST", @@ -96,7 +95,7 @@ describe("errors for invalid request input", () => { }); }); - it("400s for invalid action type", async ({ db }) => { + it("400s for invalid action type", async () => { const res = await handler( // @ts-expect-error - invalid is not a valid action type new Request(createApiUrl("imageUploader", "invalid"), { @@ -118,7 +117,7 @@ describe("errors for invalid request input", () => { }); describe("file route config", () => { - it("blocks unmatched file types", async ({ db }) => { + it("blocks unmatched file types", async () => { const res = await handler( new Request(createApiUrl("imageUploader", "upload"), { method: "POST", @@ -137,7 +136,7 @@ describe("file route config", () => { }); }); - it("blocks for too big files", async ({ db }) => { + it("blocks for too big files", async () => { const res = await handler( new Request(createApiUrl("imageUploader", "upload"), { method: "POST", @@ -159,7 +158,7 @@ describe("file route config", () => { }); }); - it("blocks for too many files", async ({ db }) => { + it("blocks for too many files", async () => { const res = await handler( new Request(createApiUrl("imageUploader", "upload"), { method: "POST", @@ -182,7 +181,7 @@ describe("file route config", () => { }); }); - it("blocks for too few files", async ({ db }) => { + it("blocks for too few files", async () => { const res = await handler( new Request(createApiUrl("withMinInput", "upload"), { method: "POST", @@ -202,7 +201,7 @@ describe("file route config", () => { }); }); - it("uses file type to match mime type with router config", async ({ db }) => { + it("uses file type to match mime type with router config", async () => { const res = await handler( new Request(createApiUrl("imageUploader", "upload"), { method: "POST", @@ -228,7 +227,7 @@ describe("file route config", () => { ]); }); - it("prefers file.type over file.name extension", async ({ db }) => { + it("prefers file.type over file.name extension", async () => { const res = await handler( new Request(createApiUrl("imageUploader", "upload"), { method: "POST", @@ -254,9 +253,7 @@ describe("file route config", () => { ]); }); - it("falls back to filename lookup type when there's no recognized mime type", async ({ - db, - }) => { + it("falls back to filename lookup type when there's no recognized mime type", async () => { const res = await handler( new Request(createApiUrl("imageUploader", "upload"), { method: "POST", @@ -285,7 +282,7 @@ describe("file route config", () => { }); describe(".input()", () => { - it("blocks when input is missing", async ({ db }) => { + it("blocks when input is missing", async () => { const res = await handler( new Request(createApiUrl("withInput", "upload"), { method: "POST", @@ -308,7 +305,7 @@ describe(".input()", () => { }); }); - it("blocks when input doesn't match schema", async ({ db }) => { + it("blocks when input doesn't match schema", async () => { const res = await handler( new Request(createApiUrl("withInput", "upload"), { method: "POST", @@ -334,7 +331,7 @@ describe(".input()", () => { }); }); - it("forwards input to middleware", async ({ db }) => { + it("forwards input to middleware", async () => { const res = await handler( new Request(createApiUrl("withInput", "upload"), { method: "POST", @@ -356,7 +353,7 @@ describe(".input()", () => { }); describe(".middleware()", () => { - it("forwards files to middleware", async ({ db }) => { + it("forwards files to middleware", async () => { const res = await handler( new Request(createApiUrl("imageUploader", "upload"), { method: "POST", @@ -377,7 +374,7 @@ describe(".middleware()", () => { expect(res.status).toBe(200); }); - it("early exits if middleware throws", async ({ db }) => { + it("early exits if middleware throws", async () => { const res = await handler( new Request(createApiUrl("middlewareThrows", "upload"), { method: "POST", @@ -407,7 +404,7 @@ describe(".middleware()", () => { }); describe(".onUploadComplete()", () => { - it("forwards correct args to onUploadComplete handler", async ({ db }) => { + it("forwards correct args to onUploadComplete handler", async () => { const payload = JSON.stringify({ status: "uploaded", metadata: {}, @@ -457,7 +454,7 @@ describe(".onUploadComplete()", () => { }); }); - it("is blocked on missing signature", async ({ db }) => { + it("is blocked on missing signature", async () => { const payload = JSON.stringify({ status: "uploaded", metadata: {}, @@ -490,7 +487,7 @@ describe(".onUploadComplete()", () => { expect(uploadCompleteMock).not.toHaveBeenCalled(); }); - it("is blocked on invalid signature", async ({ db }) => { + it("is blocked on invalid signature", async () => { const payload = JSON.stringify({ status: "uploaded", metadata: {}, diff --git a/packages/uploadthing/test/sdk.live.test.ts b/packages/uploadthing/test/sdk.live.test.ts new file mode 100644 index 0000000000..483fc624c8 --- /dev/null +++ b/packages/uploadthing/test/sdk.live.test.ts @@ -0,0 +1,296 @@ +/* eslint-disable no-restricted-globals */ +import * as S from "effect/Schema"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +import { UTApi, UTFile } from "../src/sdk"; +import { UploadThingToken } from "../src/types"; +import { + appUrlPattern, + fileUrlPattern, + testToken, + UTFS_IO_URL, +} from "./__test-helpers"; + +const shouldRun = + typeof process.env.UPLOADTHING_TEST_TOKEN === "string" && + process.env.UPLOADTHING_TEST_TOKEN.length > 0; + +describe.runIf(shouldRun)( + "smoke test with live api", + { timeout: 15_000 }, + () => { + const token = shouldRun + ? process.env.UPLOADTHING_TEST_TOKEN! + : testToken.encoded; + const utapi = new UTApi({ token }); + + const appId = S.decodeSync(UploadThingToken)(token).appId; + + const localInfo = { totalBytes: 0, filesUploaded: 0 }; + const TEST_APP_LIMIT_BYTES = 2147483648; // free 2GB + // const TEST_APP_LIMIT_BYTES = 107374182400; // paid 100GB + + // Clean up any files before and after tests + beforeAll(async () => { + const { files } = await utapi.listFiles(); + await utapi.deleteFiles(files.map((f) => f.key)); + }); + afterAll(async () => { + const { files } = await utapi.listFiles(); + await utapi.deleteFiles(files.map((f) => f.key)); + }); + + // These will all run in serial + + it("should have no files", async () => { + const { files, hasMore } = await utapi.listFiles(); + expect(files).toHaveLength(0); + expect(hasMore).toBe(false); + + const usageInfo = await utapi.getUsageInfo(); + expect(usageInfo).toEqual({ + totalBytes: 0, + appTotalBytes: 0, + filesUploaded: 0, + limitBytes: TEST_APP_LIMIT_BYTES, + }); + localInfo.totalBytes = usageInfo.totalBytes; + localInfo.filesUploaded = usageInfo.filesUploaded; + }); + + it("should upload a file", async () => { + const file = new File(["foo"], "foo.txt", { type: "text/plain" }); + const result = await utapi.uploadFiles(file); + const key = result.data!.key; + expect(result).toEqual({ + data: { + customId: null, + key: expect.stringMatching(/.+/), + lastModified: file.lastModified, + name: "foo.txt", + size: 3, + type: "text/plain", + url: expect.stringMatching(fileUrlPattern), + appUrl: expect.stringMatching(appUrlPattern(appId)), + fileHash: expect.any(String), + }, + error: null, + }); + + const content = await fetch(result.data!.url).then((r) => r.text()); + expect(content).toBe("foo"); + + const usageInfo = await utapi.getUsageInfo(); + expect(usageInfo).toEqual({ + totalBytes: 3, + appTotalBytes: 3, + filesUploaded: 1, + limitBytes: TEST_APP_LIMIT_BYTES, + }); + + localInfo.totalBytes += result.data!.size; + localInfo.filesUploaded++; + }); + + it("should upload a private file", async () => { + const file = new File(["foo"], "foo.txt", { type: "text/plain" }); + const result = await utapi.uploadFiles(file, { + acl: "private", + }); + const key = result.data!.key; + expect(result).toEqual({ + data: { + customId: null, + key: expect.stringMatching(/.+/), + lastModified: file.lastModified, + name: "foo.txt", + size: 3, + type: "text/plain", + url: expect.stringMatching(fileUrlPattern), + appUrl: expect.stringMatching(appUrlPattern(appId)), + fileHash: expect.any(String), + }, + error: null, + }); + + const response = await fetch(result.data!.url); + expect(response.status).toBe(403); + + const { url } = await utapi.getSignedURL(result.data!.key); + const content = await fetch(url).then((r) => r.text()); + expect(content).toBe("foo"); + + localInfo.totalBytes += result.data!.size; + localInfo.filesUploaded++; + }); + + it("should upload a file from a url", async () => { + const result = await utapi.uploadFilesFromUrl( + "https://uploadthing.com/favicon.ico", + ); + const key = result.data!.key; + expect(result).toEqual({ + data: { + customId: null, + key: expect.stringMatching(/.+/), + lastModified: expect.any(Number), + name: "favicon.ico", + size: expect.any(Number), + type: "image/vnd.microsoft.icon", + url: expect.stringMatching(fileUrlPattern), + appUrl: expect.stringMatching(appUrlPattern(appId)), + fileHash: expect.any(String), + }, + error: null, + }); + + localInfo.totalBytes += result.data!.size; + localInfo.filesUploaded++; + }); + + it("should rename a file with fileKey", async () => { + const customId = crypto.randomUUID(); + + const file = new UTFile(["foo"], "bar.txt"); + const result = await utapi.uploadFiles(file); + const fileKey = result.data!.key; + expect(result).toEqual({ + data: { + customId: null, + key: expect.stringMatching(/.+/), + lastModified: expect.any(Number), + + name: "bar.txt", + size: 3, + type: "text/plain", + url: expect.stringMatching(fileUrlPattern), + appUrl: expect.stringMatching(appUrlPattern(appId)), + fileHash: expect.any(String), + }, + error: null, + }); + + const { success } = await utapi.renameFiles({ + fileKey, + newName: "baz.txt", + }); + expect(success).toBe(true); + + const { files } = await utapi.listFiles(); + expect(files.find((f) => f.key === fileKey)).toHaveProperty( + "name", + "baz.txt", + ); + + // FIXME: Bug in uploadthing server + // const heads = await fetch(result.data!.url).then((r) => r.headers); + // expect(heads.get("Content-Disposition")).toEqual( + // expect.stringContaining("filename=baz.txt"), + // ); + + localInfo.totalBytes += result.data!.size; + localInfo.filesUploaded++; + }); + + it("should rename a file with customId", async () => { + const customId = crypto.randomUUID(); + + const file = new UTFile(["foo"], "bar.txt", { customId }); + const result = await utapi.uploadFiles(file); + const key = result.data!.key; + expect(result).toEqual({ + data: { + customId: customId, + key: expect.stringMatching(/.+/), + lastModified: file.lastModified, + name: "bar.txt", + size: 3, + type: "text/plain", + url: expect.stringMatching(fileUrlPattern), + appUrl: expect.stringMatching(appUrlPattern(appId)), + fileHash: expect.any(String), + }, + error: null, + }); + + const { success } = await utapi.renameFiles({ + customId, + newName: "baz.txt", + }); + expect(success).toBe(true); + + const { files } = await utapi.listFiles(); + expect(files.find((f) => f.customId === customId)).toHaveProperty( + "name", + "baz.txt", + ); + + // FIXME: Bug in uploadthing server + // const heads = await fetch(result.data!.url).then((r) => r.headers); + // expect(heads.get("Content-Disposition")).toEqual( + // expect.stringContaining("filename=baz.txt"), + // ); + + localInfo.totalBytes += result.data!.size; + localInfo.filesUploaded++; + }); + + it("should update ACL", async () => { + const file = new File(["foo"], "foo.txt", { type: "text/plain" }); + const result = await utapi.uploadFiles(file); + const { key, url } = result.data!; + expect(result).toEqual({ + data: { + customId: null, + key: expect.stringMatching(/.+/), + lastModified: file.lastModified, + name: "foo.txt", + size: 3, + type: "text/plain", + url: expect.stringMatching(fileUrlPattern), + appUrl: expect.stringMatching(appUrlPattern(appId)), + fileHash: expect.any(String), + }, + error: null, + }); + + const firstChange = await utapi.updateACL(key, "private"); + expect(firstChange.success).toBe(true); + await expect(fetch(url)).resolves.toHaveProperty("status", 403); + + const secondChange = await utapi.updateACL(key, "public-read"); + expect(secondChange.success).toBe(true); + await expect(fetch(url)).resolves.toHaveProperty("status", 200); + + localInfo.totalBytes += result.data!.size; + localInfo.filesUploaded++; + }); + + it("should delete a file", async () => { + const { files } = await utapi.listFiles(); + const someFile = files[0]; + + const response = await fetch(`${UTFS_IO_URL}/f/${someFile.key}`); + const size = Number(response.headers.get("Content-Length")); + + const result = await utapi.deleteFiles(someFile.key); + expect(result).toEqual({ + deletedCount: 1, + success: true, + }); + + localInfo.totalBytes -= size; + localInfo.filesUploaded--; + }); + + it("should have correct usage info", async () => { + const usageInfo = await utapi.getUsageInfo(); + expect(usageInfo).toEqual({ + totalBytes: localInfo.totalBytes, + appTotalBytes: localInfo.totalBytes, + filesUploaded: localInfo.filesUploaded, + limitBytes: TEST_APP_LIMIT_BYTES, + }); + }); + }, +); diff --git a/packages/uploadthing/test/sdk.test.ts b/packages/uploadthing/test/sdk.test.ts index 29ba51f2c4..13a024cbde 100644 --- a/packages/uploadthing/test/sdk.test.ts +++ b/packages/uploadthing/test/sdk.test.ts @@ -1,34 +1,33 @@ -/* eslint-disable no-restricted-globals */ -import * as S from "effect/Schema"; import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; import { afterAll, beforeAll, describe, expect, expectTypeOf, - it as rawIt, + it, } from "vitest"; -import { UploadThingToken } from "../src/internal/shared-schemas"; import { UTApi, UTFile } from "../src/sdk"; import type { UploadFileResult } from "../src/sdk/types"; import { API_URL, - appUrlPattern, - fileUrlPattern, + handlers, INGEST_URL, - it, - msw, requestSpy, testToken, UTFS_IO_URL, } from "./__test-helpers"; +const msw = setupServer(...handlers); +beforeAll(() => msw.listen()); +afterAll(() => msw.close()); + describe("uploadFiles", () => { const fooFile = new File(["foo"], "foo.txt", { type: "text/plain" }); - it("uploads successfully", async ({ db }) => { + it("uploads successfully", async () => { const utapi = new UTApi({ token: testToken.encoded }); const result = await utapi.uploadFiles(fooFile); @@ -61,21 +60,21 @@ describe("uploadFiles", () => { }); }); - it("returns array if array is passed", async ({ db }) => { + it("returns array if array is passed", async () => { const utapi = new UTApi({ token: testToken.encoded }); const result = await utapi.uploadFiles([fooFile]); expectTypeOf(result); expect(Array.isArray(result)).toBe(true); }); - it("returns single object if no array is passed", async ({ db }) => { + it("returns single object if no array is passed", async () => { const utapi = new UTApi({ token: testToken.encoded }); const result = await utapi.uploadFiles(fooFile); expectTypeOf(result); expect(Array.isArray(result)).toBe(false); }); - it("accepts UTFile", async ({ db }) => { + it("accepts UTFile", async () => { const utapi = new UTApi({ token: testToken.encoded }); const file = new UTFile(["foo"], "foo.txt"); await utapi.uploadFiles(file); @@ -87,14 +86,14 @@ describe("uploadFiles", () => { ); }); - it("accepts UndiciFile", async ({ db }) => { + it("accepts UndiciFile", async () => { const utapi = new UTApi({ token: testToken.encoded }); const { File } = await import("undici"); const file = new File(["foo"], "foo.txt"); await utapi.uploadFiles(file); }); - it("accepts UTFile with customId", async ({ db }) => { + it("accepts UTFile with customId", async () => { const utapi = new UTApi({ token: testToken.encoded }); const fileWithId = new UTFile(["foo"], "foo.txt", { customId: "foo" }); await utapi.uploadFiles(fileWithId); @@ -108,7 +107,7 @@ describe("uploadFiles", () => { }); describe("uploadFilesFromUrl", () => { - it("downloads, then uploads successfully", async ({ db }) => { + it("downloads, then uploads successfully", async () => { const utapi = new UTApi({ token: testToken.encoded }); const result = await utapi.uploadFilesFromUrl( "https://cdn.foo.com/foo.txt", @@ -144,7 +143,7 @@ describe("uploadFilesFromUrl", () => { }); }); - it("returns array if array is passed", async ({ db }) => { + it("returns array if array is passed", async () => { const utapi = new UTApi({ token: testToken.encoded }); const result = await utapi.uploadFilesFromUrl([ "https://cdn.foo.com/foo.txt", @@ -154,7 +153,7 @@ describe("uploadFilesFromUrl", () => { expect(Array.isArray(result)).toBe(true); }); - it("returns single object if no array is passed", async ({ db }) => { + it("returns single object if no array is passed", async () => { const utapi = new UTApi({ token: testToken.encoded }); const result = await utapi.uploadFilesFromUrl( "https://cdn.foo.com/foo.txt", @@ -163,7 +162,7 @@ describe("uploadFilesFromUrl", () => { expect(Array.isArray(result)).toBe(false); }); - it("can override name", async ({ db }) => { + it("can override name", async () => { const utapi = new UTApi({ token: testToken.encoded }); await utapi.uploadFilesFromUrl({ url: "https://cdn.foo.com/my-super-long-pathname-thats-too-long-for-ut.txt", @@ -182,7 +181,7 @@ describe("uploadFilesFromUrl", () => { ); }); - it("can provide a customId", async ({ db }) => { + it("can provide a customId", async () => { const utapi = new UTApi({ token: testToken.encoded }); await utapi.uploadFilesFromUrl({ url: "https://cdn.foo.com/foo.txt", @@ -202,7 +201,7 @@ describe("uploadFilesFromUrl", () => { }); // if passed data url, array contains UploadThingError - it("returns error if data url is passed", async ({ db }) => { + it("returns error if data url is passed", async () => { const utapi = new UTApi({ token: testToken.encoded }); const result = await utapi.uploadFilesFromUrl( "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==", @@ -218,7 +217,7 @@ describe("uploadFilesFromUrl", () => { }); }); - it("gracefully handles download errors", async ({ db }) => { + it("gracefully handles download errors", async () => { const utapi = new UTApi({ token: testToken.encoded }); msw.use( @@ -244,7 +243,7 @@ describe("uploadFilesFromUrl", () => { }); }); - it("gracefully handles download errors", async ({ db }) => { + it("gracefully handles download errors", async () => { const utapi = new UTApi({ token: testToken.encoded }); msw.use( @@ -288,7 +287,7 @@ describe("uploadFilesFromUrl", () => { ]); }); - it("preserves order if some download fails", async ({ db }) => { + it("preserves order if some download fails", async () => { const utapi = new UTApi({ token: testToken.encoded }); const result = await utapi.uploadFilesFromUrl([ "https://cdn.foo.com/foo.txt", @@ -340,7 +339,7 @@ describe("uploadFilesFromUrl", () => { }); describe("getSignedURL", () => { - it("sends request without expiresIn", async ({ db }) => { + it("sends request without expiresIn", async () => { const utapi = new UTApi({ token: testToken.encoded }); await utapi.getSignedURL("foo"); @@ -357,7 +356,7 @@ describe("getSignedURL", () => { }); }); - it("sends request with valid expiresIn (1)", async ({ db }) => { + it("sends request with valid expiresIn (1)", async () => { const utapi = new UTApi({ token: testToken.encoded }); await utapi.getSignedURL("foo", { expiresIn: "1d" }); @@ -374,7 +373,7 @@ describe("getSignedURL", () => { }); }); - it("sends request with valid expiresIn (2)", async ({ db }) => { + it("sends request with valid expiresIn (2)", async () => { const utapi = new UTApi({ token: testToken.encoded }); await utapi.getSignedURL("foo", { expiresIn: "3 minutes" }); @@ -391,7 +390,7 @@ describe("getSignedURL", () => { }); }); - it("throws if expiresIn is invalid", async ({ db }) => { + it("throws if expiresIn is invalid", async () => { const utapi = new UTApi({ token: testToken.encoded }); await expect(() => // @ts-expect-error - intentionally passing invalid expiresIn @@ -402,7 +401,7 @@ describe("getSignedURL", () => { expect(requestSpy).toHaveBeenCalledTimes(0); }); - it("throws if expiresIn is longer than 7 days", async ({ db }) => { + it("throws if expiresIn is longer than 7 days", async () => { const utapi = new UTApi({ token: testToken.encoded }); await expect(() => utapi.getSignedURL("foo", { expiresIn: "10 days" }), @@ -414,7 +413,7 @@ describe("getSignedURL", () => { }); describe("updateACL", () => { - it("single file", async ({ db }) => { + it("single file", async () => { const utapi = new UTApi({ token: testToken.encoded }); await expect(utapi.updateACL("ut-key", "public-read")).resolves.toEqual({ @@ -433,7 +432,7 @@ describe("updateACL", () => { }); }); - it("many keys", async ({ db }) => { + it("many keys", async () => { const utapi = new UTApi({ token: testToken.encoded }); await expect( @@ -457,7 +456,7 @@ describe("updateACL", () => { }); }); - it("many keys with keytype override", async ({ db }) => { + it("many keys with keytype override", async () => { const utapi = new UTApi({ token: testToken.encoded }); await expect( @@ -483,290 +482,3 @@ describe("updateACL", () => { }); }); }); - -const shouldRun = - typeof process.env.UPLOADTHING_TEST_TOKEN === "string" && - process.env.UPLOADTHING_TEST_TOKEN.length > 0; - -describe.runIf(shouldRun)( - "smoke test with live api", - { timeout: 15_000 }, - () => { - const token = shouldRun - ? process.env.UPLOADTHING_TEST_TOKEN! - : testToken.encoded; - const utapi = new UTApi({ token }); - - const appId = S.decodeSync(UploadThingToken)(token).appId; - - const localInfo = { totalBytes: 0, filesUploaded: 0 }; - const TEST_APP_LIMIT_BYTES = 2147483648; // free 2GB - // const TEST_APP_LIMIT_BYTES = 107374182400; // paid 100GB - - // Clean up any files before and after tests - beforeAll(async () => { - // Close MSW proxy, don't want to interfere with these live tests - msw.close(); - - const { files } = await utapi.listFiles(); - await utapi.deleteFiles(files.map((f) => f.key)); - }); - afterAll(async () => { - const { files } = await utapi.listFiles(); - await utapi.deleteFiles(files.map((f) => f.key)); - }); - - // These will all run in serial - - rawIt("should have no files", async () => { - const { files, hasMore } = await utapi.listFiles(); - expect(files).toHaveLength(0); - expect(hasMore).toBe(false); - - const usageInfo = await utapi.getUsageInfo(); - expect(usageInfo).toEqual({ - totalBytes: 0, - appTotalBytes: 0, - filesUploaded: 0, - limitBytes: TEST_APP_LIMIT_BYTES, - }); - localInfo.totalBytes = usageInfo.totalBytes; - localInfo.filesUploaded = usageInfo.filesUploaded; - }); - - rawIt("should upload a file", async () => { - const file = new File(["foo"], "foo.txt", { type: "text/plain" }); - const result = await utapi.uploadFiles(file); - const key = result.data!.key; - expect(result).toEqual({ - data: { - customId: null, - key: expect.stringMatching(/.+/), - lastModified: file.lastModified, - name: "foo.txt", - size: 3, - type: "text/plain", - url: expect.stringMatching(fileUrlPattern), - appUrl: expect.stringMatching(appUrlPattern(appId)), - fileHash: expect.any(String), - }, - error: null, - }); - - const content = await fetch(result.data!.url).then((r) => r.text()); - expect(content).toBe("foo"); - - const usageInfo = await utapi.getUsageInfo(); - expect(usageInfo).toEqual({ - totalBytes: 3, - appTotalBytes: 3, - filesUploaded: 1, - limitBytes: TEST_APP_LIMIT_BYTES, - }); - - localInfo.totalBytes += result.data!.size; - localInfo.filesUploaded++; - }); - - rawIt("should upload a private file", async () => { - const file = new File(["foo"], "foo.txt", { type: "text/plain" }); - const result = await utapi.uploadFiles(file, { - acl: "private", - }); - const key = result.data!.key; - expect(result).toEqual({ - data: { - customId: null, - key: expect.stringMatching(/.+/), - lastModified: file.lastModified, - name: "foo.txt", - size: 3, - type: "text/plain", - url: expect.stringMatching(fileUrlPattern), - appUrl: expect.stringMatching(appUrlPattern(appId)), - fileHash: expect.any(String), - }, - error: null, - }); - - const response = await fetch(result.data!.url); - expect(response.status).toBe(403); - - const { url } = await utapi.getSignedURL(result.data!.key); - const content = await fetch(url).then((r) => r.text()); - expect(content).toBe("foo"); - - localInfo.totalBytes += result.data!.size; - localInfo.filesUploaded++; - }); - - rawIt("should upload a file from a url", async () => { - const result = await utapi.uploadFilesFromUrl( - "https://uploadthing.com/favicon.ico", - ); - const key = result.data!.key; - expect(result).toEqual({ - data: { - customId: null, - key: expect.stringMatching(/.+/), - lastModified: expect.any(Number), - name: "favicon.ico", - size: expect.any(Number), - type: "image/vnd.microsoft.icon", - url: expect.stringMatching(fileUrlPattern), - appUrl: expect.stringMatching(appUrlPattern(appId)), - fileHash: expect.any(String), - }, - error: null, - }); - - localInfo.totalBytes += result.data!.size; - localInfo.filesUploaded++; - }); - - rawIt("should rename a file with fileKey", async () => { - const customId = crypto.randomUUID(); - - const file = new UTFile(["foo"], "bar.txt"); - const result = await utapi.uploadFiles(file); - const fileKey = result.data!.key; - expect(result).toEqual({ - data: { - customId: null, - key: expect.stringMatching(/.+/), - lastModified: expect.any(Number), - - name: "bar.txt", - size: 3, - type: "text/plain", - url: expect.stringMatching(fileUrlPattern), - appUrl: expect.stringMatching(appUrlPattern(appId)), - fileHash: expect.any(String), - }, - error: null, - }); - - const { success } = await utapi.renameFiles({ - fileKey, - newName: "baz.txt", - }); - expect(success).toBe(true); - - const { files } = await utapi.listFiles(); - expect(files.find((f) => f.key === fileKey)).toHaveProperty( - "name", - "baz.txt", - ); - - // FIXME: Bug in uploadthing server - // const heads = await fetch(result.data!.url).then((r) => r.headers); - // expect(heads.get("Content-Disposition")).toEqual( - // expect.stringContaining("filename=baz.txt"), - // ); - - localInfo.totalBytes += result.data!.size; - localInfo.filesUploaded++; - }); - - rawIt("should rename a file with customId", async () => { - const customId = crypto.randomUUID(); - - const file = new UTFile(["foo"], "bar.txt", { customId }); - const result = await utapi.uploadFiles(file); - const key = result.data!.key; - expect(result).toEqual({ - data: { - customId: customId, - key: expect.stringMatching(/.+/), - lastModified: file.lastModified, - name: "bar.txt", - size: 3, - type: "text/plain", - url: expect.stringMatching(fileUrlPattern), - appUrl: expect.stringMatching(appUrlPattern(appId)), - fileHash: expect.any(String), - }, - error: null, - }); - - const { success } = await utapi.renameFiles({ - customId, - newName: "baz.txt", - }); - expect(success).toBe(true); - - const { files } = await utapi.listFiles(); - expect(files.find((f) => f.customId === customId)).toHaveProperty( - "name", - "baz.txt", - ); - - // FIXME: Bug in uploadthing server - // const heads = await fetch(result.data!.url).then((r) => r.headers); - // expect(heads.get("Content-Disposition")).toEqual( - // expect.stringContaining("filename=baz.txt"), - // ); - - localInfo.totalBytes += result.data!.size; - localInfo.filesUploaded++; - }); - - rawIt("should update ACL", async () => { - const file = new File(["foo"], "foo.txt", { type: "text/plain" }); - const result = await utapi.uploadFiles(file); - const { key, url } = result.data!; - expect(result).toEqual({ - data: { - customId: null, - key: expect.stringMatching(/.+/), - lastModified: file.lastModified, - name: "foo.txt", - size: 3, - type: "text/plain", - url: expect.stringMatching(fileUrlPattern), - appUrl: expect.stringMatching(appUrlPattern(appId)), - fileHash: expect.any(String), - }, - error: null, - }); - - const firstChange = await utapi.updateACL(key, "private"); - expect(firstChange.success).toBe(true); - await expect(fetch(url)).resolves.toHaveProperty("status", 403); - - const secondChange = await utapi.updateACL(key, "public-read"); - expect(secondChange.success).toBe(true); - await expect(fetch(url)).resolves.toHaveProperty("status", 200); - - localInfo.totalBytes += result.data!.size; - localInfo.filesUploaded++; - }); - - rawIt("should delete a file", async () => { - const { files } = await utapi.listFiles(); - const someFile = files[0]; - - const response = await fetch(`${UTFS_IO_URL}/f/${someFile.key}`); - const size = Number(response.headers.get("Content-Length")); - - const result = await utapi.deleteFiles(someFile.key); - expect(result).toEqual({ - deletedCount: 1, - success: true, - }); - - localInfo.totalBytes -= size; - localInfo.filesUploaded--; - }); - - rawIt("should have correct usage info", async () => { - const usageInfo = await utapi.getUsageInfo(); - expect(usageInfo).toEqual({ - totalBytes: localInfo.totalBytes, - appTotalBytes: localInfo.totalBytes, - filesUploaded: localInfo.filesUploaded, - limitBytes: TEST_APP_LIMIT_BYTES, - }); - }); - }, -); diff --git a/packages/uploadthing/test/upload-builder.test.ts b/packages/uploadthing/test/upload-builder.test.ts index 5164e3bc86..cfb16ca5af 100644 --- a/packages/uploadthing/test/upload-builder.test.ts +++ b/packages/uploadthing/test/upload-builder.test.ts @@ -1,7 +1,4 @@ /* eslint-disable @typescript-eslint/no-empty-function */ - -import * as Schema from "effect/Schema"; -import * as v from "valibot"; import { expect, expectTypeOf, it } from "vitest"; import { z } from "zod"; diff --git a/packages/uploadthing/vitest.config.ts b/packages/uploadthing/vitest.config.ts deleted file mode 100644 index 48b09a3fad..0000000000 --- a/packages/uploadthing/vitest.config.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "../../vitest.config"; diff --git a/packages/vue/package.json b/packages/vue/package.json index b305be2a80..7f9fabed32 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -62,6 +62,14 @@ "eslintConfig": { "root": true, "rules": { + "no-console": "error", + "no-restricted-globals": [ + "error", + { + "name": "fetch", + "message": "fetch should be passed as parameter to support overriding default behaviors" + } + ], "no-restricted-imports": [ "error", { diff --git a/packages/vue/src/components/button.tsx b/packages/vue/src/components/button.tsx index b563db26c7..270d7c8f52 100644 --- a/packages/vue/src/components/button.tsx +++ b/packages/vue/src/components/button.tsx @@ -21,7 +21,7 @@ import type { UploadthingComponentProps, UseUploadthingProps, } from "../types"; -import { INTERNAL_uploadthingHookGen } from "../useUploadThing"; +import { __useUploadThingInternal } from "../useUploadThing"; import { Cancel, progressWidths, Spinner, usePaste } from "./shared"; export type ButtonStyleFieldCallbackArgs = { @@ -67,10 +67,6 @@ export type UploadButtonProps< export const generateUploadButton = ( initOpts?: GenerateTypedHelpersOptions, ) => { - const useUploadThing = INTERNAL_uploadthingHookGen({ - url: resolveMaybeUrlArg(initOpts?.url), - }); - return Vue.defineComponent( (props: { config: UploadButtonProps; @@ -109,10 +105,13 @@ export const generateUploadButton = ( onBeforeUploadBegin: $props.onBeforeUploadBegin, }); - const { startUpload, isUploading, routeConfig } = useUploadThing( - $props.endpoint, - useUploadthingProps, - ); + const { startUpload, isUploading, routeConfig } = + __useUploadThingInternal( + resolveMaybeUrlArg(initOpts?.url), + $props.endpoint, + $props.fetch ?? initOpts?.fetch ?? globalThis.fetch, + useUploadthingProps, + ); const permittedFileTypes = computed(() => generatePermittedFileTypes(routeConfig.value), ); diff --git a/packages/vue/src/components/dropzone.tsx b/packages/vue/src/components/dropzone.tsx index a35ab54f1f..ddc4778c23 100644 --- a/packages/vue/src/components/dropzone.tsx +++ b/packages/vue/src/components/dropzone.tsx @@ -39,7 +39,7 @@ import type { UploadthingComponentProps, UseUploadthingProps, } from "../types"; -import { INTERNAL_uploadthingHookGen } from "../useUploadThing"; +import { __useUploadThingInternal } from "../useUploadThing"; import { Cancel, progressWidths, Spinner, usePaste } from "./shared"; export type DropzoneStyleFieldCallbackArgs = { @@ -95,10 +95,6 @@ export type UploadDropzoneProps< export const generateUploadDropzone = ( initOpts?: GenerateTypedHelpersOptions, ) => { - const useUploadThing = INTERNAL_uploadthingHookGen({ - url: resolveMaybeUrlArg(initOpts?.url), - }); - return Vue.defineComponent( (props: { config: UploadDropzoneProps; @@ -132,10 +128,13 @@ export const generateUploadDropzone = ( onUploadBegin: $props.onUploadBegin, onBeforeUploadBegin: $props.onBeforeUploadBegin, }); - const { startUpload, isUploading, routeConfig } = useUploadThing( - $props.endpoint, - useUploadthingProps, - ); + const { startUpload, isUploading, routeConfig } = + __useUploadThingInternal( + resolveMaybeUrlArg(initOpts?.url), + $props.endpoint, + $props.fetch ?? initOpts?.fetch ?? globalThis.fetch, + useUploadthingProps, + ); const permittedFileTypes = computed(() => generatePermittedFileTypes(routeConfig.value), diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index 4e490711ce..4794075bb2 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -2,6 +2,7 @@ import type { ClassListMerger, ErrorMessage, ExtendObjectIf, + FetchEsque, MaybePromise, UploadThingError, } from "@uploadthing/shared"; @@ -26,6 +27,25 @@ export interface GenerateTypedHelpersOptions { * @default (VERCEL_URL ?? window.location.origin) + "/api/uploadthing" */ url?: string | URL; + /** + * Provide a custom fetch implementation. + * @default `globalThis.fetch` + * @example + * ```ts + * fetch: (input, init) => { + * if (input.toString().startsWith(MY_SERVER_URL)) { + * // Include cookies in the request to your API + * return fetch(input, { + * ...init, + * credentials: "include", + * }); + * } + * + * return fetch(input, init); + * } + * ``` + */ + fetch?: FetchEsque | undefined; } export type UseUploadthingProps< @@ -112,6 +132,25 @@ export type UploadthingComponentProps< * The endpoint from your FileRouter to use for the upload */ endpoint: EndpointArg; + /** + * Provide a custom fetch implementation. + * @default `globalThis.fetch` + * @example + * ```ts + * fetch: (input, init) => { + * if (input.toString().startsWith(MY_SERVER_URL)) { + * // Include cookies in the request to your API + * return fetch(input, { + * ...init, + * credentials: "include", + * }); + * } + * + * return fetch(input, init); + * } + * ``` + */ + fetch?: FetchEsque | undefined; config?: { mode?: "auto" | "manual"; appendOnPaste?: boolean; diff --git a/packages/vue/src/useUploadThing.ts b/packages/vue/src/useUploadThing.ts index b00c800da3..4d6d3888f1 100644 --- a/packages/vue/src/useUploadThing.ts +++ b/packages/vue/src/useUploadThing.ts @@ -1,15 +1,24 @@ import { useFetch } from "@vueuse/core"; +import type { ComputedRef } from "vue"; import { computed, ref } from "vue"; -import type { EndpointMetadata } from "@uploadthing/shared"; +import type { + EndpointMetadata, + ExpandedRouteConfig, + FetchEsque, +} from "@uploadthing/shared"; import { INTERNAL_DO_NOT_USE__fatalClientError, resolveMaybeUrlArg, unwrap, UploadAbortedError, UploadThingError, + warnIfInvalidPeerDependency, } from "@uploadthing/shared"; -import { genUploader } from "uploadthing/client"; +import { + genUploader, + version as uploadthingClientVersion, +} from "uploadthing/client"; import type { EndpointArg, FileRouter, @@ -17,6 +26,7 @@ import type { inferErrorShape, } from "uploadthing/types"; +import { peerDependencies } from "../package.json"; import type { GenerateTypedHelpersOptions, UseUploadthingProps } from "./types"; import { useEvent } from "./utils/useEvent"; @@ -25,138 +35,151 @@ export type { ExpandedRouteConfig, } from "@uploadthing/shared"; -const useRouteConfig = (url: URL, endpoint: string) => { +const useRouteConfig = ( + fetch: FetchEsque, + url: URL, + endpoint: string, +): ComputedRef => { // TODO: useState with server-inserted data to skip fetch on client - const { data } = useFetch(url.href); + const { data } = useFetch(url.href, { + fetch: fetch as never, + }); return computed(() => { if (!data.value) return undefined; const endpointData = typeof data.value === "string" ? (JSON.parse(data.value) as EndpointMetadata) - : (data.value as EndpointMetadata); + : data.value; return endpointData?.find((x) => x.slug === endpoint)?.config; }); }; -export const INTERNAL_uploadthingHookGen = < +export function __useUploadThingInternal< TRouter extends FileRouter, ->(initOpts: { - /** - * URL to the UploadThing API endpoint - * @example URL { http://localhost:3000/api/uploadthing } - * @example URL { https://www.example.com/api/uploadthing } - */ - url: URL; -}) => { + TEndpoint extends keyof TRouter, +>( + url: URL, + endpoint: EndpointArg, + fetch: FetchEsque, + opts?: UseUploadthingProps, +) { const { uploadFiles, routeRegistry } = genUploader({ - url: initOpts.url, + fetch, + url, package: "@uploadthing/vue", }); - const useUploadThing = ( - endpoint: EndpointArg, - opts?: UseUploadthingProps, - ) => { - const isUploading = ref(false); - const uploadProgress = ref(0); - const fileProgress = ref(new Map()); - - type InferredInput = inferEndpointInput; - type FuncInput = undefined extends InferredInput - ? [files: File[], input?: undefined] - : [files: File[], input: InferredInput]; - - const startUpload = useEvent(async (...args: FuncInput) => { - const files = (await opts?.onBeforeUploadBegin?.(args[0])) ?? args[0]; - const input = args[1]; - - isUploading.value = true; - opts?.onUploadProgress?.(0); - files.forEach((f) => fileProgress.value.set(f, 0)); - try { - const res = await uploadFiles(endpoint, { - signal: opts?.signal, - headers: opts?.headers, - files, - onUploadProgress: (progress) => { - if (!opts?.onUploadProgress) return; - fileProgress.value.set(progress.file, progress.progress); - let sum = 0; - fileProgress.value.forEach((p) => { - sum += p; - }); - const averageProgress = - Math.floor(sum / fileProgress.value.size / 10) * 10; - if (averageProgress !== uploadProgress.value) { - opts.onUploadProgress(averageProgress); - uploadProgress.value = averageProgress; - } - }, - onUploadBegin({ file }) { - if (!opts?.onUploadBegin) return; - - opts.onUploadBegin(file); - }, - // @ts-expect-error - input may not be defined on the type - input, - }); - - await opts?.onClientUploadComplete?.(res); - return res; - } catch (e) { - /** - * This is the only way to introduce this as a non-breaking change - * TODO: Consider refactoring API in the next major version - */ - if (e instanceof UploadAbortedError) throw e; - - let error: UploadThingError>; - if (e instanceof UploadThingError) { - error = e as UploadThingError>; - } else { - error = INTERNAL_DO_NOT_USE__fatalClientError(e as Error); - console.error( - "Something went wrong. Please contact UploadThing and provide the following cause:", - error.cause instanceof Error ? error.cause.toString() : error.cause, - ); - } - await opts?.onUploadError?.(error); - } finally { - isUploading.value = false; - fileProgress.value = new Map(); - uploadProgress.value = 0; + const isUploading = ref(false); + const uploadProgress = ref(0); + const fileProgress = ref(new Map()); + + type InferredInput = inferEndpointInput; + type FuncInput = undefined extends InferredInput + ? [files: File[], input?: undefined] + : [files: File[], input: InferredInput]; + + const startUpload = useEvent(async (...args: FuncInput) => { + const files = (await opts?.onBeforeUploadBegin?.(args[0])) ?? args[0]; + const input = args[1]; + + isUploading.value = true; + opts?.onUploadProgress?.(0); + files.forEach((f) => fileProgress.value.set(f, 0)); + try { + const res = await uploadFiles(endpoint, { + signal: opts?.signal, + headers: opts?.headers, + files, + onUploadProgress: (progress) => { + if (!opts?.onUploadProgress) return; + fileProgress.value.set(progress.file, progress.progress); + let sum = 0; + fileProgress.value.forEach((p) => { + sum += p; + }); + const averageProgress = + Math.floor(sum / fileProgress.value.size / 10) * 10; + if (averageProgress !== uploadProgress.value) { + opts.onUploadProgress(averageProgress); + uploadProgress.value = averageProgress; + } + }, + onUploadBegin({ file }) { + if (!opts?.onUploadBegin) return; + + opts.onUploadBegin(file); + }, + // @ts-expect-error - input may not be defined on the type + input, + }); + + await opts?.onClientUploadComplete?.(res); + return res; + } catch (e) { + if (e instanceof UploadAbortedError) throw e; + + let error: UploadThingError>; + if (e instanceof UploadThingError) { + error = e as UploadThingError>; + } else { + error = INTERNAL_DO_NOT_USE__fatalClientError(e as Error); + // eslint-disable-next-line no-console + console.error( + "Something went wrong. Please contact UploadThing and provide the following cause:", + error.cause instanceof Error ? error.cause.toString() : error.cause, + ); } - }); - - const _endpoint = computed(() => unwrap(endpoint, routeRegistry)); - const routeConfig = useRouteConfig(initOpts.url, _endpoint.value as string); - - return { - startUpload, - isUploading, - routeConfig, - /** - * @deprecated Use `routeConfig` instead - */ - permittedFileInfo: routeConfig - ? { slug: _endpoint.value, config: routeConfig } - : undefined, - } as const; - }; - - return useUploadThing; -}; + await opts?.onUploadError?.(error); + } finally { + isUploading.value = false; + fileProgress.value = new Map(); + uploadProgress.value = 0; + } + }); + + const _endpoint = computed(() => unwrap(endpoint, routeRegistry)); + const routeConfig = useRouteConfig(fetch, url, _endpoint.value as string); + + return { + startUpload, + isUploading, + routeConfig, + /** + * @deprecated Use `routeConfig` instead + */ + permittedFileInfo: routeConfig + ? { slug: _endpoint.value, config: routeConfig } + : undefined, + } as const; +} export const generateVueHelpers = ( initOpts?: GenerateTypedHelpersOptions, ) => { + warnIfInvalidPeerDependency( + "@uploadthing/vue", + peerDependencies.uploadthing, + uploadthingClientVersion, + ); + + const fetch = initOpts?.fetch ?? globalThis.fetch; const url = resolveMaybeUrlArg(initOpts?.url); + const clientHelpers = genUploader({ + fetch, + url, + package: "@uploadthing/vue", + }); + + function useUploadThing( + endpoint: EndpointArg, + opts?: UseUploadthingProps, + ) { + return __useUploadThingInternal(url, endpoint, fetch, opts); + } + return { - useUploadThing: INTERNAL_uploadthingHookGen({ url }), - ...genUploader({ - url, - package: "@uploadthing/vue", - }), - }; + useUploadThing, + ...clientHelpers, + } as const; }; diff --git a/patches/@mswjs__interceptors@0.26.15.patch b/patches/@mswjs__interceptors@0.26.15.patch deleted file mode 100644 index 5cc46a5a8a..0000000000 --- a/patches/@mswjs__interceptors@0.26.15.patch +++ /dev/null @@ -1,150 +0,0 @@ -diff --git a/lib/node/interceptors/fetch/index.mjs b/lib/node/interceptors/fetch/index.mjs -index ae8f98cb6c31744f849ecba96416273603867f3d..1efa85de6f91d125fb69cc74600cab7e34c29398 100644 ---- a/lib/node/interceptors/fetch/index.mjs -+++ b/lib/node/interceptors/fetch/index.mjs -@@ -1,17 +1,7 @@ --import { -- isPropertyAccessible --} from "../../chunk-DERTLGL3.mjs"; --import { -- IS_PATCHED_MODULE --} from "../../chunk-HAGW22AN.mjs"; --import { -- emitAsync, -- toInteractiveRequest --} from "../../chunk-OUWBQF3Z.mjs"; --import { -- Interceptor, -- createRequestId --} from "../../chunk-QED3Q6Z2.mjs"; -+import { isPropertyAccessible } from "../../chunk-DERTLGL3.mjs"; -+import { IS_PATCHED_MODULE } from "../../chunk-HAGW22AN.mjs"; -+import { emitAsync, toInteractiveRequest } from "../../chunk-OUWBQF3Z.mjs"; -+import { Interceptor, createRequestId } from "../../chunk-QED3Q6Z2.mjs"; - - // src/interceptors/fetch/index.ts - import { invariant } from "outvariant"; -@@ -34,7 +24,10 @@ var _FetchInterceptor = class extends Interceptor { - super(_FetchInterceptor.symbol); - } - checkEnvironment() { -- return typeof globalThis !== "undefined" && typeof globalThis.fetch !== "undefined"; -+ return ( -+ typeof globalThis !== "undefined" && -+ typeof globalThis.fetch !== "undefined" -+ ); - } - async setup() { - const pureFetch = globalThis.fetch; -@@ -45,10 +38,16 @@ var _FetchInterceptor = class extends Interceptor { - globalThis.fetch = async (input, init) => { - var _a; - const requestId = createRequestId(); -- const resolvedInput = typeof input === "string" && typeof location !== "undefined" && !canParseUrl(input) ? new URL(input, location.origin) : input; -+ const resolvedInput = -+ typeof input === "string" && -+ typeof location !== "undefined" && -+ !canParseUrl(input) -+ ? new URL(input, location.origin) -+ : input; - const request = new Request(resolvedInput, init); - this.logger.info("[%s] %s", request.method, request.url); -- const { interactiveRequest, requestController } = toInteractiveRequest(request); -+ const { interactiveRequest, requestController } = -+ toInteractiveRequest(request); - this.logger.info( - 'emitting the "request" event for %d listener(s)...', - this.emitter.listenerCount("request") -@@ -76,7 +75,7 @@ var _FetchInterceptor = class extends Interceptor { - const resolverResult = await until(async () => { - const listenersFinished = emitAsync(this.emitter, "request", { - request: interactiveRequest, -- requestId -+ requestId, - }); - await Promise.race([ - requestAborted, -@@ -84,7 +83,7 @@ var _FetchInterceptor = class extends Interceptor { - // with the request abort Promise because otherwise awaiting the listeners - // would always yield some response (or undefined). - listenersFinished, -- requestController.responsePromise -+ requestController.responsePromise, - ]); - this.logger.info("all request listeners have been resolved!"); - const mockedResponse2 = await requestController.responsePromise; -@@ -98,9 +97,15 @@ var _FetchInterceptor = class extends Interceptor { - return Promise.reject(createNetworkError(resolverResult.error)); - } - const mockedResponse = resolverResult.data; -- if (mockedResponse && !((_a = request.signal) == null ? void 0 : _a.aborted)) { -+ if ( -+ mockedResponse && -+ !((_a = request.signal) == null ? void 0 : _a.aborted) -+ ) { - this.logger.info("received mocked response:", mockedResponse); -- if (isPropertyAccessible(mockedResponse, "type") && mockedResponse.type === "error") { -+ if ( -+ isPropertyAccessible(mockedResponse, "type") && -+ mockedResponse.type === "error" -+ ) { - this.logger.info( - "received a network error response, rejecting the request promise..." - ); -@@ -111,25 +116,25 @@ var _FetchInterceptor = class extends Interceptor { - response: responseClone, - isMockedResponse: true, - request: interactiveRequest, -- requestId -+ requestId, - }); - Object.defineProperty(mockedResponse, "url", { - writable: false, - enumerable: true, - configurable: false, -- value: request.url -+ value: request.url, - }); - return mockedResponse; - } - this.logger.info("no mocked response received!"); -- return pureFetch(request).then((response) => { -+ return pureFetch(resolvedInput, init).then((response) => { - const responseClone = response.clone(); - this.logger.info("original fetch performed", responseClone); - this.emitter.emit("response", { - response: responseClone, - isMockedResponse: false, - request: interactiveRequest, -- requestId -+ requestId, - }); - return response; - }); -@@ -137,11 +142,11 @@ var _FetchInterceptor = class extends Interceptor { - Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, { - enumerable: true, - configurable: true, -- value: true -+ value: true, - }); - this.subscriptions.push(() => { - Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, { -- value: void 0 -+ value: void 0, - }); - globalThis.fetch = pureFetch; - this.logger.info( -@@ -155,10 +160,8 @@ var FetchInterceptor = _FetchInterceptor; - FetchInterceptor.symbol = Symbol("fetch"); - function createNetworkError(cause) { - return Object.assign(new TypeError("Failed to fetch"), { -- cause -+ cause, - }); - } --export { -- FetchInterceptor --}; -+export { FetchInterceptor }; - //# sourceMappingURL=index.mjs.map diff --git a/patches/msw@2.2.13.patch b/patches/msw@2.2.13.patch deleted file mode 100644 index e9ee81101e..0000000000 --- a/patches/msw@2.2.13.patch +++ /dev/null @@ -1,58 +0,0 @@ -diff --git a/lib/core/handlers/RequestHandler.mjs b/lib/core/handlers/RequestHandler.mjs -index 3c26a001bd980be7505d6fcbbbaf325750869330..a9d28c952a1a450b657cab1f73700da929fe1905 100644 ---- a/lib/core/handlers/RequestHandler.mjs -+++ b/lib/core/handlers/RequestHandler.mjs -@@ -116,27 +116,26 @@ class RequestHandler { - } - wrapResolver(resolver) { - return async (info) => { -- const result = this.resolverGenerator || await resolver(info); -+ const result = this.resolverGenerator || (await resolver(info)); - if (isIterable(result)) { - this.isUsed = false; -- const { value, done } = result[Symbol.iterator]().next(); -- const nextResponse = await value; -- if (done) { -- this.isUsed = true; -- } -- if (!nextResponse && done) { -- invariant( -- this.resolverGeneratorResult, -- "Failed to returned a previously stored generator response: the value is not a valid Response." -- ); -- return this.resolverGeneratorResult.clone(); -- } -+ - if (!this.resolverGenerator) { - this.resolverGenerator = result; - } -+ -+ const { value, done } = await this.resolverGenerator.next() -+ const nextResponse = await value; -+ - if (nextResponse) { -- this.resolverGeneratorResult = nextResponse?.clone(); -+ this.resolverGeneratorResult = nextResponse.clone(); -+ } -+ -+ if (done) { -+ this.isUsed = true; -+ return this.resolverGeneratorResult?.clone(); - } -+ - return nextResponse; - } - return result; -diff --git a/lib/core/utils/internal/isIterable.mjs b/lib/core/utils/internal/isIterable.mjs -index 4e9d6f92080acad602105f50ef10b4cbfb748861..962b60dd9544299b088c011ca14c9f9565e92639 100644 ---- a/lib/core/utils/internal/isIterable.mjs -+++ b/lib/core/utils/internal/isIterable.mjs -@@ -2,7 +2,7 @@ function isIterable(fn) { - if (!fn) { - return false; - } -- return typeof fn[Symbol.iterator] == "function"; -+ return Reflect.has(fn, Symbol.iterator) || Reflect.has(fn, Symbol.asyncIterator); - } - export { - isIterable diff --git a/playground-v6/package.json b/playground-v6/package.json index 56b907c802..a5fa9d6a1f 100644 --- a/playground-v6/package.json +++ b/playground-v6/package.json @@ -11,7 +11,7 @@ "dependencies": { "@uploadthing/react": "npm:@uploadthing/react@6", "clsx": "2.1.1", - "effect": "3.11.5", + "effect": "3.12.0", "next": "canary", "react": "18.3.1", "react-dom": "18.3.1", diff --git a/playground/package.json b/playground/package.json index e58672533c..9420def9e3 100644 --- a/playground/package.json +++ b/playground/package.json @@ -11,7 +11,7 @@ "dependencies": { "@uploadthing/react": "workspace:*", "clsx": "2.1.1", - "effect": "3.11.5", + "effect": "3.12.0", "next": "canary", "react": "18.3.1", "react-dom": "18.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e75becdc0..07e81efab8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,15 +5,9 @@ settings: excludeLinksFromLockfile: false patchedDependencies: - '@mswjs/interceptors@0.26.15': - hash: b4i3mfjisdgapabfrhqgz5r23e - path: patches/@mswjs__interceptors@0.26.15.patch bunchee@6.1.2: hash: zsxtdggzsv4wyktgrmbmvae3om path: patches/bunchee@6.1.2.patch - msw@2.2.13: - hash: mpkjv35lscrawpqthnrnago5ai - path: patches/msw@2.2.13.patch importers: @@ -29,29 +23,17 @@ importers: specifier: ^2.27.1 version: 2.27.1 '@effect/vitest': - specifier: 0.13.15 - version: 0.13.15(effect@3.11.5)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2)(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1)) + specifier: 0.16.0 + version: 0.16.0(effect@3.12.0)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8)(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)) '@ianvs/prettier-plugin-sort-imports': - specifier: ^4.2.1 - version: 4.2.1(@vue/compiler-sfc@3.5.11)(prettier@3.3.3) + specifier: ^4.4.0 + version: 4.4.0(@vue/compiler-sfc@3.5.11)(prettier@3.4.2) '@manypkg/cli': specifier: ^0.21.3 version: 0.21.4 - '@playwright/test': - specifier: 1.45.0 - version: 1.45.0 '@prettier/sync': specifier: ^0.5.2 - version: 0.5.2(prettier@3.3.3) - '@testing-library/dom': - specifier: ^10.4.0 - version: 10.4.0 - '@testing-library/jest-dom': - specifier: ^6.4.8 - version: 6.4.8 - '@testing-library/react': - specifier: ^16.0.0 - version: 16.0.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 0.5.2(prettier@3.4.2) '@types/bun': specifier: ^1.1.5 version: 1.1.5 @@ -61,21 +43,24 @@ importers: '@uploadthing/eslint-config': specifier: workspace:* version: link:tooling/eslint-config + '@vitest/browser': + specifier: ^2.1.8 + version: 2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8) '@vitest/coverage-v8': - specifier: ^2.1.2 - version: 2.1.2(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2)(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1)) - happy-dom: - specifier: ^13.6.2 - version: 13.10.1 + specifier: ^2.1.8 + version: 2.1.8(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8)(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)) msw: - specifier: 2.2.13 - version: 2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2) + specifier: 2.7.0 + version: 2.7.0(@types/node@20.16.11)(typescript@5.6.2) + playwright: + specifier: 1.49.1 + version: 1.49.1 prettier: - specifier: ^3.3.2 - version: 3.3.3 + specifier: ^3.4.2 + version: 3.4.2 prettier-plugin-tailwindcss: - specifier: ^0.6.5 - version: 0.6.5(@ianvs/prettier-plugin-sort-imports@4.2.1(@vue/compiler-sfc@3.5.11)(prettier@3.3.3))(prettier@3.3.3) + specifier: ^0.6.9 + version: 0.6.9(@ianvs/prettier-plugin-sort-imports@4.4.0(@vue/compiler-sfc@3.5.11)(prettier@3.4.2))(prettier@3.4.2) turbo: specifier: 2.3.3 version: 2.3.3 @@ -85,12 +70,9 @@ importers: uploadthing: specifier: workspace:* version: link:packages/uploadthing - vite-tsconfig-paths: - specifier: ^4.3.2 - version: 4.3.2(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1)) vitest: - specifier: ^2.1.2 - version: 2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1) + specifier: ^2.1.8 + version: 2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1) docs: dependencies: @@ -117,7 +99,10 @@ importers: version: 14.2.11(@mdx-js/loader@3.0.1(webpack@5.94.0(esbuild@0.21.5)))(@mdx-js/react@3.0.1(@types/react@18.3.3)(react@18.3.1)) '@scalar/api-reference-react': specifier: ^0.3.37 - version: 0.3.37(@types/bun@1.1.5)(postcss@8.4.49)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1)) + version: 0.3.37(@types/bun@1.1.5)(postcss@8.4.49)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)) + '@shikijs/transformers': + specifier: ^1.17.5 + version: 1.17.5 '@sindresorhus/slugify': specifier: ^2.1.1 version: 2.2.1 @@ -171,16 +156,16 @@ importers: version: 0.1.4 next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-sitemap: specifier: ^4.2.3 - version: 4.2.3(next@14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 4.2.3(next@14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-view-transitions: specifier: ^0.3.0 - version: 0.3.0(next@14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 0.3.0(next@14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) npm-to-yarn: specifier: ^3.0.0 version: 3.0.0 @@ -208,6 +193,9 @@ importers: remark-unwrap-images: specifier: ^4.0.0 version: 4.0.0 + sharp: + specifier: 0.33.1 + version: 0.33.1 shiki: specifier: ^1.17.5 version: 1.17.5 @@ -238,31 +226,12 @@ importers: zustand: specifier: ^4.3.2 version: 4.5.2(@types/react@18.3.3)(react@18.3.1) - devDependencies: - '@shikijs/transformers': - specifier: ^1.17.5 - version: 1.17.5 - eslint: - specifier: ^8.57.0 - version: 8.57.0 - eslint-config-next: - specifier: ^14.2.1 - version: 14.2.2(eslint@8.57.0)(typescript@5.6.2) - prettier: - specifier: ^3.3.2 - version: 3.3.3 - prettier-plugin-tailwindcss: - specifier: ^0.6.5 - version: 0.6.5(@ianvs/prettier-plugin-sort-imports@4.2.1(@vue/compiler-sfc@3.5.11)(prettier@3.3.3))(prettier@3.3.3) - sharp: - specifier: 0.33.1 - version: 0.33.1 examples/backend-adapters: dependencies: '@playwright/test': - specifier: 1.45.0 - version: 1.45.0 + specifier: 1.49.1 + version: 1.49.1 '@uploadthing/react': specifier: 7.1.5 version: link:../../packages/react @@ -348,11 +317,11 @@ importers: examples/backend-adapters/server: dependencies: '@effect/platform': - specifier: 0.70.7 - version: 0.70.7(effect@3.11.5) + specifier: 0.72.0 + version: 0.72.0(effect@3.12.0) '@effect/platform-node': - specifier: 0.65.7 - version: 0.65.7(@effect/platform@0.70.7(effect@3.11.5))(effect@3.11.5) + specifier: 0.68.0 + version: 0.68.0(@effect/platform@0.72.0(effect@3.12.0))(effect@3.12.0) '@elysiajs/cors': specifier: ^1.1.1 version: 1.1.1(elysia@1.1.16(@sinclair/typebox@0.34.3)(openapi-types@12.1.3)(typescript@5.6.2)) @@ -372,8 +341,8 @@ importers: specifier: ^16.4.5 version: 16.4.5 effect: - specifier: 3.11.5 - version: 3.11.5 + specifier: 3.12.0 + version: 3.12.0 elysia: specifier: ^1.1.16 version: 1.1.16(@sinclair/typebox@0.34.3)(openapi-types@12.1.3)(typescript@5.6.2) @@ -422,7 +391,7 @@ importers: version: link:../../packages/react next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -617,7 +586,7 @@ importers: version: link:../../packages/react next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -783,16 +752,16 @@ importers: version: 0.30.10(@cloudflare/workers-types@4.20240620.0)(@libsql/client@0.6.0)(@types/react@18.3.3)(bun-types@1.1.14)(react@18.3.1) geist: specifier: ^1.3.0 - version: 1.3.0(next@14.2.11(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 1.3.0(next@14.2.11(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) lucide-react: specifier: ^0.368.0 version: 0.368.0(react@18.3.1) next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-auth: specifier: 5.0.0-beta.19 - version: 5.0.0-beta.19(next@14.2.11(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 5.0.0-beta.19(next@14.2.11(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -853,7 +822,7 @@ importers: dependencies: '@clerk/nextjs': specifier: ^4.29.8 - version: 4.30.0(next@14.2.11(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 4.30.0(next@14.2.11(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@t3-oss/env-nextjs': specifier: ^0.10.1 version: 0.10.1(typescript@5.6.2)(zod@3.23.8) @@ -862,7 +831,7 @@ importers: version: link:../../packages/react next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -908,7 +877,7 @@ importers: dependencies: '@clerk/nextjs': specifier: ^4.29.8 - version: 4.30.0(next@14.2.11(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 4.30.0(next@14.2.11(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@t3-oss/env-nextjs': specifier: ^0.10.1 version: 0.10.1(typescript@5.6.2)(zod@3.23.8) @@ -917,7 +886,7 @@ importers: version: link:../../packages/react next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -1033,7 +1002,7 @@ importers: version: 0.30.10(@cloudflare/workers-types@4.20240620.0)(@libsql/client@0.6.0)(@types/react@18.3.3)(bun-types@1.1.14)(react@18.3.1) next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -1094,7 +1063,7 @@ importers: version: 0.30.10(@cloudflare/workers-types@4.20240620.0)(@libsql/client@0.6.0)(@types/react@18.3.3)(bun-types@1.1.14)(react@18.3.1) next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -1170,7 +1139,7 @@ importers: version: 0.368.0(react@18.3.1) next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1222,7 +1191,7 @@ importers: version: link:../../packages/react next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -1253,7 +1222,7 @@ importers: dependencies: next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -1284,7 +1253,7 @@ importers: version: link:../../packages/react next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -1404,7 +1373,7 @@ importers: version: 3.13.2(rollup@3.29.5)(webpack-sources@3.2.3) '@nuxt/test-utils': specifier: ^3.12.0 - version: 3.12.1(@playwright/test@1.45.0)(h3@1.11.1)(happy-dom@13.10.1)(magicast@0.3.5)(playwright-core@1.45.0)(rollup@3.29.5)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1))(vue-router@4.3.2(vue@3.4.25(typescript@5.6.3)))(vue@3.4.25(typescript@5.6.3))(webpack-sources@3.2.3) + version: 3.12.1(@playwright/test@1.49.1)(h3@1.11.1)(happy-dom@13.10.1)(magicast@0.3.5)(playwright-core@1.49.1)(rollup@3.29.5)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1))(vue-router@4.3.2(vue@3.4.25(typescript@5.6.3)))(vue@3.4.25(typescript@5.6.3))(webpack-sources@3.2.3) '@nuxtjs/tailwindcss': specifier: ^6.12.2 version: 6.12.2(magicast@0.3.5)(rollup@3.29.5)(webpack-sources@3.2.3) @@ -1457,9 +1426,6 @@ importers: '@uploadthing/tsconfig': specifier: workspace:* version: link:../../tooling/tsconfig - '@vitest/browser': - specifier: 2.1.2 - version: 2.1.2(@vitest/spy@2.1.2)(playwright@1.45.0)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2) bunchee: specifier: ^6.1.2 version: 6.1.2(patch_hash=zsxtdggzsv4wyktgrmbmvae3om)(typescript@5.6.2) @@ -1471,7 +1437,7 @@ importers: version: 8.57.0 next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -1484,6 +1450,9 @@ importers: uploadthing: specifier: workspace:* version: link:../uploadthing + vitest-browser-react: + specifier: 0.0.4 + version: 0.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8)(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)) wait-on: specifier: ^7.2.0 version: 7.2.0 @@ -1497,8 +1466,8 @@ importers: specifier: workspace:* version: link:../mime-types effect: - specifier: 3.11.5 - version: 3.11.5 + specifier: 3.12.0 + version: 3.12.0 sqids: specifier: ^0.3.0 version: 0.3.0 @@ -1623,8 +1592,8 @@ importers: packages/uploadthing: dependencies: '@effect/platform': - specifier: 0.70.7 - version: 0.70.7(effect@3.11.5) + specifier: 0.72.0 + version: 0.72.0(effect@3.12.0) '@standard-schema/spec': specifier: 1.0.0-beta.3 version: 1.0.0-beta.3 @@ -1635,8 +1604,8 @@ importers: specifier: workspace:* version: link:../shared effect: - specifier: 3.11.5 - version: 3.11.5 + specifier: 3.12.0 + version: 3.12.0 devDependencies: '@remix-run/server-runtime': specifier: ^2.12.0 @@ -1679,7 +1648,7 @@ importers: version: 1.11.1 next: specifier: 14.2.11 - version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nuxt: specifier: ^3.11.2 version: 3.11.2(@parcel/watcher@2.4.1)(@types/node@22.7.5)(encoding@0.1.13)(eslint@8.57.0)(ioredis@5.3.2)(lightningcss@1.28.1)(magicast@0.3.5)(optionator@0.9.3)(rollup@4.29.1)(terser@5.34.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vue-tsc@2.0.14(typescript@5.6.2))(webpack-sources@3.2.3) @@ -1760,11 +1729,11 @@ importers: specifier: 2.1.1 version: 2.1.1 effect: - specifier: 3.11.5 - version: 3.11.5 + specifier: 3.12.0 + version: 3.12.0 next: specifier: canary - version: 15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -1801,16 +1770,16 @@ importers: dependencies: '@uploadthing/react': specifier: npm:@uploadthing/react@6 - version: 6.8.0(next@15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(solid-js@1.8.23)(svelte@4.2.15)(uploadthing@6.13.3(@effect/platform@0.70.7(effect@3.11.5))(express@4.21.1)(fastify@4.26.2)(h3@1.13.0)(next@15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.16))(vue@3.4.25(typescript@5.6.3)) + version: 6.8.0(next@15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(solid-js@1.8.23)(svelte@4.2.15)(uploadthing@6.13.3(@effect/platform@0.72.0(effect@3.12.0))(express@4.21.1)(fastify@4.26.2)(h3@1.13.0)(next@15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.16))(vue@3.4.25(typescript@5.6.3)) clsx: specifier: 2.1.1 version: 2.1.1 effect: - specifier: 3.11.5 - version: 3.11.5 + specifier: 3.12.0 + version: 3.12.0 next: specifier: canary - version: 15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -1819,7 +1788,7 @@ importers: version: 18.3.1(react@18.3.1) uploadthing: specifier: npm:uploadthing@6 - version: 6.13.3(@effect/platform@0.70.7(effect@3.11.5))(express@4.21.1)(fastify@4.26.2)(h3@1.13.0)(next@15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.16) + version: 6.13.3(@effect/platform@0.72.0(effect@3.12.0))(express@4.21.1)(fastify@4.26.2)(h3@1.13.0)(next@15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.16) zod: specifier: 3.23.8 version: 3.23.8 @@ -2873,8 +2842,8 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@bundled-es-modules/cookie@2.0.0': - resolution: {integrity: sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==} + '@bundled-es-modules/cookie@2.0.1': + resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} '@bundled-es-modules/statuses@1.0.1': resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} @@ -3123,32 +3092,32 @@ packages: resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} - '@effect/platform-node-shared@0.20.7': - resolution: {integrity: sha512-WwqXDwp/SUFMOhAgpNd8ReFjofHHtC19mpAus29m7X3bTyv/dLR80KRKGC2eI1Nm8ffg+vnfXx7xekTnom4PCA==} + '@effect/platform-node-shared@0.22.0': + resolution: {integrity: sha512-v5syfSWbek/v5uqRweEjq1zwtl17FMaOeSIt1OZFzQd4GIclVOgM/8chNVtAmpkdQh7SaKNijFruO0VET54e6w==} peerDependencies: - '@effect/platform': ^0.70.7 - effect: ^3.11.5 + '@effect/platform': ^0.72.0 + effect: ^3.12.0 - '@effect/platform-node@0.65.7': - resolution: {integrity: sha512-wos3sLmKV8mTXh5e5kY48wB+KlKDVNQeKTg6QXxexSOpFxDEpH74Hj6GM9jerTpqKahokUUyVVZ/PU4GdNVn8A==} + '@effect/platform-node@0.68.0': + resolution: {integrity: sha512-0BkiWEw59+C/54+UCPfCFBNrxkHOAUPa/GJKfWVyL7LOA1pbrLrwdse1S3deLwP4IEPuepMyEOL9BHNsorDhcA==} peerDependencies: - '@effect/platform': ^0.70.7 - effect: ^3.11.5 + '@effect/platform': ^0.72.0 + effect: ^3.12.0 - '@effect/platform@0.70.7': - resolution: {integrity: sha512-TbNwj/mOJhycPygbmicGBS7CNtv5Z8WVheRbLUdP3oPAe/nbSOJVLc8ZPvOejhquF/1vJMKuqY5MWfkcBpvi/g==} + '@effect/platform@0.72.0': + resolution: {integrity: sha512-uHsW2hlo6AiIW3zpLAVAoNvJngS/JwCZBAM4RSRllm8JTgjjCraef78FJBDNnSXz+2a10Xzzx4qxVsP3s8EV5Q==} peerDependencies: - effect: ^3.11.5 + effect: ^3.12.0 '@effect/schema@0.68.18': resolution: {integrity: sha512-+knLs36muKsyqIvQTB0itGp5Lwy+5jgEC0G5P8wSsrB6EWGFirS87QjbaFYGbg32l/P51RM+9cPMiAEyICwN6g==} peerDependencies: effect: ^3.4.8 - '@effect/vitest@0.13.15': - resolution: {integrity: sha512-vcXv8RscguOHh/IaZk5wnATPqfNu7my9NcDb80uBdwbuaLhkoogMZ+N6Q2+298MWiGs/5l/Ho6bmukexx2IvKQ==} + '@effect/vitest@0.16.0': + resolution: {integrity: sha512-caUXVs8Xsf07XUHRu1pOsHDiww7rE556+0PYZHIg+X6oL6gA9k2+d8ZFmutaW9hJoSQuy+vKyYAFM9/1rVSSAQ==} peerDependencies: - effect: ^3.10.15 + effect: ^3.12.0 vitest: ^2.0.5 '@egjs/agent@2.4.3': @@ -4319,8 +4288,8 @@ packages: resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} engines: {node: '>=18.18'} - '@ianvs/prettier-plugin-sort-imports@4.2.1': - resolution: {integrity: sha512-NKN1LVFWUDGDGr3vt+6Ey3qPeN/163uR1pOPAlkWpgvAqgxQ6kSdUf1F0it8aHUtKRUzEGcK38Wxd07O61d7+Q==} + '@ianvs/prettier-plugin-sort-imports@4.4.0': + resolution: {integrity: sha512-f4/e+/ANGk3tHuwRW0uh2YuBR50I4h1ZjGQ+5uD8sWfinHTivQsnieR5cz24t8M6Vx4rYvZ5v/IEKZhYpzQm9Q==} peerDependencies: '@vue/compiler-sfc': 2.7.x || 3.x prettier: 2 || 3 @@ -4546,25 +4515,25 @@ packages: cpu: [x64] os: [win32] - '@inquirer/confirm@3.2.0': - resolution: {integrity: sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==} - engines: {node: '>=18'} - - '@inquirer/core@9.2.1': - resolution: {integrity: sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==} + '@inquirer/confirm@5.1.1': + resolution: {integrity: sha512-vVLSbGci+IKQvDOtzpPTCOiEJCNidHcAq9JYVoWTW0svb5FiwSLotkM+JXNXejfjnzVYV9n0DTBythl9+XgTxg==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' - '@inquirer/figures@1.0.7': - resolution: {integrity: sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==} + '@inquirer/core@10.1.2': + resolution: {integrity: sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==} engines: {node: '>=18'} - '@inquirer/type@1.5.5': - resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==} + '@inquirer/figures@1.0.9': + resolution: {integrity: sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==} engines: {node: '>=18'} - '@inquirer/type@2.0.0': - resolution: {integrity: sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==} + '@inquirer/type@3.0.2': + resolution: {integrity: sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' '@internationalized/date@3.5.4': resolution: {integrity: sha512-qoVJVro+O0rBaw+8HPjUB1iH8Ihf8oziEnqMnvhJUSuVIrHOuZ6eNLHNvzXJKUvAtaDiqMnRlg8Z2mgh09BlUw==} @@ -4770,16 +4739,8 @@ packages: '@types/react': '>=16' react: '>=16' - '@mswjs/cookies@1.1.0': - resolution: {integrity: sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==} - engines: {node: '>=18'} - - '@mswjs/interceptors@0.26.15': - resolution: {integrity: sha512-HM47Lu1YFmnYHKMBynFfjCp0U/yRskHj/8QEJW0CBEPOlw8Gkmjfll+S9b8M7V5CNDw2/ciRxjjnWeaCiblSIQ==} - engines: {node: '>=18'} - - '@mswjs/interceptors@0.35.9': - resolution: {integrity: sha512-SSnyl/4ni/2ViHKkiZb8eajA/eN1DNFaHjhGiLUdZvDz6PKF4COSf/17xqSz64nOo2Ia29SA6B2KNCsyCbVmaQ==} + '@mswjs/interceptors@0.37.4': + resolution: {integrity: sha512-YUenGsnvhhuBkabJZrga8dv/8QFRBe/isTb5CYvmzaI/IISLIkKp8kItSu9URY9tsJLvkPkq2W48OU/piDvfnA==} engines: {node: '>=18'} '@neon-rs/load@0.0.4': @@ -4806,8 +4767,8 @@ packages: '@next/env@14.2.11': resolution: {integrity: sha512-HYsQRSIXwiNqvzzYThrBwq6RhXo3E0n8j8nQnAs8i4fCEo2Zf/3eS0IiRA8XnRg9Ha0YnpkyJZIZg1qEwemrHw==} - '@next/env@15.1.1-canary.23': - resolution: {integrity: sha512-SXTGshemjQqkpdpWoxLuXBXnWR9rpFzwWr3pBti6yAfphviUrpFCgB5vQrx9lM1AdSwQt6yb8ceJ4Mzzenks/g==} + '@next/env@15.1.1-canary.24': + resolution: {integrity: sha512-8ZxSBQ9POJreM9jvlY0zYpOL3Wo5B2FH17F4kokLS9kgvVN91reLAaT58MECydNLTfNlkpwDGXyMDYPnT6GvCA==} '@next/eslint-plugin-next@14.2.2': resolution: {integrity: sha512-q+Ec2648JtBpKiu/FSJm8HAsFXlNvioHeBCbTP12T1SGcHYwhqHULSfQgFkPgHDu3kzNp2Kem4J54bK4rPQ5SQ==} @@ -4832,8 +4793,8 @@ packages: cpu: [arm64] os: [darwin] - '@next/swc-darwin-arm64@15.1.1-canary.23': - resolution: {integrity: sha512-5eqwgu1ibgu6AN25+IoXBy4ikUT7T/pSKTf67EWvSE+wcSCI9Dw8i95rGRsssS3FJYJyqYoY2PknTmWQhQsMog==} + '@next/swc-darwin-arm64@15.1.1-canary.24': + resolution: {integrity: sha512-lZTlw0k3zY6XPFlLUwfcFJzwBOLkI2D+YDXFAayu/Jx2d9fLxKCgx3NP0QazhucNrT+lKy8yCn/rYDcQIGkdZw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -4844,8 +4805,8 @@ packages: cpu: [x64] os: [darwin] - '@next/swc-darwin-x64@15.1.1-canary.23': - resolution: {integrity: sha512-Zxr4vre6qbhY5OPcobX4T1G3eCQxTzL6SjOwn/PcQn//RH0f/3+IxLMoSrmMNp7Fy3h+nKCJxe7SILYzqfVyJw==} + '@next/swc-darwin-x64@15.1.1-canary.24': + resolution: {integrity: sha512-4Yw1uLDEM1uT0d3NFT+EfN1TmUOQcUs2vQKnx4sIehPfhr7LC9sR11c/fJbSy2A3uQ9Gcsd/1PdD0+WkQFMo6A==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -4856,8 +4817,8 @@ packages: cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-gnu@15.1.1-canary.23': - resolution: {integrity: sha512-rrLxBFHBgTSBuLpRpe3sfyz7yu1PmxHIQGJG2cOhxP+8gzER4sN8VL9dv+ux/xl97AvdLIYphxY28fKTW6V+4g==} + '@next/swc-linux-arm64-gnu@15.1.1-canary.24': + resolution: {integrity: sha512-T/hi1OKES2sQ6LNxLVQ0k43J9h/1xD1saHYjYPkekZxzWkprIhcetG33FB6//Mxs12T6LFaDfGBP2PBztRyJQg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -4868,8 +4829,8 @@ packages: cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.1.1-canary.23': - resolution: {integrity: sha512-IidMzvAPiLqKZe/YrF2nqXCQZtD4jj9xU7PAjspmNrfvpo9gv7XULhjinHQyyuvndV7thmdpj/NzAAxNmVAkUw==} + '@next/swc-linux-arm64-musl@15.1.1-canary.24': + resolution: {integrity: sha512-R0dzuHrlVgqZTzcqXVt3XMEMYHOF3j5XmV65N0FCNH6JhJhquZ+z93w4cO2DFK4DzImEEe3XCz5UOtiHJrCkfg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -4880,8 +4841,8 @@ packages: cpu: [x64] os: [linux] - '@next/swc-linux-x64-gnu@15.1.1-canary.23': - resolution: {integrity: sha512-JcMVi03+0SAYQSsKgFZ4FqBpJcQkDT3fr1E169tosfmgTNS+eWKBCjKmhTVUlXrjRZZnZ76EAIhgenXwaPNX7g==} + '@next/swc-linux-x64-gnu@15.1.1-canary.24': + resolution: {integrity: sha512-/ai4uLGHseS72nzohPAEBChBaGhAFhAtFNOmznNcESpq6VINS1TVNgaQSKwFruFyjiEIoqq73zWenzC0RYBv/A==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -4892,8 +4853,8 @@ packages: cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.1.1-canary.23': - resolution: {integrity: sha512-ud7DXXcswmxAR1NJ32WGh6Xz4u1G6S8BOWOYOkR9HpcpRZ6806ZlWj97s6DNSBsJFvU3HwYaJhTwv5ppLuCXzA==} + '@next/swc-linux-x64-musl@15.1.1-canary.24': + resolution: {integrity: sha512-a5Q0jiXVoqwjFDV24eX0uMvqk+ln1pYD53c206DsrK4schlUZboCE1KNQPjEarqpn2IxerJtbfQ8n9qVHSq5eQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -4904,8 +4865,8 @@ packages: cpu: [arm64] os: [win32] - '@next/swc-win32-arm64-msvc@15.1.1-canary.23': - resolution: {integrity: sha512-vDFLvDWYj+PrRzQqS9Lf6LVAoKNAUgL/+S3y0OuCNFHY6AsF77R3H+AM4OlFvOtoTwLJoBgMuRqRdDyRgIYzTw==} + '@next/swc-win32-arm64-msvc@15.1.1-canary.24': + resolution: {integrity: sha512-FesCeTvAl6MxUeBdMIJKzClgeH0+ylnQKDKehK6ZqWY/tnDU313lRPULXSl+YTqlw5bVJu3GTnWIWwuz4S11Tw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -4922,8 +4883,8 @@ packages: cpu: [x64] os: [win32] - '@next/swc-win32-x64-msvc@15.1.1-canary.23': - resolution: {integrity: sha512-JLa3WRxH5LJJc8YtR6JiTWmhqNQjoe+KDpAaGIsvhwWXBpKMMbhfs5zpUz7mPm9mrX/bWInc2TNhq6k7HuR4vQ==} + '@next/swc-win32-x64-msvc@15.1.1-canary.24': + resolution: {integrity: sha512-BuurAiSqa+sp4XQ0DHJfFYqXooTh4jCWvxQTirVyNGRO3I1nlBReo4kjjZj48Q0HNg7wh1E+Ptcln2KqekSugw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -5247,8 +5208,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.45.0': - resolution: {integrity: sha512-TVYsfMlGAaxeUllNkywbwek67Ncf8FRGn8ZlRdO291OL3NjG9oMbfVhyP82HQF0CZLMrYsvesqoUekxdWuF9Qw==} + '@playwright/test@1.49.1': + resolution: {integrity: sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==} engines: {node: '>=18'} hasBin: true @@ -6858,21 +6819,6 @@ packages: resolution: {integrity: sha512-JD0G+Zc38f5MBHA4NgxQMR5XtO5Jx9g86jqturNTt2WUfRmLDIY7iKkWHDCCTiDuFMre6nxAD5wHw9W5kI4rGw==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - '@testing-library/react@16.0.0': - resolution: {integrity: sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ==} - engines: {node: '>=18'} - peerDependencies: - '@testing-library/dom': ^10.0.0 - '@types/react': ^18.0.0 - '@types/react-dom': ^18.0.0 - react: ^18.0.0 - react-dom: ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@testing-library/user-event@14.5.2': resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} engines: {node: '>=12', npm: '>=6'} @@ -7222,9 +7168,6 @@ packages: '@types/ms@0.7.31': resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} - '@types/mute-stream@0.0.4': - resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} - '@types/nlcst@1.0.4': resolution: {integrity: sha512-ABoYdNQ/kBSsLvZAekMhIPMQ3YUZvavStpKYs7BjLLuKVmIMA0LUgZ7b54zzuWJRbHF80v1cNf4r90Vd6eMQDg==} @@ -7321,9 +7264,6 @@ packages: '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} - '@types/wrap-ansi@3.0.0': - resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} - '@types/ws@8.5.10': resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} @@ -7691,12 +7631,12 @@ packages: vite: ^5.0.0 vue: ^3.2.25 - '@vitest/browser@2.1.2': - resolution: {integrity: sha512-tqpGfz2sfjFFNuZ2iLZ6EGRVnH8z18O93ZIicbLsxDhiLgRNz84UcjSvX4pbheuddW+BJeNbLGdM3BU8vohbEg==} + '@vitest/browser@2.1.8': + resolution: {integrity: sha512-OWVvEJThRgxlNMYNVLEK/9qVkpRcLvyuKLngIV3Hob01P56NjPHprVBYn+rx4xAJudbM9yrCrywPIEuA3Xyo8A==} peerDependencies: playwright: '*' safaridriver: '*' - vitest: 2.1.2 + vitest: 2.1.8 webdriverio: '*' peerDependenciesMeta: playwright: @@ -7706,11 +7646,11 @@ packages: webdriverio: optional: true - '@vitest/coverage-v8@2.1.2': - resolution: {integrity: sha512-b7kHrFrs2urS0cOk5N10lttI8UdJ/yP3nB4JYTREvR5o18cR99yPpK4gK8oQgI42BVv0ILWYUSYB7AXkAUDc0g==} + '@vitest/coverage-v8@2.1.8': + resolution: {integrity: sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==} peerDependencies: - '@vitest/browser': 2.1.2 - vitest: 2.1.2 + '@vitest/browser': 2.1.8 + vitest: 2.1.8 peerDependenciesMeta: '@vitest/browser': optional: true @@ -7718,14 +7658,13 @@ packages: '@vitest/expect@1.6.0': resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} - '@vitest/expect@2.1.2': - resolution: {integrity: sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==} + '@vitest/expect@2.1.8': + resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==} - '@vitest/mocker@2.1.2': - resolution: {integrity: sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==} + '@vitest/mocker@2.1.8': + resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} peerDependencies: - '@vitest/spy': 2.1.2 - msw: ^2.3.5 + msw: ^2.4.9 vite: ^5.0.0 peerDependenciesMeta: msw: @@ -7733,32 +7672,32 @@ packages: vite: optional: true - '@vitest/pretty-format@2.1.2': - resolution: {integrity: sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==} + '@vitest/pretty-format@2.1.8': + resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==} '@vitest/runner@1.6.0': resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} - '@vitest/runner@2.1.2': - resolution: {integrity: sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==} + '@vitest/runner@2.1.8': + resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} '@vitest/snapshot@1.6.0': resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} - '@vitest/snapshot@2.1.2': - resolution: {integrity: sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==} + '@vitest/snapshot@2.1.8': + resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} '@vitest/spy@1.6.0': resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} - '@vitest/spy@2.1.2': - resolution: {integrity: sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==} + '@vitest/spy@2.1.8': + resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} '@vitest/utils@1.6.0': resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} - '@vitest/utils@2.1.2': - resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==} + '@vitest/utils@2.1.8': + resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} '@volar/language-core@2.2.0-alpha.10': resolution: {integrity: sha512-njVJLtpu0zMvDaEk7K5q4BRpOgbyEUljU++un9TfJoJNhxG0z/hWwpwgTRImO42EKvwIxF3XUzeMk+qatAFy7Q==} @@ -8651,8 +8590,8 @@ packages: resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} engines: {node: '>=4'} - chai@5.1.1: - resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} + chai@5.1.2: + resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} engines: {node: '>=12'} chalk@2.4.2: @@ -8999,6 +8938,10 @@ packages: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cookies@0.9.1: resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} engines: {node: '>= 0.8'} @@ -9623,8 +9566,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - effect@3.11.5: - resolution: {integrity: sha512-oSzaR/S/2A/qDTnDqMWxQUNSjCG2sRLB4NEvTu+l9RqE122MTgKXOWzw0x4MHsdovRTzAihfkpgBj2aLFnH2+w==} + effect@3.12.0: + resolution: {integrity: sha512-b/u9s3b9HfTo0qygVouegP0hkbiuxRIeaCe1ppf8P88hPyl6lKCbErtn7Az4jG7LuU7f0Wgm4c8WXbMcL2j8+g==} effect@3.4.8: resolution: {integrity: sha512-qOQNrSSN3ITuAtARtN2Ldq6E5f42splY9VV18LqpKOXMwQCCEWkXdns4by3D2CJnDXQD2KCE0iGcRR2KowiQIA==} @@ -10104,6 +10047,10 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} + expo-asset@10.0.10: resolution: {integrity: sha512-0qoTIihB79k+wGus9wy0JMKq7DdenziVx3iUkGvMAy2azscSgWH6bd2gJ9CGnhC6JRd3qTMFBL0ou/fx7WZl7A==} peerDependencies: @@ -10276,6 +10223,10 @@ packages: resolution: {integrity: sha512-8HKz3qXqnHYp/VCNn2qfjHdAdcI8zcSqOyX64GOMukp7SL2bfzfeDKjSd+UyECtejccaZv3LcvZTm9YDD22iCQ==} engines: {node: '>=8.0.0'} + fast-check@3.23.2: + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + engines: {node: '>=8.0.0'} + fast-content-type-parse@1.1.0: resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} @@ -10837,6 +10788,10 @@ packages: resolution: {integrity: sha512-9GZLEFvQL5EgfJX2zcBgu1nsPUn98JF/EiJnSfQbdxI6YEQGqpd09lXXxOmYonRBIEFz9JlGCOiPflDzgS1p8w==} engines: {node: '>=16.0.0'} + happy-dom@16.3.0: + resolution: {integrity: sha512-Q71RaIhyS21vhW17Tpa5W36yqQXIlE1TZ0A0Gguts8PShUSQE/7fBgxYGxgm3+5y0gF6afdlAVHLQqgrIcfRzg==} + engines: {node: '>=18.0.0'} + hard-rejection@2.1.0: resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} engines: {node: '>=6'} @@ -12890,18 +12845,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msw@2.2.13: - resolution: {integrity: sha512-ljFf1xZsU0b4zv1l7xzEmC6OZA6yD06hcx0H+dc8V0VypaP3HGYJa1rMLjQbBWl32ptGhcfwcPCWDB1wjmsftw==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - typescript: '>= 4.7.x' - peerDependenciesMeta: - typescript: - optional: true - - msw@2.4.9: - resolution: {integrity: sha512-1m8xccT6ipN4PTqLinPwmzhxQREuxaEJYdx4nIbggxP8aM7r1e71vE7RtOUSQoAm1LydjGfZKy7370XD/tsuYg==} + msw@2.7.0: + resolution: {integrity: sha512-BIodwZ19RWfCbYTxWTUfTXc+sg4OwjCAgxU1ZsgmggX/7S3LdUifsbUPJs61j0rWb19CZRGY5if77duhc0uXzw==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -12920,9 +12865,9 @@ packages: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true - mute-stream@1.0.0: - resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} mv@2.1.1: resolution: {integrity: sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==} @@ -13029,8 +12974,8 @@ packages: sass: optional: true - next@15.1.1-canary.23: - resolution: {integrity: sha512-KiG0PrS6B2IcVznR8FYTUNnziJBQX2nGhae4uPAGuG53qNiIpJ4Bc8THdveWtDPHn9pt5w46VHf+2LLqT0PHTg==} + next@15.1.1-canary.24: + resolution: {integrity: sha512-qAZkvVd3AyfdJpmeeQvSao2leYvarfufByAwDDNIViRVyyiTwtjn1+6PMrGBkuqtDaf1bcCHm8accjPZlgB2RQ==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: @@ -13677,13 +13622,13 @@ packages: pkg-types@1.2.1: resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==} - playwright-core@1.45.0: - resolution: {integrity: sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==} + playwright-core@1.49.1: + resolution: {integrity: sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==} engines: {node: '>=18'} hasBin: true - playwright@1.45.0: - resolution: {integrity: sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==} + playwright@1.49.1: + resolution: {integrity: sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==} engines: {node: '>=18'} hasBin: true @@ -14048,8 +13993,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-plugin-tailwindcss@0.6.5: - resolution: {integrity: sha512-axfeOArc/RiGHjOIy9HytehlC0ZLeMaqY09mm8YCkMzznKiDkwFzOpBvtuhuv3xG5qB73+Mj7OCe2j/L1ryfuQ==} + prettier-plugin-tailwindcss@0.6.9: + resolution: {integrity: sha512-r0i3uhaZAXYP0At5xGfJH876W3HHGHDp+LCRUJrs57PBeQ6mYHMwr25KH8NPX44F2yGTvdnH7OqCshlQx183Eg==} engines: {node: '>=14.21.3'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -14063,6 +14008,7 @@ packages: prettier-plugin-import-sort: '*' prettier-plugin-jsdoc: '*' prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' prettier-plugin-organize-attributes: '*' prettier-plugin-organize-imports: '*' prettier-plugin-sort-imports: '*' @@ -14089,6 +14035,8 @@ packages: optional: true prettier-plugin-marko: optional: true + prettier-plugin-multiline-arrays: + optional: true prettier-plugin-organize-attributes: optional: true prettier-plugin-organize-imports: @@ -14105,8 +14053,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} engines: {node: '>=14'} hasBin: true @@ -15853,9 +15801,6 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.0: - resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} - tinyexec@0.3.1: resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} @@ -16275,6 +16220,10 @@ packages: resolution: {integrity: sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==} engines: {node: '>=18.17'} + undici@7.2.0: + resolution: {integrity: sha512-klt+0S55GBViA9nsq48/NSCo4YX5mjydjypxD7UmHh/brMu8h/Mhd/F7qAeoH2NOO8SDTk6kjnTFc4WpzmfYpQ==} + engines: {node: '>=20.18.1'} + unenv-nightly@1.10.0-1717606461.a117952: resolution: {integrity: sha512-u3TfBX02WzbHTpaEfWEKwDijDSFAHcgXkayUZ+MVDrjhLFvgAJzFGTSTmwlEhwWi2exyRQey23ah9wELMM6etg==} @@ -16685,8 +16634,8 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true - vite-node@2.1.2: - resolution: {integrity: sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==} + vite-node@2.1.8: + resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -16818,6 +16767,22 @@ packages: vite: optional: true + vitest-browser-react@0.0.4: + resolution: {integrity: sha512-4uK8zgo5eHlhrBVEPX8ejRt8Bn4gzV6OZFTPdb1en3FtgjEhhst400XkIQHUC875Q90rOO5Tc4zPpCl8YXvoxg==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + '@types/react': '>18.0.0' + '@types/react-dom': '>18.0.0' + '@vitest/browser': '>=2.1.0' + react: '>18.0.0' + react-dom: '>18.0.0' + vitest: '>=2.1.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + vitest-environment-nuxt@1.0.0: resolution: {integrity: sha512-AWMO9h4HdbaFdPWZw34gALFI8gbBiOpvfbyeZwHIPfh4kWg/TwElYHvYMQ61WPUlCGaS5LebfHkaI0WPyb//Iw==} @@ -16846,15 +16811,15 @@ packages: jsdom: optional: true - vitest@2.1.2: - resolution: {integrity: sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==} + vitest@2.1.8: + resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.1.2 - '@vitest/ui': 2.1.2 + '@vitest/browser': 2.1.8 + '@vitest/ui': 2.1.8 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -17546,7 +17511,7 @@ snapshots: '@babel/generator@7.2.0': dependencies: - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 jsesc: 2.5.2 lodash: 4.17.21 source-map: 0.5.7 @@ -17569,7 +17534,7 @@ snapshots: '@babel/helper-annotate-as-pure@7.25.7': dependencies: - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 '@babel/helper-builder-binary-assignment-operator-visitor@7.25.7': dependencies: @@ -17602,7 +17567,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.25.7 '@babel/helper-replace-supers': 7.25.7(@babel/core@7.25.8) '@babel/helper-skip-transparent-expression-wrappers': 7.25.7 - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.26.4 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -17615,7 +17580,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.25.7 '@babel/helper-replace-supers': 7.25.7(@babel/core@7.26.0) '@babel/helper-skip-transparent-expression-wrappers': 7.25.7 - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.26.4 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -17660,19 +17625,19 @@ snapshots: '@babel/helper-member-expression-to-functions@7.25.7': dependencies: - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.18.6': dependencies: - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 '@babel/helper-module-imports@7.25.7': dependencies: - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 transitivePeerDependencies: - supports-color @@ -17723,7 +17688,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.25.7': dependencies: - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 '@babel/helper-plugin-utils@7.25.7': {} @@ -17732,7 +17697,7 @@ snapshots: '@babel/core': 7.25.8 '@babel/helper-annotate-as-pure': 7.25.7 '@babel/helper-wrap-function': 7.25.7 - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.26.4 transitivePeerDependencies: - supports-color @@ -17741,7 +17706,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.7 '@babel/helper-wrap-function': 7.25.7 - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.26.4 transitivePeerDependencies: - supports-color @@ -17750,7 +17715,7 @@ snapshots: '@babel/core': 7.25.8 '@babel/helper-member-expression-to-functions': 7.25.7 '@babel/helper-optimise-call-expression': 7.25.7 - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.26.4 transitivePeerDependencies: - supports-color @@ -17759,21 +17724,21 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-member-expression-to-functions': 7.25.7 '@babel/helper-optimise-call-expression': 7.25.7 - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.26.4 transitivePeerDependencies: - supports-color '@babel/helper-simple-access@7.25.7': dependencies: - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.25.7': dependencies: - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 transitivePeerDependencies: - supports-color @@ -17791,9 +17756,9 @@ snapshots: '@babel/helper-wrap-function@7.25.7': dependencies: - '@babel/template': 7.25.7 - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 transitivePeerDependencies: - supports-color @@ -18391,7 +18356,7 @@ snapshots: '@babel/helper-compilation-targets': 7.25.7 '@babel/helper-plugin-utils': 7.25.7 '@babel/helper-replace-supers': 7.25.7(@babel/core@7.25.8) - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.26.4 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -18403,7 +18368,7 @@ snapshots: '@babel/helper-compilation-targets': 7.25.7 '@babel/helper-plugin-utils': 7.25.7 '@babel/helper-replace-supers': 7.25.7(@babel/core@7.26.0) - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.26.4 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -18412,13 +18377,13 @@ snapshots: dependencies: '@babel/core': 7.25.8 '@babel/helper-plugin-utils': 7.25.7 - '@babel/template': 7.25.7 + '@babel/template': 7.25.9 '@babel/plugin-transform-computed-properties@7.25.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.25.7 - '@babel/template': 7.25.7 + '@babel/template': 7.25.9 '@babel/plugin-transform-destructuring@7.25.7(@babel/core@7.25.8)': dependencies: @@ -18537,7 +18502,7 @@ snapshots: '@babel/core': 7.25.8 '@babel/helper-compilation-targets': 7.25.7 '@babel/helper-plugin-utils': 7.25.7 - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.26.4 transitivePeerDependencies: - supports-color @@ -18546,7 +18511,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-compilation-targets': 7.25.7 '@babel/helper-plugin-utils': 7.25.7 - '@babel/traverse': 7.25.7 + '@babel/traverse': 7.26.4 transitivePeerDependencies: - supports-color @@ -19409,9 +19374,9 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@bundled-es-modules/cookie@2.0.0': + '@bundled-es-modules/cookie@2.0.1': dependencies: - cookie: 0.5.0 + cookie: 0.7.2 '@bundled-es-modules/statuses@1.0.1': dependencies: @@ -19643,14 +19608,14 @@ snapshots: transitivePeerDependencies: - react - '@clerk/nextjs@4.30.0(next@14.2.11(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@clerk/nextjs@4.30.0(next@14.2.11(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@clerk/backend': 0.38.7(react@18.3.1) '@clerk/clerk-react': 4.31.0(react@18.3.1) '@clerk/clerk-sdk-node': 4.13.15(react@18.3.1) '@clerk/shared': 1.4.1(react@18.3.1) '@clerk/types': 3.64.0 - next: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) path-to-regexp: 6.2.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -19833,28 +19798,28 @@ snapshots: '@discoveryjs/json-ext@0.5.7': {} - '@effect/platform-node-shared@0.20.7(@effect/platform@0.70.7(effect@3.11.5))(effect@3.11.5)': + '@effect/platform-node-shared@0.22.0(@effect/platform@0.72.0(effect@3.12.0))(effect@3.12.0)': dependencies: - '@effect/platform': 0.70.7(effect@3.11.5) + '@effect/platform': 0.72.0(effect@3.12.0) '@parcel/watcher': 2.4.1 - effect: 3.11.5 + effect: 3.12.0 multipasta: 0.2.5 - '@effect/platform-node@0.65.7(@effect/platform@0.70.7(effect@3.11.5))(effect@3.11.5)': + '@effect/platform-node@0.68.0(@effect/platform@0.72.0(effect@3.12.0))(effect@3.12.0)': dependencies: - '@effect/platform': 0.70.7(effect@3.11.5) - '@effect/platform-node-shared': 0.20.7(@effect/platform@0.70.7(effect@3.11.5))(effect@3.11.5) - effect: 3.11.5 + '@effect/platform': 0.72.0(effect@3.12.0) + '@effect/platform-node-shared': 0.22.0(@effect/platform@0.72.0(effect@3.12.0))(effect@3.12.0) + effect: 3.12.0 mime: 3.0.0 - undici: 6.19.8 + undici: 7.2.0 ws: 8.18.0 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/platform@0.70.7(effect@3.11.5)': + '@effect/platform@0.72.0(effect@3.12.0)': dependencies: - effect: 3.11.5 + effect: 3.12.0 find-my-way-ts: 0.1.5 multipasta: 0.2.5 @@ -19863,10 +19828,10 @@ snapshots: effect: 3.4.8 fast-check: 3.22.0 - '@effect/vitest@0.13.15(effect@3.11.5)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2)(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1))': + '@effect/vitest@0.16.0(effect@3.12.0)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8)(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1))': dependencies: - effect: 3.11.5 - vitest: 2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1) + effect: 3.12.0 + vitest: 2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1) '@egjs/agent@2.4.3': {} @@ -20942,14 +20907,13 @@ snapshots: '@humanwhocodes/retry@0.4.1': {} - '@ianvs/prettier-plugin-sort-imports@4.2.1(@vue/compiler-sfc@3.5.11)(prettier@3.3.3)': + '@ianvs/prettier-plugin-sort-imports@4.4.0(@vue/compiler-sfc@3.5.11)(prettier@3.4.2)': dependencies: - '@babel/core': 7.25.8 - '@babel/generator': 7.25.7 - '@babel/parser': 7.25.8 - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 - prettier: 3.3.3 + '@babel/generator': 7.26.3 + '@babel/parser': 7.26.3 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + prettier: 3.4.2 semver: 7.6.3 optionalDependencies: '@vue/compiler-sfc': 3.5.11 @@ -21106,35 +21070,31 @@ snapshots: '@img/sharp-win32-x64@0.33.5': optional: true - '@inquirer/confirm@3.2.0': + '@inquirer/confirm@5.1.1(@types/node@20.16.11)': dependencies: - '@inquirer/core': 9.2.1 - '@inquirer/type': 1.5.5 + '@inquirer/core': 10.1.2(@types/node@20.16.11) + '@inquirer/type': 3.0.2(@types/node@20.16.11) + '@types/node': 20.16.11 - '@inquirer/core@9.2.1': + '@inquirer/core@10.1.2(@types/node@20.16.11)': dependencies: - '@inquirer/figures': 1.0.7 - '@inquirer/type': 2.0.0 - '@types/mute-stream': 0.0.4 - '@types/node': 22.7.5 - '@types/wrap-ansi': 3.0.0 + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@20.16.11) ansi-escapes: 4.3.2 cli-width: 4.1.0 - mute-stream: 1.0.0 + mute-stream: 2.0.0 signal-exit: 4.1.0 strip-ansi: 6.0.1 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 + transitivePeerDependencies: + - '@types/node' - '@inquirer/figures@1.0.7': {} - - '@inquirer/type@1.5.5': - dependencies: - mute-stream: 1.0.0 + '@inquirer/figures@1.0.9': {} - '@inquirer/type@2.0.0': + '@inquirer/type@3.0.2(@types/node@20.16.11)': dependencies: - mute-stream: 1.0.0 + '@types/node': 20.16.11 '@internationalized/date@3.5.4': dependencies: @@ -21477,18 +21437,7 @@ snapshots: '@types/react': 18.3.3 react: 18.3.1 - '@mswjs/cookies@1.1.0': {} - - '@mswjs/interceptors@0.26.15(patch_hash=b4i3mfjisdgapabfrhqgz5r23e)': - dependencies: - '@open-draft/deferred-promise': 2.2.0 - '@open-draft/logger': 0.3.0 - '@open-draft/until': 2.1.0 - is-node-process: 1.2.0 - outvariant: 1.4.3 - strict-event-emitter: 0.5.1 - - '@mswjs/interceptors@0.35.9': + '@mswjs/interceptors@0.37.4': dependencies: '@open-draft/deferred-promise': 2.2.0 '@open-draft/logger': 0.3.0 @@ -21521,7 +21470,7 @@ snapshots: '@next/env@14.2.11': {} - '@next/env@15.1.1-canary.23': {} + '@next/env@15.1.1-canary.24': {} '@next/eslint-plugin-next@14.2.2': dependencies: @@ -21541,43 +21490,43 @@ snapshots: '@next/swc-darwin-arm64@14.2.11': optional: true - '@next/swc-darwin-arm64@15.1.1-canary.23': + '@next/swc-darwin-arm64@15.1.1-canary.24': optional: true '@next/swc-darwin-x64@14.2.11': optional: true - '@next/swc-darwin-x64@15.1.1-canary.23': + '@next/swc-darwin-x64@15.1.1-canary.24': optional: true '@next/swc-linux-arm64-gnu@14.2.11': optional: true - '@next/swc-linux-arm64-gnu@15.1.1-canary.23': + '@next/swc-linux-arm64-gnu@15.1.1-canary.24': optional: true '@next/swc-linux-arm64-musl@14.2.11': optional: true - '@next/swc-linux-arm64-musl@15.1.1-canary.23': + '@next/swc-linux-arm64-musl@15.1.1-canary.24': optional: true '@next/swc-linux-x64-gnu@14.2.11': optional: true - '@next/swc-linux-x64-gnu@15.1.1-canary.23': + '@next/swc-linux-x64-gnu@15.1.1-canary.24': optional: true '@next/swc-linux-x64-musl@14.2.11': optional: true - '@next/swc-linux-x64-musl@15.1.1-canary.23': + '@next/swc-linux-x64-musl@15.1.1-canary.24': optional: true '@next/swc-win32-arm64-msvc@14.2.11': optional: true - '@next/swc-win32-arm64-msvc@15.1.1-canary.23': + '@next/swc-win32-arm64-msvc@15.1.1-canary.24': optional: true '@next/swc-win32-ia32-msvc@14.2.11': @@ -21586,7 +21535,7 @@ snapshots: '@next/swc-win32-x64-msvc@14.2.11': optional: true - '@next/swc-win32-x64-msvc@15.1.1-canary.23': + '@next/swc-win32-x64-msvc@15.1.1-canary.24': optional: true '@nodelib/fs.scandir@2.1.5': @@ -22501,7 +22450,7 @@ snapshots: - supports-color - webpack-sources - '@nuxt/test-utils@3.12.1(@playwright/test@1.45.0)(h3@1.11.1)(happy-dom@13.10.1)(magicast@0.3.5)(playwright-core@1.45.0)(rollup@3.29.5)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1))(vue-router@4.3.2(vue@3.4.25(typescript@5.6.3)))(vue@3.4.25(typescript@5.6.3))(webpack-sources@3.2.3)': + '@nuxt/test-utils@3.12.1(@playwright/test@1.49.1)(h3@1.11.1)(happy-dom@13.10.1)(magicast@0.3.5)(playwright-core@1.49.1)(rollup@3.29.5)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1))(vue-router@4.3.2(vue@3.4.25(typescript@5.6.3)))(vue@3.4.25(typescript@5.6.3))(webpack-sources@3.2.3)': dependencies: '@nuxt/kit': 3.13.2(magicast@0.3.5)(rollup@3.29.5)(webpack-sources@3.2.3) '@nuxt/schema': 3.13.2(rollup@3.29.5)(webpack-sources@3.2.3) @@ -22527,14 +22476,14 @@ snapshots: unenv: 1.9.0 unplugin: 1.14.1(webpack-sources@3.2.3) vite: 5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) - vitest-environment-nuxt: 1.0.0(@playwright/test@1.45.0)(h3@1.11.1)(happy-dom@13.10.1)(magicast@0.3.5)(playwright-core@1.45.0)(rollup@3.29.5)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1))(vue-router@4.3.2(vue@3.4.25(typescript@5.6.3)))(vue@3.4.25(typescript@5.6.3))(webpack-sources@3.2.3) + vitest-environment-nuxt: 1.0.0(@playwright/test@1.49.1)(h3@1.11.1)(happy-dom@13.10.1)(magicast@0.3.5)(playwright-core@1.49.1)(rollup@3.29.5)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1))(vue-router@4.3.2(vue@3.4.25(typescript@5.6.3)))(vue@3.4.25(typescript@5.6.3))(webpack-sources@3.2.3) vue: 3.4.25(typescript@5.6.3) vue-router: 4.3.2(vue@3.4.25(typescript@5.6.3)) optionalDependencies: - '@playwright/test': 1.45.0 + '@playwright/test': 1.49.1 happy-dom: 13.10.1 - playwright-core: 1.45.0 - vitest: 1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1) + playwright-core: 1.49.1 + vitest: 1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1) transitivePeerDependencies: - magicast - rollup @@ -22985,9 +22934,9 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.45.0': + '@playwright/test@1.49.1': dependencies: - playwright: 1.45.0 + playwright: 1.49.1 '@pnpm/config.env-replace@1.1.0': {} @@ -23005,10 +22954,10 @@ snapshots: '@popperjs/core@2.11.8': {} - '@prettier/sync@0.5.2(prettier@3.3.3)': + '@prettier/sync@0.5.2(prettier@3.4.2)': dependencies: make-synchronized: 0.2.9 - prettier: 3.3.3 + prettier: 3.4.2 '@radix-ui/primitive@1.0.0': dependencies: @@ -23697,7 +23646,7 @@ snapshots: '@babel/plugin-transform-sticky-regex': 7.25.7(@babel/core@7.25.8) '@babel/plugin-transform-typescript': 7.25.7(@babel/core@7.25.8) '@babel/plugin-transform-unicode-regex': 7.25.7(@babel/core@7.25.8) - '@babel/template': 7.25.7 + '@babel/template': 7.25.9 '@react-native/babel-plugin-codegen': 0.74.83(@babel/preset-env@7.25.7(@babel/core@7.25.8)) babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.25.8) react-refresh: 0.14.2 @@ -23746,7 +23695,7 @@ snapshots: '@babel/plugin-transform-sticky-regex': 7.25.7(@babel/core@7.26.0) '@babel/plugin-transform-typescript': 7.25.7(@babel/core@7.26.0) '@babel/plugin-transform-unicode-regex': 7.25.7(@babel/core@7.26.0) - '@babel/template': 7.25.7 + '@babel/template': 7.25.9 '@react-native/babel-plugin-codegen': 0.74.83(@babel/preset-env@7.25.7(@babel/core@7.26.0)) babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.26.0) react-refresh: 0.14.2 @@ -23880,7 +23829,7 @@ snapshots: '@react-native/codegen@0.74.87(@babel/preset-env@7.25.7(@babel/core@7.25.8))': dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 '@babel/preset-env': 7.25.7(@babel/core@7.25.8) glob: 7.2.3 hermes-parser: 0.19.1 @@ -23893,7 +23842,7 @@ snapshots: '@react-native/codegen@0.74.87(@babel/preset-env@7.25.7(@babel/core@7.26.0))': dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 '@babel/preset-env': 7.25.7(@babel/core@7.26.0) glob: 7.2.3 hermes-parser: 0.19.1 @@ -24304,7 +24253,7 @@ snapshots: estree-walker: 2.0.2 glob: 8.1.0 is-reference: 1.2.1 - magic-string: 0.30.14 + magic-string: 0.30.17 optionalDependencies: rollup: 3.29.5 @@ -24315,7 +24264,7 @@ snapshots: estree-walker: 2.0.2 glob: 8.1.0 is-reference: 1.2.1 - magic-string: 0.30.14 + magic-string: 0.30.17 optionalDependencies: rollup: 4.24.0 @@ -24335,7 +24284,7 @@ snapshots: dependencies: '@rollup/pluginutils': 5.1.3(rollup@4.24.0) estree-walker: 2.0.2 - magic-string: 0.30.14 + magic-string: 0.30.17 optionalDependencies: rollup: 4.24.0 @@ -24392,21 +24341,21 @@ snapshots: '@rollup/plugin-replace@5.0.5(rollup@3.29.5)': dependencies: '@rollup/pluginutils': 5.1.3(rollup@3.29.5) - magic-string: 0.30.14 + magic-string: 0.30.17 optionalDependencies: rollup: 3.29.5 '@rollup/plugin-replace@5.0.5(rollup@4.24.0)': dependencies: '@rollup/pluginutils': 5.1.3(rollup@4.24.0) - magic-string: 0.30.14 + magic-string: 0.30.17 optionalDependencies: rollup: 4.24.0 '@rollup/plugin-replace@5.0.5(rollup@4.29.1)': dependencies: '@rollup/pluginutils': 5.1.3(rollup@4.29.1) - magic-string: 0.30.14 + magic-string: 0.30.17 optionalDependencies: rollup: 4.29.1 @@ -24591,11 +24540,11 @@ snapshots: '@rushstack/eslint-patch@1.10.2': {} - '@scalar/api-client@2.0.15(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1))': + '@scalar/api-client@2.0.15(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1))': dependencies: '@headlessui/tailwindcss': 0.2.1(tailwindcss@3.4.16) '@headlessui/vue': 1.7.20(vue@3.4.25(typescript@5.6.2)) - '@scalar/components': 0.12.12(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1)) + '@scalar/components': 0.12.12(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(typescript@5.6.2)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)) '@scalar/draggable': 0.1.3(typescript@5.6.2) '@scalar/oas-utils': 0.2.13(typescript@5.6.2) '@scalar/object-utils': 1.1.4(vue@3.4.25(typescript@5.6.2)) @@ -24628,9 +24577,9 @@ snapshots: - typescript - vitest - '@scalar/api-reference-react@0.3.37(@types/bun@1.1.5)(postcss@8.4.49)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1))': + '@scalar/api-reference-react@0.3.37(@types/bun@1.1.5)(postcss@8.4.49)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1))': dependencies: - '@scalar/api-reference': 1.24.39(@types/bun@1.1.5)(postcss@8.4.49)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1)) + '@scalar/api-reference': 1.24.39(@types/bun@1.1.5)(postcss@8.4.49)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)) react: 18.3.1 transitivePeerDependencies: - '@jest/globals' @@ -24646,12 +24595,12 @@ snapshots: - typescript - vitest - '@scalar/api-reference@1.24.39(@types/bun@1.1.5)(postcss@8.4.49)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1))': + '@scalar/api-reference@1.24.39(@types/bun@1.1.5)(postcss@8.4.49)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1))': dependencies: '@floating-ui/vue': 1.1.1(vue@3.4.25(typescript@5.6.2)) '@headlessui/vue': 1.7.20(vue@3.4.25(typescript@5.6.2)) - '@scalar/api-client': 2.0.15(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1)) - '@scalar/components': 0.12.12(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1)) + '@scalar/api-client': 2.0.15(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(tailwindcss@3.4.16)(typescript@5.6.2)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)) + '@scalar/components': 0.12.12(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(typescript@5.6.2)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)) '@scalar/oas-utils': 0.2.13(typescript@5.6.2) '@scalar/openapi-parser': 0.7.2 '@scalar/snippetz': 0.1.6 @@ -24705,13 +24654,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@scalar/components@0.12.12(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1))': + '@scalar/components@0.12.12(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(typescript@5.6.2)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1))': dependencies: '@floating-ui/utils': 0.2.7 '@floating-ui/vue': 1.1.1(vue@3.4.25(typescript@5.6.2)) '@headlessui/vue': 1.7.20(vue@3.4.25(typescript@5.6.2)) '@scalar/code-highlight': 0.0.7 - '@storybook/test': 8.2.1(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1)) + '@storybook/test': 8.2.1(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)) '@vueuse/core': 10.11.0(vue@3.4.25(typescript@5.6.2)) cva: 1.0.0-beta.1(typescript@5.6.2) nanoid: 5.0.7 @@ -24979,7 +24928,7 @@ snapshots: globby: 14.0.2 jscodeshift: 0.15.2(@babel/preset-env@7.25.7(@babel/core@7.26.0)) lodash: 4.17.21 - prettier: 3.3.3 + prettier: 3.4.2 recast: 0.23.9 tiny-invariant: 1.3.3 transitivePeerDependencies: @@ -25018,12 +24967,12 @@ snapshots: storybook: 8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)) util: 0.12.5 - '@storybook/test@8.2.1(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1))': + '@storybook/test@8.2.1(@types/bun@1.1.5)(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0)))(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1))': dependencies: '@storybook/csf': 0.1.11 '@storybook/instrumenter': 8.2.1(storybook@8.2.1(@babel/preset-env@7.25.7(@babel/core@7.26.0))) '@testing-library/dom': 10.1.0 - '@testing-library/jest-dom': 6.4.5(@types/bun@1.1.5)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1)) + '@testing-library/jest-dom': 6.4.5(@types/bun@1.1.5)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)) '@testing-library/user-event': 14.5.2(@testing-library/dom@10.1.0) '@vitest/expect': 1.6.0 '@vitest/spy': 1.6.0 @@ -25332,7 +25281,7 @@ snapshots: '@tanstack/router-generator@1.69.1': dependencies: '@tanstack/virtual-file-routes': 1.64.0 - prettier: 3.3.3 + prettier: 3.4.2 tsx: 4.19.1 zod: 3.23.8 @@ -25451,7 +25400,7 @@ snapshots: '@testing-library/dom@10.1.0': dependencies: - '@babel/code-frame': 7.25.7 + '@babel/code-frame': 7.26.2 '@babel/runtime': 7.25.7 '@types/aria-query': 5.0.4 aria-query: 5.3.0 @@ -25471,7 +25420,7 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.4.5(@types/bun@1.1.5)(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1))': + '@testing-library/jest-dom@6.4.5(@types/bun@1.1.5)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1))': dependencies: '@adobe/css-tools': 4.4.0 '@babel/runtime': 7.25.7 @@ -25483,7 +25432,7 @@ snapshots: redent: 3.0.0 optionalDependencies: '@types/bun': 1.1.5 - vitest: 2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1) + vitest: 2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1) '@testing-library/jest-dom@6.4.8': dependencies: @@ -25495,16 +25444,7 @@ snapshots: dom-accessibility-api: 0.6.3 lodash: 4.17.21 redent: 3.0.0 - - '@testing-library/react@16.0.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@babel/runtime': 7.25.7 - '@testing-library/dom': 10.4.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.12 - '@types/react-dom': 18.3.1 + optional: true '@testing-library/user-event@14.5.2(@testing-library/dom@10.1.0)': dependencies: @@ -25739,16 +25679,16 @@ snapshots: '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.25.8 - '@babel/types': 7.25.8 + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 '@types/body-parser@1.19.5': dependencies: @@ -25892,10 +25832,6 @@ snapshots: '@types/ms@0.7.31': {} - '@types/mute-stream@0.0.4': - dependencies: - '@types/node': 20.17.3 - '@types/nlcst@1.0.4': dependencies: '@types/unist': 2.0.7 @@ -25934,6 +25870,7 @@ snapshots: '@types/node@22.7.5': dependencies: undici-types: 6.19.8 + optional: true '@types/normalize-package-data@2.4.1': {} @@ -25996,8 +25933,6 @@ snapshots: '@types/web-bluetooth@0.0.20': {} - '@types/wrap-ansi@3.0.0': {} - '@types/ws@8.5.10': dependencies: '@types/node': 20.17.3 @@ -26386,16 +26321,16 @@ snapshots: '@uploadthing/mime-types@0.2.10': {} - '@uploadthing/react@6.8.0(next@15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(solid-js@1.8.23)(svelte@4.2.15)(uploadthing@6.13.3(@effect/platform@0.70.7(effect@3.11.5))(express@4.21.1)(fastify@4.26.2)(h3@1.13.0)(next@15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.16))(vue@3.4.25(typescript@5.6.3))': + '@uploadthing/react@6.8.0(next@15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(solid-js@1.8.23)(svelte@4.2.15)(uploadthing@6.13.3(@effect/platform@0.72.0(effect@3.12.0))(express@4.21.1)(fastify@4.26.2)(h3@1.13.0)(next@15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.16))(vue@3.4.25(typescript@5.6.3))': dependencies: '@uploadthing/dropzone': 0.4.1(react@18.3.1)(solid-js@1.8.23)(svelte@4.2.15)(vue@3.4.25(typescript@5.6.3)) '@uploadthing/shared': 6.7.9 file-selector: 0.6.0 react: 18.3.1 tailwind-merge: 2.3.0 - uploadthing: 6.13.3(@effect/platform@0.70.7(effect@3.11.5))(express@4.21.1)(fastify@4.26.2)(h3@1.13.0)(next@15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.16) + uploadthing: 6.13.3(@effect/platform@0.72.0(effect@3.12.0))(express@4.21.1)(fastify@4.26.2)(h3@1.13.0)(next@15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.16) optionalDependencies: - next: 15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - solid-js - svelte @@ -26603,48 +26538,28 @@ snapshots: vite: 5.4.8(@types/node@22.7.5)(lightningcss@1.28.1)(terser@5.34.1) vue: 3.4.25(typescript@5.6.3) - '@vitest/browser@2.1.2(@vitest/spy@2.1.2)(playwright@1.45.0)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2)': + '@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) - '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(msw@2.4.9(typescript@5.6.2))(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1)) - '@vitest/utils': 2.1.2 - magic-string: 0.30.11 - msw: 2.4.9(typescript@5.6.2) - sirv: 2.0.4 + '@vitest/mocker': 2.1.8(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1)) + '@vitest/utils': 2.1.8 + magic-string: 0.30.17 + msw: 2.7.0(@types/node@20.16.11)(typescript@5.6.2) + sirv: 3.0.0 tinyrainbow: 1.2.0 - vitest: 2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2)(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.4.9(typescript@5.6.2))(terser@5.34.1) + vitest: 2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1) ws: 8.18.0 optionalDependencies: - playwright: 1.45.0 - transitivePeerDependencies: - - '@vitest/spy' - - bufferutil - - typescript - - utf-8-validate - - vite - - '@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2)': - dependencies: - '@testing-library/dom': 10.4.0 - '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) - '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(msw@2.4.9(typescript@5.6.2))(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1)) - '@vitest/utils': 2.1.2 - magic-string: 0.30.11 - msw: 2.4.9(typescript@5.6.2) - sirv: 2.0.4 - tinyrainbow: 1.2.0 - vitest: 2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1) - ws: 8.18.0 + playwright: 1.49.1 transitivePeerDependencies: - - '@vitest/spy' + - '@types/node' - bufferutil - typescript - utf-8-validate - vite - optional: true - '@vitest/coverage-v8@2.1.2(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2)(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1))': + '@vitest/coverage-v8@2.1.8(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8)(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -26653,14 +26568,14 @@ snapshots: istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.1.7 - magic-string: 0.30.11 + magic-string: 0.30.17 magicast: 0.3.5 - std-env: 3.7.0 + std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1) + vitest: 2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1) optionalDependencies: - '@vitest/browser': 2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2) + '@vitest/browser': 2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8) transitivePeerDependencies: - supports-color @@ -26670,32 +26585,23 @@ snapshots: '@vitest/utils': 1.6.0 chai: 4.5.0 - '@vitest/expect@2.1.2': + '@vitest/expect@2.1.8': dependencies: - '@vitest/spy': 2.1.2 - '@vitest/utils': 2.1.2 - chai: 5.1.1 + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))': + '@vitest/mocker@2.1.8(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))': dependencies: - '@vitest/spy': 2.1.2 + '@vitest/spy': 2.1.8 estree-walker: 3.0.3 - magic-string: 0.30.11 - optionalDependencies: - msw: 2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2) - vite: 5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) - - '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(msw@2.4.9(typescript@5.6.2))(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))': - dependencies: - '@vitest/spy': 2.1.2 - estree-walker: 3.0.3 - magic-string: 0.30.11 + magic-string: 0.30.17 optionalDependencies: - msw: 2.4.9(typescript@5.6.2) + msw: 2.7.0(@types/node@20.16.11)(typescript@5.6.2) vite: 5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) - '@vitest/pretty-format@2.1.2': + '@vitest/pretty-format@2.1.8': dependencies: tinyrainbow: 1.2.0 @@ -26706,9 +26612,9 @@ snapshots: pathe: 1.1.2 optional: true - '@vitest/runner@2.1.2': + '@vitest/runner@2.1.8': dependencies: - '@vitest/utils': 2.1.2 + '@vitest/utils': 2.1.8 pathe: 1.1.2 '@vitest/snapshot@1.6.0': @@ -26718,17 +26624,17 @@ snapshots: pretty-format: 29.7.0 optional: true - '@vitest/snapshot@2.1.2': + '@vitest/snapshot@2.1.8': dependencies: - '@vitest/pretty-format': 2.1.2 - magic-string: 0.30.11 + '@vitest/pretty-format': 2.1.8 + magic-string: 0.30.17 pathe: 1.1.2 '@vitest/spy@1.6.0': dependencies: tinyspy: 2.2.1 - '@vitest/spy@2.1.2': + '@vitest/spy@2.1.8': dependencies: tinyspy: 3.0.2 @@ -26739,9 +26645,9 @@ snapshots: loupe: 2.3.7 pretty-format: 29.7.0 - '@vitest/utils@2.1.2': + '@vitest/utils@2.1.8': dependencies: - '@vitest/pretty-format': 2.1.2 + '@vitest/pretty-format': 2.1.8 loupe: 3.1.2 tinyrainbow: 1.2.0 @@ -26760,7 +26666,7 @@ snapshots: '@vue-macros/common@1.10.2(rollup@3.29.5)(vue@3.4.25(typescript@5.6.3))': dependencies: - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 '@rollup/pluginutils': 5.1.3(rollup@3.29.5) '@vue/compiler-sfc': 3.5.11 ast-kit: 0.12.1 @@ -26773,7 +26679,7 @@ snapshots: '@vue-macros/common@1.10.2(rollup@4.24.0)(vue@3.4.25(typescript@5.6.3))': dependencies: - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 '@rollup/pluginutils': 5.1.3(rollup@4.24.0) '@vue/compiler-sfc': 3.5.11 ast-kit: 0.12.1 @@ -26786,7 +26692,7 @@ snapshots: '@vue-macros/common@1.10.2(rollup@4.29.1)(vue@3.4.25(typescript@5.6.2))': dependencies: - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 '@rollup/pluginutils': 5.1.3(rollup@4.29.1) '@vue/compiler-sfc': 3.5.11 ast-kit: 0.12.1 @@ -26799,7 +26705,7 @@ snapshots: '@vue-macros/common@1.10.2(rollup@4.29.1)(vue@3.4.25(typescript@5.6.3))': dependencies: - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 '@rollup/pluginutils': 5.1.3(rollup@4.29.1) '@vue/compiler-sfc': 3.5.11 ast-kit: 0.12.1 @@ -26818,8 +26724,8 @@ snapshots: '@babel/helper-module-imports': 7.25.7 '@babel/plugin-syntax-jsx': 7.25.7(@babel/core@7.25.8) '@babel/template': 7.25.7 - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 '@vue/babel-helper-vue-transform-on': 1.1.5 camelcase: 6.3.0 html-tags: 3.3.1 @@ -26829,7 +26735,7 @@ snapshots: '@vue/compiler-core@3.4.25': dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 '@vue/shared': 3.4.25 entities: 4.5.0 estree-walker: 2.0.2 @@ -26837,7 +26743,7 @@ snapshots: '@vue/compiler-core@3.5.11': dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 '@vue/shared': 3.5.11 entities: 4.5.0 estree-walker: 2.0.2 @@ -26867,13 +26773,13 @@ snapshots: '@vue/compiler-sfc@3.5.11': dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 '@vue/compiler-core': 3.5.11 '@vue/compiler-dom': 3.5.11 '@vue/compiler-ssr': 3.5.11 '@vue/shared': 3.5.11 estree-walker: 2.0.2 - magic-string: 0.30.14 + magic-string: 0.30.17 postcss: 8.4.49 source-map-js: 1.2.1 @@ -27457,12 +27363,12 @@ snapshots: ast-kit@0.12.1: dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 pathe: 1.1.2 ast-kit@0.9.5(rollup@3.29.5): dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 '@rollup/pluginutils': 5.1.3(rollup@3.29.5) pathe: 1.1.2 transitivePeerDependencies: @@ -27470,7 +27376,7 @@ snapshots: ast-kit@0.9.5(rollup@4.24.0): dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 '@rollup/pluginutils': 5.1.3(rollup@4.24.0) pathe: 1.1.2 transitivePeerDependencies: @@ -27478,7 +27384,7 @@ snapshots: ast-kit@0.9.5(rollup@4.29.1): dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 '@rollup/pluginutils': 5.1.3(rollup@4.29.1) pathe: 1.1.2 transitivePeerDependencies: @@ -27496,21 +27402,21 @@ snapshots: ast-walker-scope@0.5.0(rollup@3.29.5): dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 ast-kit: 0.9.5(rollup@3.29.5) transitivePeerDependencies: - rollup ast-walker-scope@0.5.0(rollup@4.24.0): dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 ast-kit: 0.9.5(rollup@4.24.0) transitivePeerDependencies: - rollup ast-walker-scope@0.5.0(rollup@4.29.1): dependencies: - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 ast-kit: 0.9.5(rollup@4.29.1) transitivePeerDependencies: - rollup @@ -27668,9 +27574,9 @@ snapshots: babel-dead-code-elimination@1.0.6: dependencies: '@babel/core': 7.25.8 - '@babel/parser': 7.25.8 - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 + '@babel/parser': 7.26.3 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 transitivePeerDependencies: - supports-color @@ -27679,7 +27585,7 @@ snapshots: '@babel/core': 7.25.8 '@babel/helper-module-imports': 7.18.6 '@babel/plugin-syntax-jsx': 7.25.7(@babel/core@7.25.8) - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 html-entities: 2.3.3 validate-html-nesting: 1.2.2 @@ -27734,7 +27640,7 @@ snapshots: babel-plugin-react-compiler@0.0.0-experimental-592953e-20240517: dependencies: '@babel/generator': 7.2.0 - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 chalk: 4.1.2 invariant: 2.2.4 pretty-format: 24.9.0 @@ -28180,7 +28086,7 @@ snapshots: pathval: 1.1.1 type-detect: 4.1.0 - chai@5.1.1: + chai@5.1.2: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 @@ -28523,6 +28429,8 @@ snapshots: cookie@0.7.1: {} + cookie@0.7.2: {} + cookies@0.9.1: dependencies: depd: 2.0.0 @@ -29039,9 +28947,9 @@ snapshots: ee-first@1.1.1: {} - effect@3.11.5: + effect@3.12.0: dependencies: - fast-check: 3.22.0 + fast-check: 3.23.2 effect@3.4.8: {} @@ -29445,8 +29353,8 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.6.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.1(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) @@ -29469,13 +29377,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: debug: 4.3.7 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-core-module: 2.13.1 @@ -29486,14 +29394,14 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.8.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.6.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -29507,33 +29415,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): - dependencies: - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - hasown: 2.0.2 - is-core-module: 2.13.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.6.2) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0): dependencies: array-includes: 3.1.8 @@ -29949,6 +29830,8 @@ snapshots: expand-template@2.0.3: optional: true + expect-type@1.1.0: {} + expo-asset@10.0.10(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.7(@babel/core@7.25.8))(encoding@0.1.13)): dependencies: expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.7(@babel/core@7.25.8))(encoding@0.1.13) @@ -30287,6 +30170,10 @@ snapshots: dependencies: pure-rand: 6.1.0 + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 + fast-content-type-parse@1.1.0: {} fast-decode-uri-component@1.0.1: {} @@ -30646,9 +30533,9 @@ snapshots: strip-ansi: 6.0.1 wide-align: 1.1.5 - geist@1.3.0(next@14.2.11(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): + geist@1.3.0(next@14.2.11(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): dependencies: - next: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) generic-names@4.0.0: dependencies: @@ -30953,6 +30840,13 @@ snapshots: entities: 4.5.0 webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 + optional: true + + happy-dom@16.3.0: + dependencies: + webidl-conversions: 7.0.0 + whatwg-mimetype: 3.0.0 + optional: true hard-rejection@2.1.0: {} @@ -31719,7 +31613,7 @@ snapshots: jest-message-util@29.7.0: dependencies: - '@babel/code-frame': 7.25.7 + '@babel/code-frame': 7.26.2 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.3 chalk: 4.1.2 @@ -31823,7 +31717,7 @@ snapshots: jscodeshift@0.14.0(@babel/preset-env@7.25.7(@babel/core@7.25.8)): dependencies: '@babel/core': 7.25.8 - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.25.8) '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.25.8) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.25.8) @@ -31848,7 +31742,7 @@ snapshots: jscodeshift@0.14.0(@babel/preset-env@7.25.7(@babel/core@7.26.0)): dependencies: '@babel/core': 7.25.8 - '@babel/parser': 7.25.8 + '@babel/parser': 7.26.3 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.25.8) '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.25.8) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.25.8) @@ -32413,7 +32307,7 @@ snapshots: magic-string-ast@0.3.0: dependencies: - magic-string: 0.30.14 + magic-string: 0.30.17 magic-string@0.25.9: dependencies: @@ -32433,8 +32327,8 @@ snapshots: magicast@0.2.10: dependencies: - '@babel/parser': 7.25.8 - '@babel/types': 7.25.8 + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 recast: 0.23.9 magicast@0.3.5: @@ -32930,9 +32824,9 @@ snapshots: metro-transform-plugins@0.80.9: dependencies: '@babel/core': 7.25.8 - '@babel/generator': 7.25.7 - '@babel/template': 7.25.7 - '@babel/traverse': 7.25.7 + '@babel/generator': 7.26.3 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.4 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color @@ -32940,9 +32834,9 @@ snapshots: metro-transform-worker@0.80.9(encoding@0.1.13): dependencies: '@babel/core': 7.25.8 - '@babel/generator': 7.25.7 - '@babel/parser': 7.25.8 - '@babel/types': 7.25.8 + '@babel/generator': 7.26.3 + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 metro: 0.80.9(encoding@0.1.13) metro-babel-transformer: 0.80.9 metro-cache: 0.80.9 @@ -32961,11 +32855,11 @@ snapshots: dependencies: '@babel/code-frame': 7.25.7 '@babel/core': 7.25.8 - '@babel/generator': 7.25.7 - '@babel/parser': 7.25.8 + '@babel/generator': 7.26.3 + '@babel/parser': 7.26.3 '@babel/template': 7.25.7 - '@babel/traverse': 7.25.7 - '@babel/types': 7.25.8 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0 @@ -33668,49 +33562,30 @@ snapshots: ms@2.1.3: {} - msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2): - dependencies: - '@bundled-es-modules/cookie': 2.0.0 - '@bundled-es-modules/statuses': 1.0.1 - '@inquirer/confirm': 3.2.0 - '@mswjs/cookies': 1.1.0 - '@mswjs/interceptors': 0.26.15(patch_hash=b4i3mfjisdgapabfrhqgz5r23e) - '@open-draft/until': 2.1.0 - '@types/cookie': 0.6.0 - '@types/statuses': 2.0.5 - chalk: 4.1.2 - graphql: 16.9.0 - headers-polyfill: 4.0.3 - is-node-process: 1.2.0 - outvariant: 1.4.3 - path-to-regexp: 6.3.0 - strict-event-emitter: 0.5.1 - type-fest: 4.26.1 - yargs: 17.7.2 - optionalDependencies: - typescript: 5.6.2 - - msw@2.4.9(typescript@5.6.2): + msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2): dependencies: - '@bundled-es-modules/cookie': 2.0.0 + '@bundled-es-modules/cookie': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 '@bundled-es-modules/tough-cookie': 0.1.6 - '@inquirer/confirm': 3.2.0 - '@mswjs/interceptors': 0.35.9 + '@inquirer/confirm': 5.1.1(@types/node@20.16.11) + '@mswjs/interceptors': 0.37.4 + '@open-draft/deferred-promise': 2.2.0 '@open-draft/until': 2.1.0 '@types/cookie': 0.6.0 '@types/statuses': 2.0.5 - chalk: 4.1.2 graphql: 16.9.0 headers-polyfill: 4.0.3 is-node-process: 1.2.0 outvariant: 1.4.3 path-to-regexp: 6.3.0 + picocolors: 1.1.1 strict-event-emitter: 0.5.1 type-fest: 4.26.1 yargs: 17.7.2 optionalDependencies: typescript: 5.6.2 + transitivePeerDependencies: + - '@types/node' muggle-string@0.4.1: {} @@ -33718,7 +33593,7 @@ snapshots: mustache@4.2.0: {} - mute-stream@1.0.0: {} + mute-stream@2.0.0: {} mv@2.1.1: dependencies: @@ -33767,19 +33642,19 @@ snapshots: nested-error-stacks@2.0.1: {} - next-auth@5.0.0-beta.19(next@14.2.11(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + next-auth@5.0.0-beta.19(next@14.2.11(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: '@auth/core': 0.32.0 - next: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - next-sitemap@4.2.3(next@14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): + next-sitemap@4.2.3(next@14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): dependencies: '@corex/deepmerge': 4.0.43 '@next/env': 13.5.6 fast-glob: 3.3.2 minimist: 1.2.8 - next: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-themes@0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: @@ -33788,13 +33663,13 @@ snapshots: next-tick@1.1.0: {} - next-view-transitions@0.3.0(next@14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next-view-transitions@0.3.0(next@14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - next: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@14.2.11(@babel/core@7.26.0)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.11(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 14.2.11 '@swc/helpers': 0.5.5 @@ -33815,14 +33690,14 @@ snapshots: '@next/swc-win32-arm64-msvc': 14.2.11 '@next/swc-win32-ia32-msvc': 14.2.11 '@next/swc-win32-x64-msvc': 14.2.11 - '@playwright/test': 1.45.0 + '@playwright/test': 1.49.1 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - next@15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@next/env': 15.1.1-canary.23 + '@next/env': 15.1.1-canary.24 '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 @@ -33832,15 +33707,15 @@ snapshots: react-dom: 18.3.1(react@18.3.1) styled-jsx: 5.1.6(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 15.1.1-canary.23 - '@next/swc-darwin-x64': 15.1.1-canary.23 - '@next/swc-linux-arm64-gnu': 15.1.1-canary.23 - '@next/swc-linux-arm64-musl': 15.1.1-canary.23 - '@next/swc-linux-x64-gnu': 15.1.1-canary.23 - '@next/swc-linux-x64-musl': 15.1.1-canary.23 - '@next/swc-win32-arm64-msvc': 15.1.1-canary.23 - '@next/swc-win32-x64-msvc': 15.1.1-canary.23 - '@playwright/test': 1.45.0 + '@next/swc-darwin-arm64': 15.1.1-canary.24 + '@next/swc-darwin-x64': 15.1.1-canary.24 + '@next/swc-linux-arm64-gnu': 15.1.1-canary.24 + '@next/swc-linux-arm64-musl': 15.1.1-canary.24 + '@next/swc-linux-x64-gnu': 15.1.1-canary.24 + '@next/swc-linux-x64-musl': 15.1.1-canary.24 + '@next/swc-win32-arm64-msvc': 15.1.1-canary.24 + '@next/swc-win32-x64-msvc': 15.1.1-canary.24 + '@playwright/test': 1.49.1 sharp: 0.33.5 transitivePeerDependencies: - '@babel/core' @@ -34859,7 +34734,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.25.7 + '@babel/code-frame': 7.26.2 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -35017,11 +34892,11 @@ snapshots: mlly: 1.7.3 pathe: 1.1.2 - playwright-core@1.45.0: {} + playwright-core@1.49.1: {} - playwright@1.45.0: + playwright@1.49.1: dependencies: - playwright-core: 1.45.0 + playwright-core: 1.49.1 optionalDependencies: fsevents: 2.3.2 @@ -35372,15 +35247,15 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-tailwindcss@0.6.5(@ianvs/prettier-plugin-sort-imports@4.2.1(@vue/compiler-sfc@3.5.11)(prettier@3.3.3))(prettier@3.3.3): + prettier-plugin-tailwindcss@0.6.9(@ianvs/prettier-plugin-sort-imports@4.4.0(@vue/compiler-sfc@3.5.11)(prettier@3.4.2))(prettier@3.4.2): dependencies: - prettier: 3.3.3 + prettier: 3.4.2 optionalDependencies: - '@ianvs/prettier-plugin-sort-imports': 4.2.1(@vue/compiler-sfc@3.5.11)(prettier@3.3.3) + '@ianvs/prettier-plugin-sort-imports': 4.4.0(@vue/compiler-sfc@3.5.11)(prettier@3.4.2) prettier@2.8.8: {} - prettier@3.3.3: {} + prettier@3.4.2: {} pretty-bytes@5.6.0: {} @@ -36466,7 +36341,7 @@ snapshots: rollup-plugin-dts@6.1.0(rollup@3.29.5)(typescript@5.6.3): dependencies: - magic-string: 0.30.14 + magic-string: 0.30.17 rollup: 3.29.5 typescript: 5.6.3 optionalDependencies: @@ -37007,9 +36882,9 @@ snapshots: solid-refresh@0.6.3(solid-js@1.8.23): dependencies: - '@babel/generator': 7.25.7 + '@babel/generator': 7.26.3 '@babel/helper-module-imports': 7.25.7 - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 solid-js: 1.8.23 transitivePeerDependencies: - supports-color @@ -37152,7 +37027,7 @@ snapshots: jscodeshift: 0.15.2(@babel/preset-env@7.25.7(@babel/core@7.26.0)) leven: 3.1.0 ora: 5.4.1 - prettier: 3.3.3 + prettier: 3.4.2 prompts: 2.4.2 semver: 7.6.3 strip-json-comments: 3.1.1 @@ -37699,8 +37574,6 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.0: {} - tinyexec@0.3.1: {} tinyglobby@0.2.10: @@ -38125,6 +37998,8 @@ snapshots: undici@6.19.8: {} + undici@7.2.0: {} + unenv-nightly@1.10.0-1717606461.a117952: dependencies: consola: 3.3.3 @@ -38570,7 +38445,7 @@ snapshots: dependencies: '@babel/core': 7.25.8 '@babel/standalone': 7.26.2 - '@babel/types': 7.25.8 + '@babel/types': 7.26.3 defu: 6.1.4 jiti: 2.4.1 mri: 1.2.0 @@ -38594,7 +38469,7 @@ snapshots: unwasm@0.3.9(webpack-sources@3.2.3): dependencies: knitwork: 1.1.0 - magic-string: 0.30.14 + magic-string: 0.30.17 mlly: 1.7.1 pathe: 1.1.2 pkg-types: 1.2.0 @@ -38608,7 +38483,7 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - uploadthing@6.13.3(@effect/platform@0.70.7(effect@3.11.5))(express@4.21.1)(fastify@4.26.2)(h3@1.13.0)(next@15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.16): + uploadthing@6.13.3(@effect/platform@0.72.0(effect@3.12.0))(express@4.21.1)(fastify@4.26.2)(h3@1.13.0)(next@15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(tailwindcss@3.4.16): dependencies: '@effect/schema': 0.68.18(effect@3.4.8) '@uploadthing/mime-types': 0.2.10 @@ -38617,11 +38492,11 @@ snapshots: effect: 3.4.8 std-env: 3.7.0 optionalDependencies: - '@effect/platform': 0.70.7(effect@3.11.5) + '@effect/platform': 0.72.0(effect@3.12.0) express: 4.21.1 fastify: 4.26.2 h3: 1.13.0 - next: 15.1.1-canary.23(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.1.1-canary.24(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) tailwindcss: 3.4.16 uqr@0.1.2: {} @@ -38843,10 +38718,11 @@ snapshots: - supports-color - terser - vite-node@2.1.2(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1): + vite-node@2.1.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1): dependencies: cac: 6.7.14 debug: 4.3.7 + es-module-lexer: 1.5.4 pathe: 1.1.2 vite: 5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) transitivePeerDependencies: @@ -39044,7 +38920,7 @@ snapshots: '@vue/babel-plugin-jsx': 1.1.5(@babel/core@7.25.8) '@vue/compiler-dom': 3.5.11 kolorist: 1.8.0 - magic-string: 0.30.14 + magic-string: 0.30.17 vite: 5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) transitivePeerDependencies: - supports-color @@ -39059,7 +38935,7 @@ snapshots: '@vue/babel-plugin-jsx': 1.1.5(@babel/core@7.25.8) '@vue/compiler-dom': 3.5.11 kolorist: 1.8.0 - magic-string: 0.30.14 + magic-string: 0.30.17 vite: 5.4.8(@types/node@22.7.5)(lightningcss@1.28.1)(terser@5.34.1) transitivePeerDependencies: - supports-color @@ -39079,17 +38955,6 @@ snapshots: transitivePeerDependencies: - supports-color - vite-tsconfig-paths@4.3.2(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1)): - dependencies: - debug: 4.3.7 - globrex: 0.1.2 - tsconfck: 3.1.3(typescript@5.6.2) - optionalDependencies: - vite: 5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) - transitivePeerDependencies: - - supports-color - - typescript - vite-tsconfig-paths@4.3.2(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.5)(lightningcss@1.28.1)(terser@5.34.1)): dependencies: debug: 4.3.7 @@ -39127,9 +38992,19 @@ snapshots: optionalDependencies: vite: 5.4.8(@types/node@22.7.5)(lightningcss@1.28.1)(terser@5.34.1) - vitest-environment-nuxt@1.0.0(@playwright/test@1.45.0)(h3@1.11.1)(happy-dom@13.10.1)(magicast@0.3.5)(playwright-core@1.45.0)(rollup@3.29.5)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1))(vue-router@4.3.2(vue@3.4.25(typescript@5.6.3)))(vue@3.4.25(typescript@5.6.3))(webpack-sources@3.2.3): + vitest-browser-react@0.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8)(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1)): dependencies: - '@nuxt/test-utils': 3.12.1(@playwright/test@1.45.0)(h3@1.11.1)(happy-dom@13.10.1)(magicast@0.3.5)(playwright-core@1.45.0)(rollup@3.29.5)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1))(vue-router@4.3.2(vue@3.4.25(typescript@5.6.3)))(vue@3.4.25(typescript@5.6.3))(webpack-sources@3.2.3) + '@vitest/browser': 2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + vitest: 2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + + vitest-environment-nuxt@1.0.0(@playwright/test@1.49.1)(h3@1.11.1)(happy-dom@13.10.1)(magicast@0.3.5)(playwright-core@1.49.1)(rollup@3.29.5)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1))(vue-router@4.3.2(vue@3.4.25(typescript@5.6.3)))(vue@3.4.25(typescript@5.6.3))(webpack-sources@3.2.3): + dependencies: + '@nuxt/test-utils': 3.12.1(@playwright/test@1.49.1)(h3@1.11.1)(happy-dom@13.10.1)(magicast@0.3.5)(playwright-core@1.49.1)(rollup@3.29.5)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1))(vue-router@4.3.2(vue@3.4.25(typescript@5.6.3)))(vue@3.4.25(typescript@5.6.3))(webpack-sources@3.2.3) transitivePeerDependencies: - '@cucumber/cucumber' - '@jest/globals' @@ -39150,7 +39025,7 @@ snapshots: - vue-router - webpack-sources - vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1): + vitest@1.6.0(@types/node@22.7.5)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@13.10.1)(lightningcss@1.28.1)(terser@5.34.1): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -39174,7 +39049,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.7.5 - '@vitest/browser': 2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2) + '@vitest/browser': 2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8) happy-dom: 13.10.1 transitivePeerDependencies: - less @@ -39187,67 +39062,32 @@ snapshots: - terser optional: true - vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2))(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(terser@5.34.1): + vitest@2.1.8(@types/node@20.16.11)(@vitest/browser@2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8))(happy-dom@16.3.0)(lightningcss@1.28.1)(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(terser@5.34.1): dependencies: - '@vitest/expect': 2.1.2 - '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(msw@2.2.13(patch_hash=mpkjv35lscrawpqthnrnago5ai)(typescript@5.6.2))(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1)) - '@vitest/pretty-format': 2.1.2 - '@vitest/runner': 2.1.2 - '@vitest/snapshot': 2.1.2 - '@vitest/spy': 2.1.2 - '@vitest/utils': 2.1.2 - chai: 5.1.1 + '@vitest/expect': 2.1.8 + '@vitest/mocker': 2.1.8(msw@2.7.0(@types/node@20.16.11)(typescript@5.6.2))(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1)) + '@vitest/pretty-format': 2.1.8 + '@vitest/runner': 2.1.8 + '@vitest/snapshot': 2.1.8 + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.1.2 debug: 4.3.7 - magic-string: 0.30.11 - pathe: 1.1.2 - std-env: 3.7.0 - tinybench: 2.9.0 - tinyexec: 0.3.0 - tinypool: 1.0.1 - tinyrainbow: 1.2.0 - vite: 5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) - vite-node: 2.1.2(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 20.16.11 - '@vitest/browser': 2.1.2(@vitest/spy@2.1.2)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2) - happy-dom: 13.10.1 - transitivePeerDependencies: - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vitest@2.1.2(@types/node@20.16.11)(@vitest/browser@2.1.2)(happy-dom@13.10.1)(lightningcss@1.28.1)(msw@2.4.9(typescript@5.6.2))(terser@5.34.1): - dependencies: - '@vitest/expect': 2.1.2 - '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(msw@2.4.9(typescript@5.6.2))(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1)) - '@vitest/pretty-format': 2.1.2 - '@vitest/runner': 2.1.2 - '@vitest/snapshot': 2.1.2 - '@vitest/spy': 2.1.2 - '@vitest/utils': 2.1.2 - chai: 5.1.1 - debug: 4.3.7 - magic-string: 0.30.11 + expect-type: 1.1.0 + magic-string: 0.30.17 pathe: 1.1.2 - std-env: 3.7.0 + std-env: 3.8.0 tinybench: 2.9.0 - tinyexec: 0.3.0 + tinyexec: 0.3.1 tinypool: 1.0.1 tinyrainbow: 1.2.0 vite: 5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) - vite-node: 2.1.2(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) + vite-node: 2.1.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.16.11 - '@vitest/browser': 2.1.2(@vitest/spy@2.1.2)(playwright@1.45.0)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.2) - happy-dom: 13.10.1 + '@vitest/browser': 2.1.8(@types/node@20.16.11)(playwright@1.49.1)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.11)(lightningcss@1.28.1)(terser@5.34.1))(vitest@2.1.8) + happy-dom: 16.3.0 transitivePeerDependencies: - less - lightningcss @@ -39401,7 +39241,8 @@ snapshots: webidl-conversions@5.0.0: {} - webidl-conversions@7.0.0: {} + webidl-conversions@7.0.0: + optional: true webpack-bundle-analyzer@4.10.1: dependencies: @@ -39489,7 +39330,8 @@ snapshots: whatwg-fetch@3.6.20: {} - whatwg-mimetype@3.0.0: {} + whatwg-mimetype@3.0.0: + optional: true whatwg-url-without-unicode@8.0.0-3: dependencies: diff --git a/prettier.config.cjs b/prettier.config.js similarity index 79% rename from prettier.config.cjs rename to prettier.config.js index 8be64e28df..759ebcb05f 100644 --- a/prettier.config.cjs +++ b/prettier.config.js @@ -1,7 +1,5 @@ -/** @typedef {import("prettier").Config} PrettierConfig */ - -/** @type { PrettierConfig | SortImportsConfig } */ -const config = { +/** @type { import("prettier").Config } */ +export default { arrowParens: "always", printWidth: 80, singleQuote: false, @@ -14,6 +12,7 @@ const config = { ], // Last version that doesn't squash type and value imports importOrderTypeScriptVersion: "4.4.0", + importOrderParserPlugins: ["typescript", "jsx", "explicitResourceManagement"], importOrder: [ "^(react/(.*)$)|^(react$)", "^(next/(.*)$)|^(next$)", @@ -27,5 +26,3 @@ const config = { ], proseWrap: "always", // printWidth line breaks in md/mdx }; - -module.exports = config; diff --git a/tooling/gh-actions/setup/action.yml b/tooling/gh-actions/setup/action.yml index 521be12801..1c52e0cd00 100644 --- a/tooling/gh-actions/setup/action.yml +++ b/tooling/gh-actions/setup/action.yml @@ -10,7 +10,7 @@ runs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 cache: pnpm - name: Setup bun diff --git a/vitest.workspace.ts b/vitest.workspace.ts index c1efc9a8b8..e69761d01c 100644 --- a/vitest.workspace.ts +++ b/vitest.workspace.ts @@ -1 +1,28 @@ -export default ["packages/*"]; +import { defineWorkspace } from "vitest/config"; + +export default defineWorkspace([ + { + extends: "./vitest.config.ts", + test: { + include: [ + "**/*.test.{ts,tsx}", + "!**/*.browser.test.{ts,tsx}", + "!**/*.e2e.test.{ts,tsx}", + ], + name: "unit", + environment: "node", + }, + }, + { + extends: "./vitest.config.ts", + test: { + include: ["**/*.browser.test.{ts,tsx}"], + name: "browser", + browser: { + provider: "playwright", + enabled: true, + name: "chromium", + }, + }, + }, +]);