diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e14e8f..cdf6366 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,5 +33,8 @@ jobs: - name: Type check run: pnpm check-types + - name: Test + run: pnpm test + - name: Build run: pnpm build diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..54a7573 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,10 @@ +# Testing requirements + +- Keep API tests under `apps/api/test`; never colocate tests in `apps/api/src`. +- Keep web tests under `apps/web/test`; never colocate tests in `apps/web/app`, `components`, `hooks`, `lib`, `services`, or `store`. +- Add or update tests whenever runtime code changes. This includes utilities, middleware, hooks, services, stores, components, pages, route wiring, and configuration. +- New behavior must cover success, failure, validation, and relevant edge cases. +- Use deterministic Faker factories or explicit fixture values. Do not depend on random data or test execution order. +- Preserve the 100% statements, branches, functions, and lines thresholds for the DB-free coverage boundary defined in each app's `vitest.config.ts`. +- Do not weaken coverage thresholds or add coverage exclusions merely to make CI pass. Document any legitimate infrastructure exclusion in the relevant Vitest configuration. +- Run the affected app's tests, coverage, type checks, and lint before considering work complete. diff --git a/apps/api/package.json b/apps/api/package.json index d8d3fa4..2b4b579 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -7,8 +7,11 @@ "dev": "bun run --hot src/server.ts", "build": "tsc", "start": "bun run src/server.ts", - "check-types": "tsc --noEmit", + "check-types": "tsc --noEmit && tsc --noEmit -p test/tsconfig.json", "lint": "eslint .", + "test": "TZ=UTC vitest run", + "test:unit": "TZ=UTC vitest run", + "test:coverage": "TZ=UTC vitest run --coverage", "db:generate": "pnpm build && drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:push": "pnpm build && drizzle-kit push", @@ -29,12 +32,15 @@ "zod": "catalog:" }, "devDependencies": { + "@faker-js/faker": "^10.0.0", "@types/bun": "^1.2.14", + "@vitest/coverage-v8": "4.1.9", "@workspace/eslint-config": "workspace:*", "@workspace/typescript-config": "workspace:*", "dotenv": "^17.3.1", "drizzle-kit": "^0.31.1", "eslint": "^9.39.2", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "4.1.9" } } diff --git a/apps/api/test/config/config.test.ts b/apps/api/test/config/config.test.ts new file mode 100644 index 0000000..93016cd --- /dev/null +++ b/apps/api/test/config/config.test.ts @@ -0,0 +1,64 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; + +const originalEnv = { ...process.env }; + +afterEach(() => { + process.env = { ...originalEnv }; + vi.resetModules(); +}); + +describe("runtime configuration", () => { + it("parses valid API environment values and defaults", async () => { + process.env = { + ...originalEnv, + NODE_ENV: "development", + DATABASE_URL: "postgres://example", + WEB_URL: "https://web.example.test", + BETTER_AUTH_SECRET: "x".repeat(32), + BETTER_AUTH_URL: "https://api.example.test", + RESEND_API_KEY: "key", + RESEND_FROM_EMAIL: "test@example.test", + GOOGLE_CLIENT_ID: "client", + GOOGLE_CLIENT_SECRET: "secret", + }; + delete process.env.PORT; + + const { env } = await import("@/config/env.config.js"); + + expect(env.PORT).toBe(3001); + expect(env.NODE_ENV).toBe("development"); + }); + + it("reports all invalid environment values", async () => { + process.env = { NODE_ENV: "test" }; + + await expect(import("@/config/env.config.js")).rejects.toThrow( + /Invalid environment variables:\nNODE_ENV:/, + ); + }); + + it("labels schema-level environment issues", async () => { + vi.doMock("@workspace/validators/schemas/env", () => ({ + apiEnvSchema: { + safeParse: () => ({ + success: false, + error: { issues: [{ path: [], message: "invalid configuration" }] }, + }), + }, + })); + + await expect(import("@/config/env.config.js")).rejects.toThrow("env: invalid configuration"); + vi.doUnmock("@workspace/validators/schemas/env"); + }); + + it.each([ + ["production", "info"], + ["development", "debug"], + ])("sets %s logging to %s", async (nodeEnv, level) => { + process.env.NODE_ENV = nodeEnv; + + const { logger } = await import("@/config/logger.config.js"); + + expect(logger.level).toBe(level); + }); +}); diff --git a/apps/api/test/lib/core.test.ts b/apps/api/test/lib/core.test.ts new file mode 100644 index 0000000..12eefd3 --- /dev/null +++ b/apps/api/test/lib/core.test.ts @@ -0,0 +1,80 @@ +import { faker } from "@faker-js/faker"; +import { Hono } from "hono"; +import { beforeEach, describe, expect, it } from "vitest"; +import { STATUS_CODES } from "@/constants/status-codes.js"; +import { getHealth } from "@/controllers/health.controller.js"; +import { AppError } from "@/lib/app-error.js"; +import { toDate } from "@/lib/date.js"; +import { getTokenFromAuthUrl } from "@/lib/get-token-from-url.js"; +import { getSessionWorkspaceId } from "@/lib/workspace.js"; + +describe("DB-free API helpers", () => { + beforeEach(() => { + faker.seed(20260706); + }); + + it("returns the health response through the public HTTP interface", async () => { + const app = new Hono().get("/health", getHealth); + + const response = await app.request("/health"); + + expect(response.status).toBe(STATUS_CODES.OK); + await expect(response.json()).resolves.toEqual({ + success: true, + data: { status: "ok" }, + }); + }); + + it("normalizes supported date inputs", () => { + const date = faker.date.past(); + + expect(toDate(undefined)).toBeUndefined(); + expect(toDate(date)).toBe(date); + expect(toDate(date.toISOString())).toEqual(date); + }); + + it("extracts auth tokens from query strings and paths", () => { + const token = faker.string.uuid(); + + expect(getTokenFromAuthUrl(`https://api.example.test/verify?token=${token}`)).toBe(token); + expect(getTokenFromAuthUrl(`https://api.example.test/verify/${token}`)).toBe(token); + expect(getTokenFromAuthUrl("https://api.example.test")).toBeNull(); + expect(getTokenFromAuthUrl("not a url")).toBeNull(); + }); + + it("returns the active workspace and rejects sessions without one", async () => { + const workspaceId = faker.string.uuid(); + const app = new Hono<{ + Variables: { session: { activeOrganizationId?: string } }; + }>() + .get("/with-workspace", (c) => { + c.set("session", { activeOrganizationId: workspaceId }); + return c.json({ workspaceId: getSessionWorkspaceId(c) }); + }) + .get("/without-workspace", (c) => { + getSessionWorkspaceId(c); + return c.body(null); + }) + .onError(() => new Response(null, { status: STATUS_CODES.INTERNAL_SERVER_ERROR })); + + const success = await app.request("/with-workspace"); + expect(await success.json()).toEqual({ workspaceId }); + + const failure = await app.request("/without-workspace"); + expect(failure.status).toBe(STATUS_CODES.INTERNAL_SERVER_ERROR); + }); + + it("creates operational application errors with defaults and details", () => { + const defaultError = new AppError("failed"); + const details = { field: faker.database.column() }; + const validationError = new AppError("invalid", STATUS_CODES.BAD_REQUEST, details); + + expect(defaultError).toMatchObject({ + name: "AppError", + message: "failed", + statusCode: STATUS_CODES.INTERNAL_SERVER_ERROR, + isOperational: true, + }); + expect(validationError.details).toBe(details); + }); +}); diff --git a/apps/api/test/lib/http.test.ts b/apps/api/test/lib/http.test.ts new file mode 100644 index 0000000..80494db --- /dev/null +++ b/apps/api/test/lib/http.test.ts @@ -0,0 +1,126 @@ +import { faker } from "@faker-js/faker"; +import { Hono } from "hono"; +import { HTTPException } from "hono/http-exception"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { STATUS_CODES } from "@/constants/status-codes.js"; +import { AppError } from "@/lib/app-error.js"; +import { sendError, sendSuccess } from "@/lib/api-response.js"; + +const logger = vi.hoisted(() => ({ + error: vi.fn(), + info: vi.fn(), + warn: vi.fn(), +})); + +vi.mock("@/config/logger.config.js", () => ({ logger })); + +import { notFoundHandler, onErrorHandler } from "@/lib/http-handlers.js"; + +describe("HTTP responses and errors", () => { + beforeEach(() => { + faker.seed(20260706); + }); + + it("supports success messages and explicit statuses", async () => { + const message = faker.lorem.sentence(); + const app = new Hono().get("/", (c) => + sendSuccess(c, { id: faker.string.uuid() }, STATUS_CODES.CREATED, message), + ); + + const response = await app.request("/"); + + expect(response.status).toBe(STATUS_CODES.CREATED); + expect(await response.json()).toEqual({ + success: true, + data: { id: expect.any(String) }, + message, + }); + }); + + it("returns errors with and without details", async () => { + const app = new Hono() + .get("/simple", (c) => sendError(c, "missing", STATUS_CODES.NOT_FOUND)) + .get("/detailed", (c) => + sendError(c, "invalid", STATUS_CODES.BAD_REQUEST, { field: "name" }), + ); + + await expect((await app.request("/simple")).json()).resolves.toEqual({ + success: false, + error: { message: "missing" }, + }); + await expect((await app.request("/detailed")).json()).resolves.toEqual({ + success: false, + error: { message: "invalid", details: { field: "name" } }, + }); + }); + + it("formats not-found responses", async () => { + const app = new Hono().notFound(notFoundHandler); + + const response = await app.request("/unknown", { method: "PATCH" }); + + expect(response.status).toBe(STATUS_CODES.NOT_FOUND); + expect(await response.json()).toMatchObject({ + error: { message: "Route PATCH /unknown not found" }, + }); + }); + + it.each([ + ["application", new AppError("invalid", STATUS_CODES.UNPROCESSABLE_ENTITY, "details"), 422], + ["HTTP", new HTTPException(403, { message: "forbidden" }), 403], + ])("returns operational %s errors", async (_label, error, status) => { + const app = new Hono() + .get("/", () => { + throw error; + }) + .onError(onErrorHandler); + + const response = await app.request("/"); + + expect(response.status).toBe(status); + expect(logger.warn).toHaveBeenCalled(); + }); + + it("preserves Better Auth HTTP responses", async () => { + const app = new Hono() + .get("/api/auth/fail", () => { + throw new HTTPException(429, { message: "rate limited" }); + }) + .onError(onErrorHandler); + + const response = await app.request("/api/auth/fail"); + + expect(response.status).toBe(429); + expect(await response.text()).toBe("rate limited"); + }); + + it("hides unexpected server errors", async () => { + const error = new Error("secret failure"); + const app = new Hono() + .get("/", () => { + throw error; + }) + .onError(onErrorHandler); + + const response = await app.request("/"); + + expect(response.status).toBe(500); + expect(await response.json()).toMatchObject({ + error: { message: "Internal Server Error" }, + }); + expect(logger.error).toHaveBeenCalled(); + }); + + it("handles unknown non-Error values defensively", async () => { + let context: Parameters[1] | undefined; + const app = new Hono().get("/", (c) => { + context = c; + return c.body(null); + }); + await app.request("/"); + + const response = await onErrorHandler("non-error" as never, context!); + + expect(response.status).toBe(500); + }); +}); diff --git a/apps/api/test/lib/infrastructure.test.ts b/apps/api/test/lib/infrastructure.test.ts new file mode 100644 index 0000000..3b7278e --- /dev/null +++ b/apps/api/test/lib/infrastructure.test.ts @@ -0,0 +1,71 @@ +import { Hono } from "hono"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + send: vi.fn(), + info: vi.fn(), + error: vi.fn(), +})); + +vi.mock("resend", () => ({ + Resend: class { + emails = { send: mocks.send }; + }, +})); +vi.mock("@/config/env.config.js", () => ({ + env: { + RESEND_API_KEY: "test-key", + RESEND_FROM_EMAIL: "Stallion ", + WEB_URL: "https://web.example.test", + }, +})); +vi.mock("@/config/logger.config.js", () => ({ + logger: { info: mocks.info, error: mocks.error }, +})); + +import { corsMiddleware } from "@/lib/cors.js"; +import { sendEmail } from "@/lib/email.js"; + +describe("external infrastructure adapters", () => { + beforeEach(() => { + mocks.send.mockResolvedValue({ data: { id: "email-id" }, error: null }); + }); + + it("sends email through the configured provider", async () => { + await sendEmail({ to: "ada@example.test", subject: "Hello", html: "

Hello

" }); + + expect(mocks.send).toHaveBeenCalledWith({ + from: "Stallion ", + to: "ada@example.test", + subject: "Hello", + html: "

Hello

", + }); + expect(mocks.info).toHaveBeenCalled(); + }); + + it("logs and throws provider failures", async () => { + mocks.send.mockResolvedValueOnce({ data: null, error: { message: "rejected" } }); + + await expect( + sendEmail({ to: "ada@example.test", subject: "Hello", html: "

Hello

" }), + ).rejects.toThrow("Failed to send email: rejected"); + expect(mocks.error).toHaveBeenCalled(); + }); + + it("applies the configured CORS policy", async () => { + const app = new Hono().use("*", corsMiddleware).get("/", (c) => c.text("ok")); + + const response = await app.request("/", { + method: "OPTIONS", + headers: { + origin: "https://web.example.test", + "access-control-request-method": "PATCH", + "access-control-request-headers": "Content-Type,Authorization", + }, + }); + + expect(response.headers.get("access-control-allow-origin")).toBe("https://web.example.test"); + expect(response.headers.get("access-control-allow-credentials")).toBe("true"); + expect(response.headers.get("access-control-allow-methods")).toContain("PATCH"); + }); +}); diff --git a/apps/api/test/middlewares/auth-middleware.test.ts b/apps/api/test/middlewares/auth-middleware.test.ts new file mode 100644 index 0000000..4a44f0c --- /dev/null +++ b/apps/api/test/middlewares/auth-middleware.test.ts @@ -0,0 +1,53 @@ +import { faker } from "@faker-js/faker"; +import { Hono } from "hono"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + getSession: vi.fn(), + logger: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, +})); + +vi.mock("@/lib/auth.config.js", () => ({ + auth: { api: { getSession: mocks.getSession } }, +})); +vi.mock("@/config/logger.config.js", () => ({ logger: mocks.logger })); + +import { authMiddleware } from "@/middlewares/auth-middleware.js"; + +describe("authentication middleware", () => { + beforeEach(() => { + faker.seed(20260706); + }); + + it("rejects requests without a session", async () => { + mocks.getSession.mockResolvedValueOnce(null); + const app = new Hono().use("*", authMiddleware).get("/", (c) => c.text("private")); + + const response = await app.request("/"); + + expect(response.status).toBe(401); + expect(mocks.logger.warn).toHaveBeenCalledWith("Failed to authenticate session"); + }); + + it("adds authenticated user and session to context", async () => { + const session = { + user: { id: faker.string.uuid(), email: faker.internet.email() }, + session: { id: faker.string.uuid(), activeOrganizationId: faker.string.uuid() }, + }; + mocks.getSession.mockResolvedValueOnce(session); + const app = new Hono<{ + Variables: { user: unknown; session: unknown }; + }>() + .use("*", authMiddleware) + .get("/", (c) => c.json({ user: c.get("user"), session: c.get("session") })); + + const response = await app.request("/", { headers: { cookie: "session=value" } }); + + expect(await response.json()).toEqual(session); + expect(mocks.getSession).toHaveBeenCalledWith({ headers: expect.any(Headers) }); + expect(mocks.logger.info).toHaveBeenCalledWith( + expect.objectContaining({ userId: session.user.id, sessionId: session.session.id }), + "User authenticated", + ); + }); +}); diff --git a/apps/api/test/middlewares/custom-fields-auth.test.ts b/apps/api/test/middlewares/custom-fields-auth.test.ts new file mode 100644 index 0000000..00a2d7d --- /dev/null +++ b/apps/api/test/middlewares/custom-fields-auth.test.ts @@ -0,0 +1,29 @@ +import { Hono, type Context, type Next } from "hono"; +import { describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + authorize: vi.fn(), + getWorkspaceId: vi.fn(() => "workspace-id"), +})); + +vi.mock("@/utils/crm-custom-fields.js", () => ({ + assertCanManageCustomFields: mocks.authorize, +})); +vi.mock("@/lib/workspace.js", () => ({ + getSessionWorkspaceId: mocks.getWorkspaceId, +})); + +import { customFieldsAuthMiddleware } from "@/middlewares/custom-fields-auth.js"; + +describe("custom-field authorization middleware", () => { + it("authorizes the active workspace before continuing", async () => { + const app = new Hono() + .use("*", (c: Context, next: Next) => customFieldsAuthMiddleware(c, next)) + .get("/", (c) => c.text("ok")); + + const response = await app.request("/"); + + expect(await response.text()).toBe("ok"); + expect(mocks.authorize).toHaveBeenCalledWith(expect.anything(), "workspace-id"); + }); +}); diff --git a/apps/api/test/middlewares/middleware.test.ts b/apps/api/test/middlewares/middleware.test.ts new file mode 100644 index 0000000..486c8e0 --- /dev/null +++ b/apps/api/test/middlewares/middleware.test.ts @@ -0,0 +1,123 @@ +import { Hono } from "hono"; +import { HTTPException } from "hono/http-exception"; +import { z } from "zod"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { STATUS_CODES } from "@/constants/status-codes.js"; +import { AppError } from "@/lib/app-error.js"; +import { onErrorHandler } from "@/lib/http-handlers.js"; +import { requestLogger } from "@/middlewares/request-logger.js"; +import { validateRequest } from "@/middlewares/validate-request.js"; + +const logger = vi.hoisted(() => ({ + error: vi.fn(), + info: vi.fn(), + warn: vi.fn(), +})); + +vi.mock("@/config/logger.config.js", () => ({ logger })); + +describe("request middleware", () => { + beforeEach(() => { + vi.spyOn(Date, "now").mockReturnValueOnce(100).mockReturnValue(125); + }); + + it("validates JSON and exposes parsed input", async () => { + const app = new Hono() + .post("/", validateRequest("json", z.object({ name: z.string().min(2) })), (c) => + c.json(c.req.valid("json")), + ) + .onError(onErrorHandler); + + const valid = await app.request("/", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ name: "Ada" }), + }); + expect(await valid.json()).toEqual({ name: "Ada" }); + + const invalid = await app.request("/", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ name: "A" }), + }); + expect(invalid.status).toBe(STATUS_CODES.UNPROCESSABLE_ENTITY); + expect(await invalid.json()).toMatchObject({ + error: { message: "Validation failed", details: expect.stringContaining("name:") }, + }); + }); + + it("uses bad request for invalid query data and handles non-Zod failures", async () => { + const invalidResult = { success: false as const, error: "invalid" }; + const nonZodSchema = { + safeParse: () => invalidResult, + safeParseAsync: async () => invalidResult, + }; + const app = new Hono() + .get("/query", validateRequest("query", z.object({ page: z.coerce.number().min(1) })), (c) => + c.json(c.req.valid("query")), + ) + .get("/other", validateRequest("query", nonZodSchema), (c) => c.body(null)) + .onError(onErrorHandler); + + expect((await app.request("/query?page=0")).status).toBe(STATUS_CODES.BAD_REQUEST); + const other = await app.request("/other"); + expect(await other.json()).toMatchObject({ + error: { details: "Invalid request payload" }, + }); + }); + + it("labels root-level validation issues with their request target", async () => { + const app = new Hono() + .get("/", validateRequest("query", z.string()), (c) => c.body(null)) + .onError(onErrorHandler); + + const response = await app.request("/"); + + expect(await response.json()).toMatchObject({ + error: { details: expect.stringContaining("query:") }, + }); + }); + + it.each([ + ["success", undefined, 204], + ["application error", new AppError("bad", STATUS_CODES.BAD_REQUEST), 400], + ["HTTP error", new HTTPException(404), 404], + ["unknown error", new Error("bad"), 500], + ])("logs status and duration for %s", async (_label, error, expectedStatus) => { + const app = new Hono(); + app.use("*", requestLogger); + app.get("/", (c) => { + if (error) throw error; + return c.body(null, 204); + }); + app.onError(() => new Response(null, { status: expectedStatus })); + + await app.request("/"); + + expect(logger.info).toHaveBeenCalledWith( + expect.objectContaining({ + method: "GET", + path: "/", + statusCode: expectedStatus, + durationMs: 25, + }), + "HTTP request completed", + ); + }); + + it.each([ + new AppError("bad", STATUS_CODES.BAD_REQUEST), + new HTTPException(404), + new Error("bad"), + ])("logs errors observed directly from downstream middleware", async (error) => { + let context: Parameters[0] | undefined; + const app = new Hono().get("/", (c) => { + context = c; + return c.body(null); + }); + await app.request("/"); + + await expect(requestLogger(context!, () => Promise.reject(error))).rejects.toBe(error); + expect(logger.info).toHaveBeenCalled(); + }); +}); diff --git a/apps/api/test/routes/routes.test.ts b/apps/api/test/routes/routes.test.ts new file mode 100644 index 0000000..5428c59 --- /dev/null +++ b/apps/api/test/routes/routes.test.ts @@ -0,0 +1,174 @@ +import { faker } from "@faker-js/faker"; +import { Hono, type Context, type Next } from "hono"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const controller = vi.hoisted(() => { + const respond = vi.fn((c: Context, ...args: unknown[]) => c.json({ args })); + return { + respond, + pipelineByStage: respond, + peopleByStatus: respond, + winRate: respond, + getOnboardingStatus: respond, + completeCrmTour: respond, + listDeals: respond, + getDeal: respond, + createDeal: respond, + updateDeal: respond, + deleteDeal: respond, + listOrgs: respond, + getOrg: respond, + createOrg: respond, + updateOrg: respond, + deleteOrg: respond, + bulkDeleteOrgs: respond, + listPeople: respond, + getPerson: respond, + createPerson: respond, + updatePerson: respond, + deletePerson: respond, + bulkDeletePeople: respond, + listCustomFieldDefinitions: respond, + createCustomFieldDefinition: respond, + updateCustomFieldDefinition: respond, + deleteCustomFieldDefinition: respond, + authHandler: vi.fn(() => new Response("auth")), + }; +}); + +vi.mock("@/controllers/analytics.controller.js", () => ({ + pipelineByStage: controller.pipelineByStage, + peopleByStatus: controller.peopleByStatus, + winRate: controller.winRate, +})); +vi.mock("@/controllers/onboarding.controller.js", () => ({ + getOnboardingStatus: controller.getOnboardingStatus, + completeCrmTour: controller.completeCrmTour, +})); +vi.mock("@/controllers/deals.controller.js", () => ({ + listDeals: controller.listDeals, + getDeal: controller.getDeal, + createDeal: controller.createDeal, + updateDeal: controller.updateDeal, + deleteDeal: controller.deleteDeal, +})); +vi.mock("@/controllers/org.controller.js", () => ({ + listOrgs: controller.listOrgs, + getOrg: controller.getOrg, + createOrg: controller.createOrg, + updateOrg: controller.updateOrg, + deleteOrg: controller.deleteOrg, + bulkDeleteOrgs: controller.bulkDeleteOrgs, +})); +vi.mock("@/controllers/people.controller.js", () => ({ + listPeople: controller.listPeople, + getPerson: controller.getPerson, + createPerson: controller.createPerson, + updatePerson: controller.updatePerson, + deletePerson: controller.deletePerson, + bulkDeletePeople: controller.bulkDeletePeople, +})); +vi.mock("@/controllers/crm-custom-fields.controller.js", () => ({ + listCustomFieldDefinitions: controller.listCustomFieldDefinitions, + createCustomFieldDefinition: controller.createCustomFieldDefinition, + updateCustomFieldDefinition: controller.updateCustomFieldDefinition, + deleteCustomFieldDefinition: controller.deleteCustomFieldDefinition, +})); +vi.mock("@/middlewares/auth-middleware.js", () => ({ + authMiddleware: (_c: Context, next: Next) => next(), +})); +vi.mock("@/middlewares/custom-fields-auth.js", () => ({ + customFieldsAuthMiddleware: (_c: Context, next: Next) => next(), +})); +vi.mock("@/lib/auth.config.js", () => ({ + auth: { handler: controller.authHandler }, +})); +vi.mock("@/config/env.config.js", () => ({ + env: { PORT: 3001, WEB_URL: "https://web.example.test" }, +})); + +import server from "@/server.js"; +import { analyticsRoutes } from "@/routes/analytics.route.js"; +import { authRoutes } from "@/routes/auth.route.js"; +import { dealRoutes } from "@/routes/deals.route.js"; +import { healthRoutes } from "@/routes/health.route.js"; +import { onboardingRoutes } from "@/routes/onboarding.route.js"; +import { orgRoutes } from "@/routes/org.route.js"; +import { peopleRoutes } from "@/routes/people.route.js"; +import { registerRoutes } from "@/routes/index.js"; + +const jsonRequest = (method: string, body?: unknown) => ({ + method, + headers: { "content-type": "application/json" }, + body: body === undefined ? undefined : JSON.stringify(body), +}); + +describe("API route wiring without database infrastructure", () => { + beforeEach(() => { + faker.seed(20260706); + }); + + it.each([ + [analyticsRoutes, "/pipeline", "GET"], + [analyticsRoutes, "/people-status", "GET"], + [analyticsRoutes, "/win-rate", "GET"], + [onboardingRoutes, "/", "GET"], + [onboardingRoutes, "/crm-tour/complete", "POST"], + ])("dispatches %s %s routes", async (app, path, method) => { + expect((await app.request(path, { method })).status).toBe(200); + }); + + it("dispatches every deals route with validated values", async () => { + const id = faker.string.uuid(); + const cases: Array<[string, RequestInit | undefined]> = [ + ["/", undefined], + [`/${id}`, undefined], + ["/", jsonRequest("POST", { title: "Deal" })], + [`/${id}`, jsonRequest("PATCH", { stage: "won" })], + [`/${id}`, { method: "DELETE" }], + ]; + + for (const [path, init] of cases) { + expect((await dealRoutes.request(path, init)).status).toBe(200); + } + }); + + it.each([ + ["organizations", orgRoutes, { name: "Acme" }], + ["people", peopleRoutes, { name: "Ada" }], + ])("dispatches every %s and custom-field route", async (_name, app, createBody) => { + const id = faker.string.uuid(); + const cases: Array<[string, RequestInit | undefined]> = [ + ["/", undefined], + ["/custom-fields", undefined], + ["/custom-fields", jsonRequest("POST", { label: "Region", type: "text" })], + [`/custom-fields/${id}`, jsonRequest("PATCH", { label: "Territory" })], + [`/custom-fields/${id}`, { method: "DELETE" }], + [`/${id}`, undefined], + ["/", jsonRequest("POST", createBody)], + [`/${id}`, jsonRequest("PATCH", createBody)], + ["/bulk", jsonRequest("DELETE", { ids: [id] })], + [`/${id}`, { method: "DELETE" }], + ]; + + for (const [path, init] of cases) { + expect((await app.request(path, init)).status).toBe(200); + } + }); + + it("forwards GET and POST Better Auth requests", async () => { + expect((await authRoutes.request("/session")).status).toBe(200); + expect((await authRoutes.request("/sign-in", { method: "POST" })).status).toBe(200); + expect(controller.authHandler).toHaveBeenCalledTimes(2); + }); + + it("registers the complete API and constructs the server export", async () => { + const app = new Hono(); + registerRoutes(app); + + expect((await app.request("/health")).status).toBe(200); + expect(server.port).toBe(3001); + expect(server.fetch).toEqual(expect.any(Function)); + expect((await healthRoutes.request("/")).status).toBe(200); + }); +}); diff --git a/apps/api/test/tsconfig.json b/apps/api/test/tsconfig.json new file mode 100644 index 0000000..3fd724b --- /dev/null +++ b/apps/api/test/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": "..", + "types": ["bun", "vitest/globals"] + }, + "include": ["../src", "./**/*.ts", "../vitest.config.ts"] +} diff --git a/apps/api/vitest.config.ts b/apps/api/vitest.config.ts new file mode 100644 index 0000000..2bf82fd --- /dev/null +++ b/apps/api/vitest.config.ts @@ -0,0 +1,37 @@ +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + alias: { + "@": fileURLToPath(new URL("./src", import.meta.url)), + }, + }, + test: { + environment: "node", + include: ["test/**/*.test.ts"], + clearMocks: true, + restoreMocks: true, + coverage: { + provider: "v8", + include: [ + "src/config/**/*.ts", + "src/constants/**/*.ts", + "src/controllers/health.controller.ts", + "src/lib/{api-response,app-error,cors,date,email,get-token-from-url,http-handlers,workspace}.ts", + "src/middlewares/**/*.ts", + "src/routes/**/*.ts", + "src/server.ts", + ], + exclude: ["test/**"], + reporter: ["text", "json-summary", "html", "lcov"], + reportsDirectory: "coverage", + thresholds: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, + }, +}); diff --git a/apps/web/app/(crm)/layout.tsx b/apps/web/app/(crm)/layout.tsx index 4ee531e..5f151e3 100644 --- a/apps/web/app/(crm)/layout.tsx +++ b/apps/web/app/(crm)/layout.tsx @@ -1,7 +1,6 @@ import { SidebarProvider, SidebarInset, SidebarTrigger } from "@workspace/ui/components/ui/sidebar"; import { TooltipProvider } from "@workspace/ui/components/ui/tooltip"; import { AppSidebar } from "@/components/layout/app-sidebar"; -import { ThemeToggle } from "@workspace/ui/components/ui/theme-toggle"; import { CrmWorkspaceGate } from "@/components/onboarding/crm-workspace-gate"; import { CrmProductTour } from "@/components/onboarding/crm-product-tour"; @@ -13,10 +12,7 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
-
- - -
+
{children}
diff --git a/apps/web/app/(crm)/settings/layout.tsx b/apps/web/app/(crm)/settings/layout.tsx index 968714c..dfcc3f2 100644 --- a/apps/web/app/(crm)/settings/layout.tsx +++ b/apps/web/app/(crm)/settings/layout.tsx @@ -19,7 +19,7 @@ export default function SettingsLayout({ children }: { children: React.ReactNode const isCustomFieldsSubRoute = parentSegment === "custom-fields"; return ( -
+
{/* Header */} - +
+
+ +
+ +
{/* Content */} diff --git a/apps/web/components/workspace/settings/general-settings.tsx b/apps/web/components/workspace/settings/general-settings.tsx index ca9b829..e0e8e4e 100644 --- a/apps/web/components/workspace/settings/general-settings.tsx +++ b/apps/web/components/workspace/settings/general-settings.tsx @@ -29,6 +29,7 @@ import { } from "@/hooks/queries/use-workspace"; import { useAuthSession } from "@/hooks/queries/use-auth"; import { Logo } from "@workspace/ui/components/ui/logo"; +import { ThemeToggle } from "@workspace/ui/components/ui/theme-toggle"; import type { GeneralSettingsProps } from "@/types/workspace-settings"; type DialogState = "idle" | "editing" | "delete" | "leave"; @@ -169,6 +170,25 @@ export function GeneralSettings({ workspace }: GeneralSettingsProps) { +
+
+

Appearance

+

+ Choose how Stallion looks on this device. +

+
+ +
+
+

Color mode

+

Switch between light and dark mode.

+
+ +
+
+ + + {/* Danger zone */}
diff --git a/apps/web/package.json b/apps/web/package.json index bba79bb..b73c196 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -9,7 +9,10 @@ "start": "next start", "lint": "eslint .", "lint:fix": "eslint . --fix", - "check-types": "tsc --noEmit" + "check-types": "tsc --noEmit", + "test": "TZ=UTC vitest run", + "test:unit": "TZ=UTC vitest run", + "test:coverage": "TZ=UTC vitest run --coverage" }, "dependencies": { "@dnd-kit/core": "catalog:", @@ -42,15 +45,26 @@ "zustand": "catalog:" }, "devDependencies": { + "@faker-js/faker": "^10.0.0", "@tailwindcss/postcss": "^4.1.18", "@tanstack/react-query-devtools": "catalog:", + "@testing-library/dom": "^10.4.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", "@types/node": "^25.1.0", "@types/react": "^19.2.10", "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "@vitest/coverage-v8": "4.1.9", "@workspace/eslint-config": "workspace:^", "@workspace/typescript-config": "workspace:*", "eslint": "^9.39.2", + "jsdom": "^29.1.1", + "msw": "^2.14.6", "tailwindcss": "^4.2.1", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "4.1.9", + "vitest-axe": "^0.1.0" } } diff --git a/apps/web/test/lib/core.test.ts b/apps/web/test/lib/core.test.ts new file mode 100644 index 0000000..929c1a8 --- /dev/null +++ b/apps/web/test/lib/core.test.ts @@ -0,0 +1,101 @@ +import { faker } from "@faker-js/faker"; +import { AxiosError, AxiosHeaders } from "axios"; +import { beforeEach, describe, expect, it } from "vitest"; +import { formatColumnLabel, getColumnLabel, getVisiblePageNumbers } from "@/lib/data-table-utils"; +import { + isBetterAuthError, + isHandledAppError, + normalizeAppError, + toBetterAuthError, +} from "@/lib/error"; +import { getInitials, slugify } from "@/lib/utils"; +import { cleanQueryParams } from "@/services/crm/utils"; + +describe("web utilities", () => { + beforeEach(() => { + faker.seed(20260706); + }); + + it("formats labels, slugs, and initials", () => { + expect(formatColumnLabel("__lastContactedAt")).toBe("last Contacted At"); + expect(formatColumnLabel("owner-id")).toBe("owner id"); + expect(getColumnLabel("createdAt", "Created")).toBe("Created"); + expect(getColumnLabel("createdAt", null)).toBe("created At"); + expect(slugify(" Acmé Sales -- Team! ")).toBe("acm-sales-team"); + expect(getInitials("Ada Lovelace Byron")).toBe("AL"); + }); + + it("builds compact pagination ranges", () => { + expect(getVisiblePageNumbers(0, 0)).toEqual([]); + expect(getVisiblePageNumbers(0, 4)).toEqual([1, 2, 3, 4]); + expect(getVisiblePageNumbers(0, 10)).toEqual([1, 2, "ellipsis", 10]); + expect(getVisiblePageNumbers(4, 10)).toEqual([1, "ellipsis", 4, 5, 6, "ellipsis", 10]); + expect(getVisiblePageNumbers(9, 10)).toEqual([1, "ellipsis", 9, 10]); + }); + + it("removes only empty query values", () => { + expect( + cleanQueryParams({ + search: "", + ownerId: undefined, + stage: null, + page: 0, + enabled: false, + name: faker.company.name(), + }), + ).toEqual(expect.objectContaining({ page: 0, enabled: false })); + }); + + it("normalizes Better Auth and generic errors", () => { + const fallback = faker.lorem.sentence(); + expect(isBetterAuthError(null)).toBe(false); + expect(isBetterAuthError({})).toBe(false); + expect(isBetterAuthError({ code: "INVALID" })).toBe(true); + expect(toBetterAuthError(undefined, fallback)).toEqual({ message: fallback }); + expect(toBetterAuthError({ message: "specific", status: 400 }, fallback)).toEqual({ + message: "specific", + status: 400, + }); + expect(normalizeAppError({ status: 401, statusText: "Unauthorized" })).toEqual({ + code: undefined, + message: "Please sign in again", + status: 401, + }); + expect(normalizeAppError(new Error("broken"))).toEqual({ message: "broken" }); + expect(normalizeAppError("broken")).toEqual({ message: "An unexpected error occurred" }); + expect(isHandledAppError(new Error("handled"))).toBe(true); + expect(isHandledAppError(Symbol("unhandled"))).toBe(false); + }); + + it("normalizes API errors and friendly status messages", () => { + const createAxiosError = (status: number, data: unknown) => + new AxiosError("network message", "ERR_BAD_RESPONSE", undefined, undefined, { + status, + statusText: "Error", + headers: new AxiosHeaders(), + config: { headers: new AxiosHeaders() }, + data, + }); + + expect( + normalizeAppError( + createAxiosError(403, { + success: false, + error: { message: "raw", details: "owner role required" }, + }), + ), + ).toEqual({ + details: "owner role required", + message: "You don't have permission to do that", + status: 403, + }); + expect(normalizeAppError(createAxiosError(404, { message: "missing" })).message).toBe( + "Resource not found", + ); + expect(normalizeAppError(createAxiosError(500, {})).message).toBe( + "Something went wrong. Please try again.", + ); + expect(isBetterAuthError(createAxiosError(400, {}))).toBe(false); + expect(isHandledAppError(createAxiosError(400, {}))).toBe(true); + }); +}); diff --git a/apps/web/test/server.ts b/apps/web/test/server.ts new file mode 100644 index 0000000..bd0bda5 --- /dev/null +++ b/apps/web/test/server.ts @@ -0,0 +1,3 @@ +import { setupServer } from "msw/node"; + +export const server = setupServer(); diff --git a/apps/web/test/services/services.test.ts b/apps/web/test/services/services.test.ts new file mode 100644 index 0000000..e0b49f0 --- /dev/null +++ b/apps/web/test/services/services.test.ts @@ -0,0 +1,273 @@ +import { faker } from "@faker-js/faker"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => { + const fn = () => vi.fn(); + return { + api: { get: fn(), post: fn(), patch: fn(), delete: fn() }, + auth: { + getSession: fn(), + signIn: { email: fn() }, + signUp: { email: fn() }, + signOut: fn(), + requestPasswordReset: fn(), + resetPassword: fn(), + verifyEmail: fn(), + organization: { + create: fn(), + list: fn(), + getFullOrganization: fn(), + listMembers: fn(), + listInvitations: fn(), + update: fn(), + delete: fn(), + setActive: fn(), + removeMember: fn(), + updateMemberRole: fn(), + leave: fn(), + inviteMember: fn(), + cancelInvitation: fn(), + getInvitation: fn(), + acceptInvitation: fn(), + rejectInvitation: fn(), + }, + }, + }; +}); + +vi.mock("@/lib/axios-client", () => ({ apiClient: mocks.api })); +vi.mock("@/lib/auth-client", () => ({ authClient: mocks.auth })); + +import * as authService from "@/services/auth.service"; +import * as onboardingService from "@/services/onboarding.service"; +import * as workspaceService from "@/services/workspace.service"; +import * as analyticsService from "@/services/crm/analytics.service"; +import * as customFieldsService from "@/services/crm/custom-fields.service"; +import * as dealsService from "@/services/crm/deals.service"; +import * as orgService from "@/services/crm/org.service"; +import * as peopleService from "@/services/crm/people.service"; + +const success = (data: unknown = { id: "result" }) => ({ data, error: null }); +const failure = { data: null, error: { code: "FAILED", message: "failed", status: 400 } }; + +describe("web service contracts", () => { + beforeEach(() => { + faker.seed(20260706); + const envelope = { + data: { + data: { + id: "result", + customField: { id: "field" }, + customFields: [{ id: "field" }], + deal: { id: "deal" }, + deals: [], + org: { id: "org" }, + people: [], + person: { id: "person" }, + pipeline: [], + peopleStatus: [], + won: 1, + lost: 1, + total: 2, + rate: 50, + deleted: 2, + crmTourCompleted: true, + crmTourCompletedAt: null, + }, + }, + }; + Object.values(mocks.api).forEach((method) => method.mockResolvedValue(envelope)); + const authMethods = [ + mocks.auth.getSession, + mocks.auth.signIn.email, + mocks.auth.signUp.email, + mocks.auth.signOut, + mocks.auth.requestPasswordReset, + mocks.auth.resetPassword, + mocks.auth.verifyEmail, + ...Object.values(mocks.auth.organization), + ]; + authMethods.forEach((method) => method.mockResolvedValue(success())); + }); + + it("maps all HTTP CRM and onboarding endpoints", async () => { + const id = faker.string.uuid(); + await onboardingService.getOnboardingStatus(); + await onboardingService.completeCrmTour(); + await analyticsService.getPipelineByStage(); + await analyticsService.getPeopleByStatus(); + await analyticsService.getWinRate(); + await customFieldsService.listPeopleCustomFields(); + await customFieldsService.listOrgCustomFields(); + await customFieldsService.createPeopleCustomField({ label: "Region", type: "text" }); + await customFieldsService.createOrgCustomField({ label: "Region", type: "text" }); + await customFieldsService.updatePeopleCustomField(id, { label: "Area" }); + await customFieldsService.updateOrgCustomField(id, { label: "Area" }); + await customFieldsService.deletePeopleCustomField(id); + await customFieldsService.deleteOrgCustomField(id); + await dealsService.listDeals(); + await dealsService.listDeals({ page: 2 }); + await dealsService.getDeal(id); + await dealsService.createDeal({ title: "Deal" }); + await dealsService.updateDeal(id, { stage: "won" }); + await dealsService.deleteDeal(id); + await orgService.listOrganizations(); + await orgService.listOrganizations({ search: "acme" }); + await orgService.getOrganization(id); + await orgService.createOrganization({ name: "Acme" }); + await orgService.updateOrganization(id, { name: "Acme 2" }); + await orgService.deleteOrganization(id); + await orgService.bulkDeleteOrganizations({ ids: [id] }); + await peopleService.listPeople(); + await peopleService.listPeople({ status: "lead" }); + await peopleService.getPerson(id); + await peopleService.createPerson({ name: "Ada" }); + await peopleService.updatePerson(id, { status: "customer" }); + await peopleService.deletePerson(id); + await peopleService.bulkDeletePeople({ ids: [id] }); + + expect(mocks.api.get).toHaveBeenCalled(); + expect(mocks.api.post).toHaveBeenCalled(); + expect(mocks.api.patch).toHaveBeenCalled(); + expect(mocks.api.delete).toHaveBeenCalled(); + }); + + it("maps successful authentication operations", async () => { + const credentials = { email: faker.internet.email(), password: "secret1" }; + await expect(authService.getAuthSession()).resolves.toEqual({ id: "result" }); + await authService.signInWithEmail(credentials); + await authService.signUpWithEmail({ ...credentials, name: faker.person.fullName() }); + await authService.signOutCurrentSession(); + await authService.requestPasswordResetLink({ email: credentials.email }); + await authService.resetPasswordWithToken({ password: "secret2", token: "token" }); + await authService.verifyEmailToken("token"); + + expect(mocks.auth.signIn.email).toHaveBeenCalledWith({ ...credentials, rememberMe: true }); + }); + + it("treats missing and unauthorized sessions as signed out", async () => { + mocks.auth.getSession + .mockResolvedValueOnce({ data: undefined, error: null }) + .mockResolvedValueOnce({ data: null, error: { status: 401 } }); + + await expect(authService.getAuthSession()).resolves.toBeNull(); + await expect(authService.getAuthSession()).resolves.toBeNull(); + }); + + it("surfaces every authentication operation failure", async () => { + const calls = [ + [mocks.auth.getSession, () => authService.getAuthSession()], + [ + mocks.auth.signIn.email, + () => authService.signInWithEmail({ email: "a@b.co", password: "x" }), + ], + [ + mocks.auth.signUp.email, + () => authService.signUpWithEmail({ name: "A", email: "a@b.co", password: "x" }), + ], + [mocks.auth.signOut, () => authService.signOutCurrentSession()], + [ + mocks.auth.requestPasswordReset, + () => authService.requestPasswordResetLink({ email: "a@b.co" }), + ], + [ + mocks.auth.resetPassword, + () => authService.resetPasswordWithToken({ password: "x", token: "token" }), + ], + [mocks.auth.verifyEmail, () => authService.verifyEmailToken("token")], + ] as const; + + for (const [method, call] of calls) { + method.mockResolvedValueOnce(failure); + await expect(call()).rejects.toMatchObject({ message: "failed" }); + } + }); + + it("maps workspace operations and response variants", async () => { + const id = faker.string.uuid(); + await workspaceService.createWorkspace({ name: "Sales" }); + await workspaceService.createWorkspace({ + name: "Sales", + slug: "sales", + logo: "logo", + metadata: { tier: "pro" }, + }); + await workspaceService.listWorkspaces(); + await workspaceService.getFullWorkspace(); + await workspaceService.getFullWorkspace({ organizationId: id }); + + mocks.auth.organization.listMembers + .mockResolvedValueOnce(success([{ id: "member" }])) + .mockResolvedValueOnce(success({ members: [{ id: "member" }] })) + .mockResolvedValueOnce(success(null)); + await workspaceService.listWorkspaceMembers(); + await workspaceService.listWorkspaceMembers({ organizationId: id }); + await workspaceService.listWorkspaceMembers(); + + mocks.auth.organization.listInvitations + .mockResolvedValueOnce(success([{ id: "invite" }])) + .mockResolvedValueOnce(success({ invitations: [{ id: "invite" }] })) + .mockResolvedValueOnce(success(null)); + await workspaceService.listWorkspaceInvitations(); + await workspaceService.listWorkspaceInvitations({ organizationId: id }); + await workspaceService.listWorkspaceInvitations(); + + await workspaceService.updateWorkspace(id, { name: "New" }); + await workspaceService.deleteWorkspace(id); + await workspaceService.setActiveWorkspace(); + await workspaceService.setActiveWorkspace({ organizationId: id }); + await workspaceService.removeMember("member"); + await workspaceService.removeMember("member", id); + await workspaceService.updateMemberRole("member", "admin"); + await workspaceService.updateMemberRole("member", "admin", id); + await workspaceService.leaveWorkspace(id); + await workspaceService.inviteMember({ email: "a@b.co", role: "member" }); + await workspaceService.inviteMember({ + email: "a@b.co", + role: "member", + organizationId: id, + resend: true, + }); + await workspaceService.cancelInvitation(id); + await workspaceService.getInvitation(id); + await workspaceService.acceptInvitation(id); + await workspaceService.rejectInvitation(id); + + expect(mocks.auth.organization.create).toHaveBeenCalledWith( + expect.objectContaining({ slug: "sales" }), + ); + }); + + it("surfaces every workspace operation failure", async () => { + const id = faker.string.uuid(); + const calls = [ + [mocks.auth.organization.create, () => workspaceService.createWorkspace({ name: "Sales" })], + [mocks.auth.organization.list, () => workspaceService.listWorkspaces()], + [mocks.auth.organization.getFullOrganization, () => workspaceService.getFullWorkspace()], + [mocks.auth.organization.listMembers, () => workspaceService.listWorkspaceMembers()], + [mocks.auth.organization.listInvitations, () => workspaceService.listWorkspaceInvitations()], + [mocks.auth.organization.update, () => workspaceService.updateWorkspace(id, {})], + [mocks.auth.organization.delete, () => workspaceService.deleteWorkspace(id)], + [mocks.auth.organization.setActive, () => workspaceService.setActiveWorkspace()], + [mocks.auth.organization.removeMember, () => workspaceService.removeMember("member")], + [ + mocks.auth.organization.updateMemberRole, + () => workspaceService.updateMemberRole("member", "admin"), + ], + [mocks.auth.organization.leave, () => workspaceService.leaveWorkspace(id)], + [ + mocks.auth.organization.inviteMember, + () => workspaceService.inviteMember({ email: "a@b.co", role: "member" }), + ], + [mocks.auth.organization.cancelInvitation, () => workspaceService.cancelInvitation(id)], + [mocks.auth.organization.getInvitation, () => workspaceService.getInvitation(id)], + [mocks.auth.organization.acceptInvitation, () => workspaceService.acceptInvitation(id)], + [mocks.auth.organization.rejectInvitation, () => workspaceService.rejectInvitation(id)], + ] as const; + + for (const [method, call] of calls) { + method.mockResolvedValueOnce(failure); + await expect(call()).rejects.toMatchObject({ message: "failed" }); + } + }); +}); diff --git a/apps/web/test/setup.ts b/apps/web/test/setup.ts new file mode 100644 index 0000000..e75f013 --- /dev/null +++ b/apps/web/test/setup.ts @@ -0,0 +1,34 @@ +import "@testing-library/jest-dom/vitest"; +import { cleanup } from "@testing-library/react"; +import { afterAll, afterEach, beforeAll, vi } from "vitest"; +import { server } from "./server"; + +beforeAll(() => server.listen({ onUnhandledRequest: "error" })); + +afterEach(() => { + cleanup(); + server.resetHandlers(); + vi.useRealTimers(); +}); + +afterAll(() => server.close()); + +Object.defineProperty(window, "matchMedia", { + writable: true, + value: vi.fn().mockImplementation((query: string) => ({ + matches: false, + media: query, + onchange: null, + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })), +}); + +class ResizeObserverMock { + observe = vi.fn(); + unobserve = vi.fn(); + disconnect = vi.fn(); +} + +vi.stubGlobal("ResizeObserver", ResizeObserverMock); diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts new file mode 100644 index 0000000..2ea4e4c --- /dev/null +++ b/apps/web/vitest.config.ts @@ -0,0 +1,41 @@ +import { fileURLToPath } from "node:url"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + "@": fileURLToPath(new URL(".", import.meta.url)), + }, + }, + test: { + environment: "jsdom", + include: ["test/**/*.test.{ts,tsx}"], + setupFiles: ["./test/setup.ts"], + clearMocks: true, + restoreMocks: true, + coverage: { + provider: "v8", + include: [ + "app/**/*.{ts,tsx}", + "components/**/*.{ts,tsx}", + "constants/**/*.ts", + "hooks/**/*.{ts,tsx}", + "lib/**/*.{ts,tsx}", + "services/**/*.ts", + "store/**/*.ts", + "proxy.ts", + ], + exclude: ["test/**", "**/*.d.ts", "types/**"], + reporter: ["text", "json-summary", "html", "lcov"], + reportsDirectory: "coverage", + thresholds: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, + }, +}); diff --git a/package.json b/package.json index feae9ff..f8aa8a2 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,9 @@ "dev": "turbo run dev", "lint": "turbo run lint", "check-types": "turbo run check-types", + "test": "turbo run test", + "test:unit": "turbo run test:unit", + "test:coverage": "turbo run test:coverage", "format": "prettier --write .", "format:check": "prettier --check .", "docker:up": "docker-compose up -d", diff --git a/packages/ui/src/components/ui/sidebar.tsx b/packages/ui/src/components/ui/sidebar.tsx index 75e3741..fc42ca0 100644 --- a/packages/ui/src/components/ui/sidebar.tsx +++ b/packages/ui/src/components/ui/sidebar.tsx @@ -12,7 +12,7 @@ import { Separator } from "./separator"; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "./sheet"; import { Skeleton } from "./skeleton"; import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip"; -import { PanelLeftIcon } from "lucide-react"; +import { ChevronsLeft, ChevronsRight } from "lucide-react"; const SIDEBAR_COOKIE_NAME = "sidebar_state"; const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; @@ -237,8 +237,14 @@ function Sidebar({ ); } -function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps) { - const { toggleSidebar } = useSidebar(); +function SidebarTrigger({ + className, + onClick, + ...props +}: React.ComponentProps) { + const { state, toggleSidebar } = useSidebar(); + const isExpanded = state === "expanded"; + const label = isExpanded ? "Collapse sidebar" : "Expand sidebar"; return ( ); } diff --git a/packages/ui/src/components/ui/theme-toggle.tsx b/packages/ui/src/components/ui/theme-toggle.tsx index af5bb1a..b794420 100644 --- a/packages/ui/src/components/ui/theme-toggle.tsx +++ b/packages/ui/src/components/ui/theme-toggle.tsx @@ -4,20 +4,29 @@ import { useTheme } from "next-themes"; import { Sun, Moon } from "lucide-react"; import { Button } from "./button"; -export function ThemeToggle() { +export function getNextTheme(resolvedTheme: string | undefined) { + return resolvedTheme === "dark" ? "light" : "dark"; +} + +export function ThemeToggle({ showLabel = false }: { showLabel?: boolean }) { const { resolvedTheme, setTheme } = useTheme(); + const nextTheme = getNextTheme(resolvedTheme); + const label = `Switch to ${nextTheme} mode`; return ( ); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c730025..a42e891 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,7 +115,7 @@ importers: version: link:../../packages/validators better-auth: specifier: 'catalog:' - version: 1.4.19(drizzle-kit@0.31.9)(drizzle-orm@0.44.7(bun-types@1.3.9)(kysely@0.28.11)(postgres@3.4.8))(next@16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.4.19(drizzle-kit@0.31.9)(drizzle-orm@0.44.7(bun-types@1.3.9)(kysely@0.28.11)(postgres@3.4.8))(next@16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.9) drizzle-orm: specifier: ^0.44.2 version: 0.44.7(bun-types@1.3.9)(kysely@0.28.11)(postgres@3.4.8) @@ -138,9 +138,15 @@ importers: specifier: 'catalog:' version: 3.25.76 devDependencies: + '@faker-js/faker': + specifier: ^10.0.0 + version: 10.5.0 '@types/bun': specifier: ^1.2.14 version: 1.3.9 + '@vitest/coverage-v8': + specifier: 4.1.9 + version: 4.1.9(vitest@4.1.9) '@workspace/eslint-config': specifier: workspace:* version: link:../../packages/eslint-config @@ -159,6 +165,9 @@ importers: typescript: specifier: 'catalog:' version: 5.9.3 + vitest: + specifier: 4.1.9 + version: 4.1.9(@types/node@25.3.0)(@vitest/coverage-v8@4.1.9)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@25.3.0)(typescript@5.9.3))(vite@8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1)) apps/web: dependencies: @@ -194,7 +203,7 @@ importers: version: 1.13.6 better-auth: specifier: 'catalog:' - version: 1.4.19(drizzle-kit@0.31.9)(drizzle-orm@0.44.7(bun-types@1.3.9)(kysely@0.28.11)(postgres@3.4.8))(next@16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.4.19(drizzle-kit@0.31.9)(drizzle-orm@0.44.7(bun-types@1.3.9)(kysely@0.28.11)(postgres@3.4.8))(next@16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.9) class-variance-authority: specifier: 'catalog:' version: 0.7.1 @@ -247,12 +256,27 @@ importers: specifier: 'catalog:' version: 5.0.11(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) devDependencies: + '@faker-js/faker': + specifier: ^10.0.0 + version: 10.5.0 '@tailwindcss/postcss': specifier: ^4.1.18 version: 4.2.1 '@tanstack/react-query-devtools': specifier: 'catalog:' version: 5.91.3(@tanstack/react-query@5.90.21(react@19.2.4))(react@19.2.4) + '@testing-library/dom': + specifier: ^10.4.1 + version: 10.4.1 + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@testing-library/user-event': + specifier: ^14.6.1 + version: 14.6.1(@testing-library/dom@10.4.1) '@types/node': specifier: ^25.1.0 version: 25.3.0 @@ -262,6 +286,12 @@ importers: '@types/react-dom': specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^6.0.3 + version: 6.0.3(vite@8.1.3(@types/node@25.3.0)(jiti@2.6.1)) + '@vitest/coverage-v8': + specifier: 4.1.9 + version: 4.1.9(vitest@4.1.9) '@workspace/eslint-config': specifier: workspace:^ version: link:../../packages/eslint-config @@ -271,12 +301,24 @@ importers: eslint: specifier: ^9.39.2 version: 9.39.3(jiti@2.6.1) + jsdom: + specifier: ^29.1.1 + version: 29.1.1(@noble/hashes@2.0.1) + msw: + specifier: ^2.14.6 + version: 2.14.6(@types/node@25.3.0)(typescript@5.9.3) tailwindcss: specifier: ^4.2.1 version: 4.2.1 typescript: specifier: 'catalog:' version: 5.9.3 + vitest: + specifier: 4.1.9 + version: 4.1.9(@types/node@25.3.0)(@vitest/coverage-v8@4.1.9)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@25.3.0)(typescript@5.9.3))(vite@8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1)) + vitest-axe: + specifier: ^0.1.0 + version: 0.1.0(vitest@4.1.9) packages/eslint-config: devDependencies: @@ -440,10 +482,28 @@ importers: packages: + '@adobe/css-tools@4.5.0': + resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} + '@asamuzakjp/css-color@5.1.11': + resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@7.1.1': + resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/generational-cache@1.0.1': + resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -482,8 +542,12 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.27.1': @@ -499,6 +563,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.28.6': resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} @@ -515,6 +584,14 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@better-auth/core@1.4.19': resolution: {integrity: sha512-uADLHG1jc5BnEJi7f6ijUN5DmPPRSj++7m/G19z3UqA3MVCo4Y4t1MMa4IIxLCqGDFv22drdfxescgW+HnIowA==} peerDependencies: @@ -536,6 +613,46 @@ packages: '@better-fetch/fetch@1.1.21': resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==} + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + hasBin: true + + '@csstools/color-helpers@6.1.0': + resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.2.1': + resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-color-parser@4.1.9': + resolution: {integrity: sha512-paQcIaOO53Rk5+YrBaBjm/SgrV4INImjo2BT1DtQRYr+XeTRbeAYlS+jxXp9drqvKmtFnWRJKIalDLhZZDu42A==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.6': + resolution: {integrity: sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + '@dnd-kit/accessibility@3.1.1': resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} peerDependencies: @@ -561,9 +678,18 @@ packages: '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@emnapi/runtime@1.7.1': resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} deprecated: 'Merged into tsx: https://tsx.is' @@ -904,6 +1030,19 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@exodus/bytes@1.15.1': + resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + + '@faker-js/faker@10.5.0': + resolution: {integrity: sha512-bsxD8WLS5lIj7aaoCx1YJkktqYj5vlBUE6HWzu2Q51ksrGJ0H737ECCKlFU7Yf8Br45z9t99frBp/J7kzbMPAg==} + engines: {node: ^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0, npm: '>=10'} + '@fastify/deepmerge@3.2.1': resolution: {integrity: sha512-N5Oqvltoa2r9z1tbx4xjky0oRR60v+T47Ic4J1ukoVQcptLOrIdRnCSdTGmOmajZuHVKlTnfcmrjyqsGEW1ztA==} @@ -1118,6 +1257,41 @@ packages: cpu: [x64] os: [win32] + '@inquirer/ansi@2.0.7': + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/confirm@6.1.1': + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@11.2.1': + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.7': + resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/type@4.0.7': + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1134,6 +1308,16 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@mswjs/interceptors@0.41.9': + resolution: {integrity: sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==} + engines: {node: '>=18'} + + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + '@next/env@16.1.6': resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==} @@ -1208,6 +1392,21 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/deferred-promise@3.0.0': + resolution: {integrity: sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + + '@oxc-project/types@0.138.0': + resolution: {integrity: sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==} + '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -1908,6 +2107,98 @@ packages: react: ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^18.0 || ^19.0 || ^19.0.0-rc + '@rolldown/binding-android-arm64@1.1.4': + resolution: {integrity: sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.1.4': + resolution: {integrity: sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.4': + resolution: {integrity: sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.1.4': + resolution: {integrity: sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.1.4': + resolution: {integrity: sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.1.4': + resolution: {integrity: sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.1.4': + resolution: {integrity: sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-ppc64-gnu@1.1.4': + resolution: {integrity: sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@rolldown/binding-linux-s390x-gnu@1.1.4': + resolution: {integrity: sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.1.4': + resolution: {integrity: sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.1.4': + resolution: {integrity: sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.1.4': + resolution: {integrity: sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.1.4': + resolution: {integrity: sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.1.4': + resolution: {integrity: sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.1.4': + resolution: {integrity: sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} @@ -2036,9 +2327,47 @@ packages: resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} engines: {node: '>=12'} + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/bun@1.3.9': resolution: {integrity: sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} @@ -2066,6 +2395,9 @@ packages: '@types/d3-timer@3.0.2': resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -2083,6 +2415,12 @@ packages: '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/set-cookie-parser@2.4.10': + resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} + + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + '@typescript-eslint/eslint-plugin@8.56.1': resolution: {integrity: sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2142,6 +2480,57 @@ packages: resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitejs/plugin-react@6.0.3': + resolution: {integrity: sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + + '@vitest/coverage-v8@4.1.9': + resolution: {integrity: sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==} + peerDependencies: + '@vitest/browser': 4.1.9 + vitest: 4.1.9 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@4.1.9': + resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} + + '@vitest/mocker@4.1.9': + resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.9': + resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} + + '@vitest/runner@4.1.9': + resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} + + '@vitest/snapshot@4.1.9': + resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} + + '@vitest/spy@4.1.9': + resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} + + '@vitest/utils@4.1.9': + resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2155,10 +2544,18 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2166,6 +2563,13 @@ packages: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.2: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} @@ -2194,6 +2598,13 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-v8-to-istanbul@1.0.4: + resolution: {integrity: sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==} + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -2209,6 +2620,10 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + axe-core@4.12.1: + resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==} + engines: {node: '>=4'} + axios@1.13.6: resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} @@ -2294,6 +2709,9 @@ packages: zod: optional: true + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -2335,16 +2753,32 @@ packages: caniuse-lite@1.0.30001760: resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -2375,10 +2809,21 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -2426,6 +2871,10 @@ packages: resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} engines: {node: '>=12'} + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -2456,6 +2905,9 @@ packages: decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -2478,6 +2930,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -2489,6 +2945,12 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} @@ -2616,6 +3078,9 @@ packages: electron-to-chromium@1.5.302: resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} @@ -2627,6 +3092,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + es-abstract@1.24.0: resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} @@ -2643,6 +3112,9 @@ packages: resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} + es-module-lexer@2.3.0: + resolution: {integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -2752,6 +3224,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -2759,6 +3234,10 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + fast-copy@4.0.2: resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} @@ -2785,6 +3264,15 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -2833,6 +3321,11 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -2847,6 +3340,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -2893,6 +3390,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphql@16.14.2: + resolution: {integrity: sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + gsap@3.15.0: resolution: {integrity: sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==} @@ -2923,6 +3424,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + headers-polyfill@5.0.1: + resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==} + help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} @@ -2936,6 +3440,13 @@ packages: resolution: {integrity: sha512-gJnaDHXKDayjt8ue0n8Gs0A007yKXj4Xzb8+cNjZeYsSzzwKc0Lr+OZgYwVfB0pHfUs17EPoLvrOsEaJ9mj+Tg==} engines: {node: '>=16.9.0'} + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-to-text@9.0.5: resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} engines: {node: '>=14'} @@ -2962,6 +3473,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -3010,6 +3525,10 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-generator-function@1.1.0: resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} engines: {node: '>= 0.4'} @@ -3029,6 +3548,9 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-number-object@1.1.1: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} @@ -3037,6 +3559,9 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -3079,6 +3604,18 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} @@ -3094,6 +3631,9 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3101,6 +3641,15 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + jsdom@29.1.1: + resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -3144,74 +3693,147 @@ packages: cpu: [arm64] os: [android] + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + lightningcss-darwin-arm64@1.31.1: resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + lightningcss-darwin-x64@1.31.1: resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + lightningcss-freebsd-x64@1.31.1: resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + lightningcss-linux-arm-gnueabihf@1.31.1: resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + lightningcss-linux-arm64-gnu@1.31.1: resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + lightningcss-linux-arm64-musl@1.31.1: resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + lightningcss-linux-x64-gnu@1.31.1: resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + lightningcss-linux-x64-musl@1.31.1: resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + lightningcss-win32-arm64-msvc@1.31.1: resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + lightningcss-win32-x64-msvc@1.31.1: resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.31.1: - resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} - + cpu: [x64] + os: [win32] + + lightningcss@1.31.1: + resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} + engines: {node: '>= 12.0.0'} + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -3225,6 +3847,10 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -3233,13 +3859,27 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -3256,6 +3896,10 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + minimatch@10.2.3: resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==} engines: {node: 18 || 20 || >=22} @@ -3269,11 +3913,30 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msw@2.14.6: + resolution: {integrity: sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.15: + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanostores@1.1.0: resolution: {integrity: sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA==} engines: {node: ^20.0.0 || >=22.0.0} @@ -3339,6 +4002,10 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + engines: {node: '>=12.20.0'} + on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} @@ -3350,6 +4017,9 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} @@ -3366,6 +4036,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + parseley@0.12.1: resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} @@ -3380,6 +4053,12 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} @@ -3390,8 +4069,8 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} pino-abstract-transport@2.0.0: @@ -3419,6 +4098,10 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.16: + resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -3436,6 +4119,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} @@ -3491,6 +4178,9 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -3563,6 +4253,10 @@ packages: react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -3571,6 +4265,14 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resend@4.8.0: resolution: {integrity: sha512-R8eBOFQDO6dzRTDmaMEdpqrkmgSjPpVXt4nGfWsZdYOet0kqra0xgbvTES6HmCriZEXbmGk3e0DiGIaLFTFSHA==} engines: {node: '>=18'} @@ -3586,10 +4288,18 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + rettime@0.11.11: + resolution: {integrity: sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rolldown@1.1.4: + resolution: {integrity: sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rou3@0.7.12: resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==} @@ -3612,6 +4322,10 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -3639,6 +4353,9 @@ packages: set-cookie-parser@2.7.2: resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + set-cookie-parser@3.1.1: + resolution: {integrity: sha512-vM9SUhjsUYs6UeJUmygc5Ofm5eQGe85riob5ju6XCgFGJI5PLV4nrDAQpQjd+LkFBpAkADn5BQQpZ9EUNkyLuA==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -3679,6 +4396,13 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -3703,10 +4427,27 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} @@ -3726,6 +4467,14 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -3755,6 +4504,13 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + tailwind-merge@3.5.0: resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} @@ -3771,14 +4527,40 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + + tldts-core@7.4.6: + resolution: {integrity: sha512-TkQNGJIhlEphpHCjKodMTSe23egUZr/g+flI2qkLgiJ/maAzSgXypSLRTNH3nCmqgayEmtcJBiLcfODSAr1xoA==} + + tldts@7.4.6: + resolution: {integrity: sha512-rbP0Gyx8b3Ae9yO//CU2wbSnQNoQ66m1nJdSbSHmnwKwzkkz/u8mERYU8T2rmlmy+bJvRNn84yNCW8gYqox44Q==} + hasBin: true + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + engines: {node: '>=16'} + + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -3833,6 +4615,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-fest@5.8.0: + resolution: {integrity: sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==} + engines: {node: '>=20'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -3868,6 +4654,13 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici@7.28.0: + resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} + engines: {node: '>=20.18.1'} + + until-async@3.0.2: + resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -3911,6 +4704,111 @@ packages: victory-vendor@36.9.2: resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + vite@8.1.3: + resolution: {integrity: sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest-axe@0.1.0: + resolution: {integrity: sha512-jvtXxeQPg8R/2ANTY8QicA5pvvdRP4F0FsVUAHANJ46YCDASie/cuhlSzu0DGcLmZvGBSBNsNuK3HqfaeknyvA==} + peerDependencies: + vitest: '>=0.16.0' + + vitest@4.1.9: + resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.9 + '@vitest/browser-preview': 4.1.9 + '@vitest/browser-webdriverio': 4.1.9 + '@vitest/coverage-istanbul': 4.1.9 + '@vitest/coverage-v8': 4.1.9 + '@vitest/ui': 4.1.9 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -3932,16 +4830,44 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3978,11 +4904,33 @@ packages: snapshots: + '@adobe/css-tools@4.5.0': {} + '@alloc/quick-lru@5.2.0': {} + '@asamuzakjp/css-color@5.1.11': + dependencies: + '@asamuzakjp/generational-cache': 1.0.1 + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@asamuzakjp/dom-selector@7.1.1': + dependencies: + '@asamuzakjp/generational-cache': 1.0.1 + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + + '@asamuzakjp/generational-cache@1.0.1': {} + + '@asamuzakjp/nwsapi@2.3.9': {} + '@babel/code-frame@7.29.0': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 @@ -4010,8 +4958,8 @@ snapshots: '@babel/generator@7.29.1': dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 @@ -4029,7 +4977,7 @@ snapshots: '@babel/helper-module-imports@7.28.6': dependencies: '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color @@ -4037,42 +4985,48 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.28.6': dependencies: '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 '@babel/parser@7.29.0': dependencies: '@babel/types': 7.29.0 + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + '@babel/runtime@7.28.6': {} '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@babel/traverse@7.29.0': dependencies: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.7 '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -4080,7 +5034,14 @@ snapshots: '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bcoe/v8-coverage@1.0.2': {} '@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)': dependencies: @@ -4103,6 +5064,34 @@ snapshots: '@better-fetch/fetch@1.1.21': {} + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.2.1 + + '@csstools/color-helpers@6.1.0': {} + + '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-color-parser@4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/color-helpers': 6.1.0 + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.6(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.0': {} + '@dnd-kit/accessibility@3.1.1(react@19.2.4)': dependencies: react: 19.2.4 @@ -4130,11 +5119,27 @@ snapshots: '@drizzle-team/brocli@0.10.2': {} + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild-kit/core-utils@3.3.2': dependencies: esbuild: 0.18.20 @@ -4340,6 +5345,12 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@exodus/bytes@1.15.1(@noble/hashes@2.0.1)': + optionalDependencies: + '@noble/hashes': 2.0.1 + + '@faker-js/faker@10.5.0': {} + '@fastify/deepmerge@3.2.1': {} '@floating-ui/core@1.7.4': @@ -4510,6 +5521,33 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true + '@inquirer/ansi@2.0.7': {} + + '@inquirer/confirm@6.1.1(@types/node@25.3.0)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@25.3.0) + '@inquirer/type': 4.0.7(@types/node@25.3.0) + optionalDependencies: + '@types/node': 25.3.0 + + '@inquirer/core@11.2.1(@types/node@25.3.0)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.3.0) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 25.3.0 + + '@inquirer/figures@2.0.7': {} + + '@inquirer/type@4.0.7(@types/node@25.3.0)': + optionalDependencies: + '@types/node': 25.3.0 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4529,6 +5567,22 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@mswjs/interceptors@0.41.9': + 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 + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + '@next/env@16.1.6': {} '@next/eslint-plugin-next@16.1.6': @@ -4575,6 +5629,19 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/deferred-promise@3.0.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + + '@oxc-project/types@0.138.0': {} + '@pinojs/redact@0.4.0': {} '@radix-ui/number@1.1.1': {} @@ -5332,6 +6399,57 @@ snapshots: react-dom: 19.2.4(react@19.2.4) react-promise-suspense: 0.3.4 + '@rolldown/binding-android-arm64@1.1.4': + optional: true + + '@rolldown/binding-darwin-arm64@1.1.4': + optional: true + + '@rolldown/binding-darwin-x64@1.1.4': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.4': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.4': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.4': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.1.4': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.4': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.4': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.1.4': + optional: true + + '@rolldown/binding-linux-x64-musl@1.1.4': + optional: true + + '@rolldown/binding-openharmony-arm64@1.1.4': + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.4': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.4': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.1.4': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + '@selderee/plugin-htmlparser2@0.11.0': dependencies: domhandler: 5.0.3 @@ -5437,10 +6555,56 @@ snapshots: '@tanstack/table-core@8.21.3': {} + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.28.6 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.5.0 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@babel/runtime': 7.28.6 + '@testing-library/dom': 10.4.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/aria-query@5.0.4': {} + '@types/bun@1.3.9': dependencies: bun-types: 1.3.9 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + '@types/d3-array@3.2.2': {} '@types/d3-color@3.1.3': {} @@ -5465,6 +6629,8 @@ snapshots: '@types/d3-timer@3.0.2': {} + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -5481,6 +6647,12 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/set-cookie-parser@2.4.10': + dependencies: + '@types/node': 25.3.0 + + '@types/statuses@2.0.6': {} + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -5550,7 +6722,7 @@ snapshots: debug: 4.4.3 minimatch: 10.2.3 semver: 7.7.3 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -5572,6 +6744,67 @@ snapshots: '@typescript-eslint/types': 8.56.1 eslint-visitor-keys: 5.0.1 + '@vitejs/plugin-react@6.0.3(vite@8.1.3(@types/node@25.3.0)(jiti@2.6.1))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1) + + '@vitest/coverage-v8@4.1.9(vitest@4.1.9)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.9 + ast-v8-to-istanbul: 1.0.4 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.3 + obug: 2.1.3 + std-env: 4.1.0 + tinyrainbow: 3.1.0 + vitest: 4.1.9(@types/node@25.3.0)(@vitest/coverage-v8@4.1.9)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@25.3.0)(typescript@5.9.3))(vite@8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1)) + + '@vitest/expect@4.1.9': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.9(msw@2.14.6(@types/node@25.3.0)(typescript@5.9.3))(vite@8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1))': + dependencies: + '@vitest/spy': 4.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.14.6(@types/node@25.3.0)(typescript@5.9.3) + vite: 8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1) + + '@vitest/pretty-format@4.1.9': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.9': + dependencies: + '@vitest/utils': 4.1.9 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + '@vitest/utils': 4.1.9 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.9': {} + + '@vitest/utils@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -5585,16 +6818,26 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-regex@5.0.1: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + argparse@2.0.1: {} aria-hidden@1.2.6: dependencies: tslib: 2.8.1 + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + array-buffer-byte-length@1.0.2: dependencies: call-bound: 1.0.4 @@ -5652,6 +6895,14 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + assertion-error@2.0.1: {} + + ast-v8-to-istanbul@1.0.4: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + async-function@1.0.0: {} asynckit@0.4.0: {} @@ -5662,6 +6913,8 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + axe-core@4.12.1: {} + axios@1.13.6: dependencies: follow-redirects: 1.15.11 @@ -5676,7 +6929,7 @@ snapshots: baseline-browser-mapping@2.10.0: {} - better-auth@1.4.19(drizzle-kit@0.31.9)(drizzle-orm@0.44.7(bun-types@1.3.9)(kysely@0.28.11)(postgres@3.4.8))(next@16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + better-auth@1.4.19(drizzle-kit@0.31.9)(drizzle-orm@0.44.7(bun-types@1.3.9)(kysely@0.28.11)(postgres@3.4.8))(next@16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.9): dependencies: '@better-auth/core': 1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0) '@better-auth/telemetry': 1.4.19(@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)) @@ -5696,6 +6949,7 @@ snapshots: next: 16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + vitest: 4.1.9(@types/node@25.3.0)(@vitest/coverage-v8@4.1.9)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@25.3.0)(typescript@5.9.3))(vite@8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1)) better-call@1.1.8(zod@4.3.6): dependencies: @@ -5706,6 +6960,10 @@ snapshots: optionalDependencies: zod: 4.3.6 + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -5754,17 +7012,29 @@ snapshots: caniuse-lite@1.0.30001760: {} + chai@6.2.2: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 + cli-width@4.1.0: {} + client-only@0.0.1: {} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clsx@2.1.1: {} cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): @@ -5795,12 +7065,21 @@ snapshots: convert-source-map@2.0.0: {} + cookie@1.1.1: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + css.escape@1.5.1: {} + csstype@3.2.3: {} d3-array@3.2.4: @@ -5841,6 +7120,13 @@ snapshots: d3-timer@3.0.1: {} + data-urls@7.0.0(@noble/hashes@2.0.1): + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1(@noble/hashes@2.0.1) + transitivePeerDependencies: + - '@noble/hashes' + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -5869,6 +7155,8 @@ snapshots: decimal.js-light@2.5.1: {} + decimal.js@10.6.0: {} + deep-is@0.1.4: {} deepmerge@4.3.1: {} @@ -5889,6 +7177,8 @@ snapshots: delayed-stream@1.0.0: {} + dequal@2.0.3: {} + detect-libc@2.1.2: {} detect-node-es@1.1.0: {} @@ -5897,6 +7187,10 @@ snapshots: dependencies: esutils: 2.0.3 + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.28.6 @@ -5947,6 +7241,8 @@ snapshots: electron-to-chromium@1.5.302: {} + emoji-regex@8.0.0: {} + end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -5958,6 +7254,8 @@ snapshots: entities@4.5.0: {} + entities@8.0.0: {} + es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 @@ -6038,6 +7336,8 @@ snapshots: iterator.prototype: 1.1.5 safe-array-concat: 1.1.3 + es-module-lexer@2.3.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -6237,10 +7537,16 @@ snapshots: estraverse@5.3.0: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + esutils@2.0.3: {} eventemitter3@4.0.7: {} + expect-type@1.4.0: {} + fast-copy@4.0.2: {} fast-deep-equal@2.0.1: {} @@ -6263,13 +7569,23 @@ snapshots: fast-safe-stringify@2.1.1: {} + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + fastq@1.19.1: dependencies: reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.5 file-entry-cache@8.0.0: dependencies: @@ -6305,6 +7621,9 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 + fsevents@2.3.3: + optional: true + function-bind@1.1.2: {} function.prototype.name@1.1.8: @@ -6320,6 +7639,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -6371,6 +7692,8 @@ snapshots: graceful-fs@4.2.11: {} + graphql@16.14.2: {} + gsap@3.15.0: {} has-bigints@1.1.0: {} @@ -6395,6 +7718,11 @@ snapshots: dependencies: function-bind: 1.1.2 + headers-polyfill@5.0.1: + dependencies: + '@types/set-cookie-parser': 2.4.10 + set-cookie-parser: 3.1.1 + help-me@5.0.0: {} hermes-estree@0.25.1: {} @@ -6405,6 +7733,14 @@ snapshots: hono@4.12.2: {} + html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): + dependencies: + '@exodus/bytes': 1.15.1(@noble/hashes@2.0.1) + transitivePeerDependencies: + - '@noble/hashes' + + html-escaper@2.0.2: {} + html-to-text@9.0.5: dependencies: '@selderee/plugin-htmlparser2': 0.11.0 @@ -6434,6 +7770,8 @@ snapshots: imurmurhash@0.1.4: {} + indent-string@4.0.0: {} + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -6488,6 +7826,8 @@ snapshots: dependencies: call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.0: dependencies: call-bound: 1.0.4 @@ -6505,6 +7845,8 @@ snapshots: is-negative-zero@2.0.3: {} + is-node-process@1.2.0: {} + is-number-object@1.1.1: dependencies: call-bound: 1.0.4 @@ -6512,6 +7854,8 @@ snapshots: is-number@7.0.0: {} + is-potential-custom-element-name@1.0.1: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -6555,6 +7899,19 @@ snapshots: isexe@2.0.0: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 @@ -6570,12 +7927,40 @@ snapshots: joycon@3.1.1: {} + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: dependencies: argparse: 2.0.1 + jsdom@29.1.1(@noble/hashes@2.0.1): + dependencies: + '@asamuzakjp/css-color': 5.1.11 + '@asamuzakjp/dom-selector': 7.1.1 + '@bramus/specificity': 2.4.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.6(css-tree@3.2.1) + '@exodus/bytes': 1.15.1(@noble/hashes@2.0.1) + css-tree: 3.2.1 + data-urls: 7.0.0(@noble/hashes@2.0.1) + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0(@noble/hashes@2.0.1) + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.5.1 + parse5: 8.0.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.1 + undici: 7.28.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1(@noble/hashes@2.0.1) + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -6609,36 +7994,69 @@ snapshots: lightningcss-android-arm64@1.31.1: optional: true + lightningcss-android-arm64@1.32.0: + optional: true + lightningcss-darwin-arm64@1.31.1: optional: true + lightningcss-darwin-arm64@1.32.0: + optional: true + lightningcss-darwin-x64@1.31.1: optional: true + lightningcss-darwin-x64@1.32.0: + optional: true + lightningcss-freebsd-x64@1.31.1: optional: true + lightningcss-freebsd-x64@1.32.0: + optional: true + lightningcss-linux-arm-gnueabihf@1.31.1: optional: true + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + lightningcss-linux-arm64-gnu@1.31.1: optional: true + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + lightningcss-linux-arm64-musl@1.31.1: optional: true + lightningcss-linux-arm64-musl@1.32.0: + optional: true + lightningcss-linux-x64-gnu@1.31.1: optional: true + lightningcss-linux-x64-gnu@1.32.0: + optional: true + lightningcss-linux-x64-musl@1.31.1: optional: true + lightningcss-linux-x64-musl@1.32.0: + optional: true + lightningcss-win32-arm64-msvc@1.31.1: optional: true + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + lightningcss-win32-x64-msvc@1.31.1: optional: true + lightningcss-win32-x64-msvc@1.32.0: + optional: true + lightningcss@1.31.1: dependencies: detect-libc: 2.1.2 @@ -6655,10 +8073,28 @@ snapshots: lightningcss-win32-arm64-msvc: 1.31.1 lightningcss-win32-x64-msvc: 1.31.1 + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 + lodash-es@4.18.1: {} + lodash.debounce@4.0.8: {} lodash.merge@4.6.2: {} @@ -6669,6 +8105,8 @@ snapshots: dependencies: js-tokens: 4.0.0 + lru-cache@11.5.1: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -6677,12 +8115,26 @@ snapshots: dependencies: react: 19.2.4 + lz-string@1.5.0: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.3 + math-intrinsics@1.1.0: {} + mdn-data@2.27.1: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -6696,6 +8148,8 @@ snapshots: dependencies: mime-db: 1.52.0 + min-indent@1.0.1: {} + minimatch@10.2.3: dependencies: brace-expansion: 5.0.3 @@ -6708,8 +8162,37 @@ snapshots: ms@2.1.3: {} + msw@2.14.6(@types/node@25.3.0)(typescript@5.9.3): + dependencies: + '@inquirer/confirm': 6.1.1(@types/node@25.3.0) + '@mswjs/interceptors': 0.41.9 + '@open-draft/deferred-promise': 3.0.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.14.2 + headers-polyfill: 5.0.1 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.11.11 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.1 + type-fest: 5.8.0 + until-async: 3.0.2 + yargs: 17.7.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + + mute-stream@3.0.0: {} + nanoid@3.3.11: {} + nanoid@3.3.15: {} + nanostores@1.1.0: {} natural-compare@1.4.0: {} @@ -6781,6 +8264,8 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + obug@2.1.3: {} + on-exit-leak-free@2.1.2: {} once@1.4.0: @@ -6796,6 +8281,8 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + outvariant@1.4.3: {} + own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 @@ -6814,6 +8301,10 @@ snapshots: dependencies: callsites: 3.1.0 + parse5@8.0.1: + dependencies: + entities: 8.0.0 + parseley@0.12.1: dependencies: leac: 0.6.0 @@ -6825,13 +8316,17 @@ snapshots: path-parse@1.0.7: {} + path-to-regexp@6.3.0: {} + + pathe@2.0.3: {} + peberminta@0.9.0: {} picocolors@1.1.1: {} picomatch@2.3.1: {} - picomatch@4.0.3: {} + picomatch@4.0.5: {} pino-abstract-transport@2.0.0: dependencies: @@ -6881,6 +8376,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.16: + dependencies: + nanoid: 3.3.15 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -6893,6 +8394,12 @@ snapshots: prettier@3.8.1: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + process-warning@5.0.0: {} prop-types@15.8.1: @@ -6993,6 +8500,8 @@ snapshots: react-is@16.13.1: {} + react-is@17.0.2: {} + react-is@18.3.1: {} react-joyride@3.1.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): @@ -7081,6 +8590,11 @@ snapshots: tiny-invariant: 1.3.3 victory-vendor: 36.9.2 + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -7101,6 +8615,10 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + resend@4.8.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@react-email/render': 1.1.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -7118,8 +8636,31 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + rettime@0.11.11: {} + reusify@1.1.0: {} + rolldown@1.1.4: + dependencies: + '@oxc-project/types': 0.138.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.4 + '@rolldown/binding-darwin-arm64': 1.1.4 + '@rolldown/binding-darwin-x64': 1.1.4 + '@rolldown/binding-freebsd-x64': 1.1.4 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.4 + '@rolldown/binding-linux-arm64-gnu': 1.1.4 + '@rolldown/binding-linux-arm64-musl': 1.1.4 + '@rolldown/binding-linux-ppc64-gnu': 1.1.4 + '@rolldown/binding-linux-s390x-gnu': 1.1.4 + '@rolldown/binding-linux-x64-gnu': 1.1.4 + '@rolldown/binding-linux-x64-musl': 1.1.4 + '@rolldown/binding-openharmony-arm64': 1.1.4 + '@rolldown/binding-wasm32-wasi': 1.1.4 + '@rolldown/binding-win32-arm64-msvc': 1.1.4 + '@rolldown/binding-win32-x64-msvc': 1.1.4 + rou3@0.7.12: {} run-parallel@1.2.0: @@ -7147,6 +8688,10 @@ snapshots: safe-stable-stringify@2.5.0: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.27.0: {} scroll@3.0.1: {} @@ -7165,6 +8710,8 @@ snapshots: set-cookie-parser@2.7.2: {} + set-cookie-parser@3.1.1: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -7253,6 +8800,10 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -7273,11 +8824,25 @@ snapshots: split2@4.2.0: {} + stackback@0.0.2: {} + + statuses@2.0.2: {} + + std-env@4.1.0: {} + stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 internal-slot: 1.1.0 + strict-event-emitter@0.5.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 @@ -7322,6 +8887,14 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + strip-json-comments@3.1.1: {} strip-json-comments@5.0.3: {} @@ -7337,6 +8910,10 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: {} + + tagged-tag@1.0.0: {} + tailwind-merge@3.5.0: {} tailwindcss@4.2.1: {} @@ -7349,15 +8926,35 @@ snapshots: tiny-invariant@1.3.3: {} - tinyglobby@0.2.15: + tinybench@2.9.0: {} + + tinyexec@1.2.4: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinyrainbow@3.1.0: {} + + tldts-core@7.4.6: {} + + tldts@7.4.6: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + tldts-core: 7.4.6 to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + tough-cookie@6.0.1: + dependencies: + tldts: 7.4.6 + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -7399,6 +8996,10 @@ snapshots: type-fest@4.41.0: {} + type-fest@5.8.0: + dependencies: + tagged-tag: 1.0.0 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -7454,6 +9055,10 @@ snapshots: undici-types@7.18.2: {} + undici@7.28.0: {} + + until-async@3.0.2: {} + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -7505,6 +9110,74 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 + vite@8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.5 + postcss: 8.5.16 + rolldown: 1.1.4 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 25.3.0 + esbuild: 0.25.12 + fsevents: 2.3.3 + jiti: 2.6.1 + + vitest-axe@0.1.0(vitest@4.1.9): + dependencies: + aria-query: 5.3.2 + axe-core: 4.12.1 + chalk: 5.6.2 + dom-accessibility-api: 0.5.16 + lodash-es: 4.18.1 + redent: 3.0.0 + vitest: 4.1.9(@types/node@25.3.0)(@vitest/coverage-v8@4.1.9)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@25.3.0)(typescript@5.9.3))(vite@8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1)) + + vitest@4.1.9(@types/node@25.3.0)(@vitest/coverage-v8@4.1.9)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.14.6(@types/node@25.3.0)(typescript@5.9.3))(vite@8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1)): + dependencies: + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(msw@2.14.6(@types/node@25.3.0)(typescript@5.9.3))(vite@8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.3.0 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.3 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 8.1.3(@types/node@25.3.0)(esbuild@0.25.12)(jiti@2.6.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 25.3.0 + '@vitest/coverage-v8': 4.1.9(vitest@4.1.9) + jsdom: 29.1.1(@noble/hashes@2.0.1) + transitivePeerDependencies: + - msw + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@8.0.1: {} + + whatwg-mimetype@5.0.0: {} + + whatwg-url@16.0.1(@noble/hashes@2.0.1): + dependencies: + '@exodus/bytes': 1.15.1(@noble/hashes@2.0.1) + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -7550,12 +9223,41 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrappy@1.0.2: {} + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + yallist@3.1.1: {} + yargs-parser@21.1.1: {} + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yocto-queue@0.1.0: {} zod-validation-error@4.0.2(zod@4.3.6): diff --git a/turbo.json b/turbo.json index 9dad802..64ea5d7 100644 --- a/turbo.json +++ b/turbo.json @@ -12,6 +12,19 @@ "check-types": { "dependsOn": ["^build"] }, + "test": { + "dependsOn": ["^build"], + "outputs": [] + }, + "test:unit": { + "dependsOn": ["^build"], + "outputs": [] + }, + "test:coverage": { + "dependsOn": ["^build"], + "outputs": ["coverage/**"], + "cache": false + }, "dev": { "cache": false, "persistent": true