test: add vitest baseline for critical payment and auth guards

This commit is contained in:
2026-04-03 18:15:18 +02:00
parent 2542748e82
commit 68416ed9de
12 changed files with 730 additions and 75 deletions

9
convex/ai-utils.ts Normal file
View File

@@ -0,0 +1,9 @@
type NodeCanvasRef = {
canvasId: string;
};
export function assertNodeBelongsToCanvasOrThrow(node: NodeCanvasRef, canvasId: string): void {
if (node.canvasId !== canvasId) {
throw new Error("Node does not belong to canvas");
}
}

View File

@@ -12,6 +12,7 @@ import {
IMAGE_MODELS, IMAGE_MODELS,
} from "./openrouter"; } from "./openrouter";
import type { Id } from "./_generated/dataModel"; import type { Id } from "./_generated/dataModel";
import { assertNodeBelongsToCanvasOrThrow } from "./ai-utils";
const MAX_IMAGE_RETRIES = 2; const MAX_IMAGE_RETRIES = 2;
@@ -526,9 +527,7 @@ export const generateImage = action({
if (!node) { if (!node) {
throw new Error("Node not found"); throw new Error("Node not found");
} }
if (node.canvasId !== args.canvasId) { assertNodeBelongsToCanvasOrThrow(node, args.canvasId);
throw new Error("Node does not belong to canvas");
}
const userId = canvas.ownerId; const userId = canvas.ownerId;
const verifiedCanvasId = canvas._id; const verifiedCanvasId = canvas._id;

View File

@@ -0,0 +1,49 @@
import type { Id } from "./_generated/dataModel";
type BatchNodeRecord = {
_id: Id<"nodes">;
canvasId: Id<"canvases">;
};
type BatchCanvasRecord = {
_id: Id<"canvases">;
ownerId: string;
};
type ValidateBatchArgs<N extends BatchNodeRecord> = {
userId: string;
nodeIds: Id<"nodes">[];
getNodeById: (nodeId: Id<"nodes">) => Promise<N | null>;
getCanvasById: (canvasId: Id<"canvases">) => Promise<BatchCanvasRecord | null>;
};
export async function validateBatchNodesForUserOrThrow<N extends BatchNodeRecord>(
args: ValidateBatchArgs<N>,
): Promise<{ canvasId: Id<"canvases">; nodes: N[] }> {
if (args.nodeIds.length === 0) {
throw new Error("Batch must contain at least one node id");
}
const nodes: N[] = [];
for (const nodeId of args.nodeIds) {
const node = await args.getNodeById(nodeId);
if (!node) {
throw new Error("Node not found");
}
nodes.push(node);
}
const canvasId = nodes[0].canvasId;
for (const node of nodes) {
if (node.canvasId !== canvasId) {
throw new Error("All nodes must belong to the same canvas");
}
}
const canvas = await args.getCanvasById(canvasId);
if (!canvas || canvas.ownerId !== args.userId) {
throw new Error("Canvas not found");
}
return { canvasId, nodes };
}

View File

@@ -3,6 +3,7 @@ import { v } from "convex/values";
import { requireAuth } from "./helpers"; import { requireAuth } from "./helpers";
import type { Doc, Id } from "./_generated/dataModel"; import type { Doc, Id } from "./_generated/dataModel";
import { isAdjustmentNodeType } from "../lib/canvas-node-types"; import { isAdjustmentNodeType } from "../lib/canvas-node-types";
import { validateBatchNodesForUserOrThrow } from "./batch-validation-utils";
import { import {
getCanvasConnectionValidationMessage, getCanvasConnectionValidationMessage,
validateCanvasConnectionPolicy, validateCanvasConnectionPolicy,
@@ -45,29 +46,12 @@ async function getValidatedBatchNodesOrThrow(
userId: string, userId: string,
nodeIds: Id<"nodes">[], nodeIds: Id<"nodes">[],
): Promise<{ canvasId: Id<"canvases">; nodes: Doc<"nodes">[] }> { ): Promise<{ canvasId: Id<"canvases">; nodes: Doc<"nodes">[] }> {
if (nodeIds.length === 0) { return await validateBatchNodesForUserOrThrow({
throw new Error("Batch must contain at least one node id"); userId,
} nodeIds,
getNodeById: (nodeId) => ctx.db.get(nodeId),
const nodes: Doc<"nodes">[] = []; getCanvasById: (canvasId) => ctx.db.get(canvasId),
for (const nodeId of nodeIds) { });
const node = await ctx.db.get(nodeId);
if (!node) {
throw new Error("Node not found");
}
nodes.push(node);
}
const canvasId = nodes[0].canvasId;
for (const node of nodes) {
if (node.canvasId !== canvasId) {
throw new Error("All nodes must belong to the same canvas");
}
}
await getCanvasOrThrow(ctx, canvasId, userId);
return { canvasId, nodes };
} }
type NodeCreateMutationName = type NodeCreateMutationName =

71
convex/polar-utils.ts Normal file
View File

@@ -0,0 +1,71 @@
type IdempotencyScope =
| "topup_paid"
| "subscription_activated_cycle"
| "subscription_revoked";
type RegisterWebhookEventArgs = {
provider: "polar";
scope: IdempotencyScope;
idempotencyKey: string;
userId: string;
polarOrderId?: string;
polarSubscriptionId?: string;
};
type ExistingEventLookup = {
provider: "polar";
idempotencyKey: string;
};
type ExistingEventRecord = {
_id: string;
};
export type WebhookEventRepo = {
findByProviderAndKey: (
lookup: ExistingEventLookup,
) => Promise<ExistingEventRecord | null>;
insert: (args: RegisterWebhookEventArgs & { createdAt: number }) => Promise<void>;
};
export function buildSubscriptionCycleIdempotencyKey(args: {
polarSubscriptionId: string;
currentPeriodStart: number;
currentPeriodEnd: number;
}): string {
return `polar:subscription_cycle:${args.polarSubscriptionId}:${args.currentPeriodStart}:${args.currentPeriodEnd}`;
}
export function buildSubscriptionRevokedIdempotencyKey(args: {
userId: string;
polarSubscriptionId?: string;
}): string {
return args.polarSubscriptionId
? `polar:subscription_revoked:${args.polarSubscriptionId}`
: `polar:subscription_revoked:user:${args.userId}`;
}
export function buildTopUpPaidIdempotencyKey(polarOrderId: string): string {
return `polar:order_paid:${polarOrderId}`;
}
export async function registerWebhookEventOnce(
repo: WebhookEventRepo,
args: RegisterWebhookEventArgs,
now: () => number = Date.now,
): Promise<boolean> {
const existing = await repo.findByProviderAndKey({
provider: args.provider,
idempotencyKey: args.idempotencyKey,
});
if (existing) {
return false;
}
await repo.insert({
...args,
createdAt: now(),
});
return true;
}

View File

@@ -1,6 +1,12 @@
import { v } from "convex/values"; import { v } from "convex/values";
import { internalMutation, type MutationCtx } from "./_generated/server"; import { internalMutation, type MutationCtx } from "./_generated/server";
import {
buildSubscriptionCycleIdempotencyKey,
buildSubscriptionRevokedIdempotencyKey,
buildTopUpPaidIdempotencyKey,
registerWebhookEventOnce,
} from "./polar-utils";
type DbCtx = Pick<MutationCtx, "db">; type DbCtx = Pick<MutationCtx, "db">;
@@ -65,7 +71,11 @@ export async function applySubscriptionActivated(ctx: DbCtx, args: ActivatedArgs
}); });
} }
const cycleKey = `polar:subscription_cycle:${args.polarSubscriptionId}:${args.currentPeriodStart}:${args.currentPeriodEnd}`; const cycleKey = buildSubscriptionCycleIdempotencyKey({
polarSubscriptionId: args.polarSubscriptionId,
currentPeriodStart: args.currentPeriodStart,
currentPeriodEnd: args.currentPeriodEnd,
});
const isFirstCycleEvent = await registerWebhookEvent(ctx, { const isFirstCycleEvent = await registerWebhookEvent(ctx, {
provider: "polar", provider: "polar",
scope: "subscription_activated_cycle", scope: "subscription_activated_cycle",
@@ -145,9 +155,10 @@ export async function applySubscriptionRevoked(ctx: DbCtx, args: RevokedArgs) {
}); });
} }
const revokedKey = args.polarSubscriptionId const revokedKey = buildSubscriptionRevokedIdempotencyKey({
? `polar:subscription_revoked:${args.polarSubscriptionId}` userId: args.userId,
: `polar:subscription_revoked:user:${args.userId}`; polarSubscriptionId: args.polarSubscriptionId,
});
const isFirstRevokedEvent = await registerWebhookEvent(ctx, { const isFirstRevokedEvent = await registerWebhookEvent(ctx, {
provider: "polar", provider: "polar",
scope: "subscription_revoked", scope: "subscription_revoked",
@@ -175,7 +186,7 @@ export async function applyTopUpPaid(ctx: DbCtx, args: TopUpArgs) {
const isFirstTopUpEvent = await registerWebhookEvent(ctx, { const isFirstTopUpEvent = await registerWebhookEvent(ctx, {
provider: "polar", provider: "polar",
scope: "topup_paid", scope: "topup_paid",
idempotencyKey: `polar:order_paid:${args.polarOrderId}`, idempotencyKey: buildTopUpPaidIdempotencyKey(args.polarOrderId),
userId: args.userId, userId: args.userId,
polarOrderId: args.polarOrderId, polarOrderId: args.polarOrderId,
}); });
@@ -218,28 +229,42 @@ async function registerWebhookEvent(
polarSubscriptionId?: string; polarSubscriptionId?: string;
} }
) { ) {
return await registerWebhookEventOnce(
{
findByProviderAndKey: async ({ provider, idempotencyKey }) => {
const existing = await ctx.db const existing = await ctx.db
.query("webhookIdempotencyEvents") .query("webhookIdempotencyEvents")
.withIndex("by_provider_key", (q) => .withIndex("by_provider_key", (q) =>
q.eq("provider", args.provider).eq("idempotencyKey", args.idempotencyKey) q.eq("provider", provider).eq("idempotencyKey", idempotencyKey)
) )
.first(); .first();
if (!existing) {
if (existing) { return null;
return false;
} }
return { _id: existing._id };
},
insert: async ({
provider,
scope,
idempotencyKey,
userId,
polarOrderId,
polarSubscriptionId,
createdAt,
}) => {
await ctx.db.insert("webhookIdempotencyEvents", { await ctx.db.insert("webhookIdempotencyEvents", {
provider: args.provider, provider,
scope: args.scope, scope,
idempotencyKey: args.idempotencyKey, idempotencyKey,
userId: args.userId, userId,
polarOrderId: args.polarOrderId, polarOrderId,
polarSubscriptionId: args.polarSubscriptionId, polarSubscriptionId,
createdAt: Date.now(), createdAt,
}); });
},
return true; },
args,
);
} }
export const handleSubscriptionActivated = internalMutation({ export const handleSubscriptionActivated = internalMutation({

View File

@@ -7,7 +7,9 @@
"dev:strict": "next dev", "dev:strict": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "eslint" "lint": "eslint",
"test": "vitest run",
"test:watch": "vitest"
}, },
"dependencies": { "dependencies": {
"@convex-dev/better-auth": "^0.11.3", "@convex-dev/better-auth": "^0.11.3",
@@ -56,6 +58,7 @@
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "16.2.1", "eslint-config-next": "16.2.1",
"tailwindcss": "^4", "tailwindcss": "^4",
"typescript": "^5" "typescript": "^5",
"vitest": "^3.2.4"
} }
} }

414
pnpm-lock.yaml generated
View File

@@ -10,10 +10,10 @@ importers:
dependencies: dependencies:
'@convex-dev/better-auth': '@convex-dev/better-auth':
specifier: ^0.11.3 specifier: ^0.11.3
version: 0.11.3(@standard-schema/spec@1.1.0)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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))(convex@1.34.0(react@19.2.4))(hono@4.12.9)(react@19.2.4)(typescript@5.9.3) version: 0.11.3(@standard-schema/spec@1.1.0)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)))(convex@1.34.0(react@19.2.4))(hono@4.12.9)(react@19.2.4)(typescript@5.9.3)
'@daveyplate/better-auth-ui': '@daveyplate/better-auth-ui':
specifier: ^3.4.0 specifier: ^3.4.0
version: 3.4.0(aed41ba285ba33af5b1a54a1a4efb176) version: 3.4.0(43fe9e8f7c24dd7a61ee43b650bb480d)
'@dnd-kit/core': '@dnd-kit/core':
specifier: ^6.3.1 specifier: ^6.3.1
version: 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -25,7 +25,7 @@ importers:
version: 0.1.97 version: 0.1.97
'@polar-sh/better-auth': '@polar-sh/better-auth':
specifier: ^1.8.3 specifier: ^1.8.3
version: 1.8.3(@polar-sh/sdk@0.46.7)(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@stripe/stripe-js@7.9.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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))(react-dom@19.2.4(react@19.2.4))(react-is@16.13.1)(react@19.2.4)(redux@5.0.1)(zod@4.3.6) version: 1.8.3(@polar-sh/sdk@0.46.7)(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@stripe/stripe-js@7.9.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)))(react-dom@19.2.4(react@19.2.4))(react-is@16.13.1)(react@19.2.4)(redux@5.0.1)(zod@4.3.6)
'@polar-sh/sdk': '@polar-sh/sdk':
specifier: ^0.46.7 specifier: ^0.46.7
version: 0.46.7 version: 0.46.7
@@ -40,7 +40,7 @@ importers:
version: 12.10.1(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 12.10.1(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
better-auth: better-auth:
specifier: ^1.5.6 specifier: ^1.5.6
version: 1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1))
class-variance-authority: class-variance-authority:
specifier: ^0.7.1 specifier: ^0.7.1
version: 0.7.1 version: 0.7.1
@@ -144,6 +144,9 @@ importers:
typescript: typescript:
specifier: ^5 specifier: ^5
version: 5.9.3 version: 5.9.3
vitest:
specifier: ^3.2.4
version: 3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)
packages: packages:
@@ -3112,6 +3115,9 @@ packages:
'@tybys/wasm-util@0.10.1': '@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
'@types/chai@5.2.3':
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
'@types/connect@3.4.38': '@types/connect@3.4.38':
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
@@ -3154,6 +3160,9 @@ packages:
'@types/d3-zoom@3.0.8': '@types/d3-zoom@3.0.8':
resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
'@types/deep-eql@4.0.2':
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
'@types/eslint-scope@3.7.7': '@types/eslint-scope@3.7.7':
resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
@@ -3374,6 +3383,35 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@vitest/expect@3.2.4':
resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
'@vitest/mocker@3.2.4':
resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==}
peerDependencies:
msw: ^2.4.9
vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
peerDependenciesMeta:
msw:
optional: true
vite:
optional: true
'@vitest/pretty-format@3.2.4':
resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==}
'@vitest/runner@3.2.4':
resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==}
'@vitest/snapshot@3.2.4':
resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==}
'@vitest/spy@3.2.4':
resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==}
'@vitest/utils@3.2.4':
resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
'@webassemblyjs/ast@1.14.1': '@webassemblyjs/ast@1.14.1':
resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==}
@@ -3570,6 +3608,10 @@ packages:
resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==} resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
assertion-error@2.0.1:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
ast-types-flow@0.0.8: ast-types-flow@0.0.8:
resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
@@ -3730,6 +3772,10 @@ packages:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
call-bind-apply-helpers@1.0.2: call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -3749,6 +3795,10 @@ packages:
caniuse-lite@1.0.30001781: caniuse-lite@1.0.30001781:
resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==}
chai@5.3.3:
resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
engines: {node: '>=18'}
chalk@4.1.2: chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -3757,6 +3807,10 @@ packages:
resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
check-error@2.1.3:
resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==}
engines: {node: '>= 16'}
chrome-trace-event@1.0.4: chrome-trace-event@1.0.4:
resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@@ -4055,6 +4109,10 @@ packages:
babel-plugin-macros: babel-plugin-macros:
optional: true optional: true
deep-eql@5.0.2:
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
engines: {node: '>=6'}
deep-is@0.1.4: deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -4193,6 +4251,9 @@ packages:
resolution: {integrity: sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==} resolution: {integrity: sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
es-module-lexer@1.7.0:
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
es-module-lexer@2.0.0: es-module-lexer@2.0.0:
resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==}
@@ -4363,6 +4424,9 @@ packages:
estree-walker@2.0.2: estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
esutils@2.0.3: esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -4408,6 +4472,10 @@ packages:
exif-parser@0.1.12: exif-parser@0.1.12:
resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==} resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==}
expect-type@1.3.0:
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
engines: {node: '>=12.0.0'}
express-rate-limit@8.3.1: express-rate-limit@8.3.1:
resolution: {integrity: sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==} resolution: {integrity: sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==}
engines: {node: '>= 16'} engines: {node: '>= 16'}
@@ -5000,6 +5068,9 @@ packages:
js-tokens@4.0.0: js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
js-tokens@9.0.1:
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
js-yaml@4.1.1: js-yaml@4.1.1:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true hasBin: true
@@ -5180,6 +5251,9 @@ packages:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true hasBin: true
loupe@3.2.1:
resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==}
lru-cache@11.2.7: lru-cache@11.2.7:
resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
@@ -5564,6 +5638,13 @@ packages:
path-to-regexp@8.3.0: path-to-regexp@8.3.0:
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
pathval@2.0.1:
resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
engines: {node: '>= 14.16'}
peberminta@0.9.0: peberminta@0.9.0:
resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==}
@@ -6057,6 +6138,9 @@ packages:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
signal-exit@3.0.7: signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
@@ -6094,6 +6178,9 @@ packages:
stable-hash@0.0.5: stable-hash@0.0.5:
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
stacktrace-parser@0.1.11: stacktrace-parser@0.1.11:
resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==}
engines: {node: '>=6'} engines: {node: '>=6'}
@@ -6108,6 +6195,9 @@ packages:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
std-env@3.10.0:
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
stdin-discarder@0.2.2: stdin-discarder@0.2.2:
resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -6184,6 +6274,9 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'} engines: {node: '>=8'}
strip-literal@3.1.0:
resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==}
strtok3@6.3.0: strtok3@6.3.0:
resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -6258,13 +6351,31 @@ packages:
tiny-invariant@1.3.3: tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
tinycolor2@1.6.0: tinycolor2@1.6.0:
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
tinyglobby@0.2.15: tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
tinypool@1.1.1:
resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
engines: {node: ^18.0.0 || >=20.0.0}
tinyrainbow@2.0.0:
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
engines: {node: '>=14.0.0'}
tinyspy@4.0.4:
resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==}
engines: {node: '>=14.0.0'}
tldts-core@7.0.27: tldts-core@7.0.27:
resolution: {integrity: sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==} resolution: {integrity: sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==}
@@ -6466,6 +6577,79 @@ packages:
victory-vendor@37.3.6: victory-vendor@37.3.6:
resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==}
vite-node@3.2.4:
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
vite@7.3.1:
resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0
jiti: '>=1.21.0'
less: ^4.0.0
lightningcss: ^1.21.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
jiti:
optional: true
less:
optional: true
lightningcss:
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@3.2.4:
resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@types/debug': ^4.1.12
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
'@vitest/browser': 3.2.4
'@vitest/ui': 3.2.4
happy-dom: '*'
jsdom: '*'
peerDependenciesMeta:
'@edge-runtime/vm':
optional: true
'@types/debug':
optional: true
'@types/node':
optional: true
'@vitest/browser':
optional: true
'@vitest/ui':
optional: true
happy-dom:
optional: true
jsdom:
optional: true
warning@4.0.3: warning@4.0.3:
resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
@@ -6526,6 +6710,11 @@ packages:
engines: {node: ^16.13.0 || >=18.0.0} engines: {node: ^16.13.0 || >=18.0.0}
hasBin: true hasBin: true
why-is-node-running@2.3.0:
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
engines: {node: '>=8'}
hasBin: true
word-wrap@1.2.5: word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -6821,11 +7010,11 @@ snapshots:
'@babel/helper-string-parser': 7.27.1 '@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5 '@babel/helper-validator-identifier': 7.28.5
'@better-auth/api-key@1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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/api-key@1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)))':
dependencies: dependencies:
'@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0)
'@better-auth/utils': 0.3.1 '@better-auth/utils': 0.3.1
better-auth: 1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1))
zod: 4.3.6 zod: 4.3.6
'@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0)': '@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0)':
@@ -6863,14 +7052,14 @@ snapshots:
'@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0)
'@better-auth/utils': 0.3.1 '@better-auth/utils': 0.3.1
'@better-auth/passkey@1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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-call@1.3.2(zod@4.3.6))(nanostores@1.2.0)': '@better-auth/passkey@1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)))(better-call@1.3.2(zod@4.3.6))(nanostores@1.2.0)':
dependencies: dependencies:
'@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0)
'@better-auth/utils': 0.3.1 '@better-auth/utils': 0.3.1
'@better-fetch/fetch': 1.1.21 '@better-fetch/fetch': 1.1.21
'@simplewebauthn/browser': 13.3.0 '@simplewebauthn/browser': 13.3.0
'@simplewebauthn/server': 13.3.0 '@simplewebauthn/server': 13.3.0
better-auth: 1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1))
better-call: 1.3.2(zod@4.3.6) better-call: 1.3.2(zod@4.3.6)
nanostores: 1.2.0 nanostores: 1.2.0
zod: 4.3.6 zod: 4.3.6
@@ -6898,10 +7087,10 @@ snapshots:
'@captchafox/types@1.4.0': {} '@captchafox/types@1.4.0': {}
'@convex-dev/better-auth@0.11.3(@standard-schema/spec@1.1.0)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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))(convex@1.34.0(react@19.2.4))(hono@4.12.9)(react@19.2.4)(typescript@5.9.3)': '@convex-dev/better-auth@0.11.3(@standard-schema/spec@1.1.0)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)))(convex@1.34.0(react@19.2.4))(hono@4.12.9)(react@19.2.4)(typescript@5.9.3)':
dependencies: dependencies:
'@better-fetch/fetch': 1.1.21 '@better-fetch/fetch': 1.1.21
better-auth: 1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1))
common-tags: 1.8.2 common-tags: 1.8.2
convex: 1.34.0(react@19.2.4) convex: 1.34.0(react@19.2.4)
convex-helpers: 0.1.114(@standard-schema/spec@1.1.0)(convex@1.34.0(react@19.2.4))(hono@4.12.9)(react@19.2.4)(typescript@5.9.3)(zod@4.3.6) convex-helpers: 0.1.114(@standard-schema/spec@1.1.0)(convex@1.34.0(react@19.2.4))(hono@4.12.9)(react@19.2.4)(typescript@5.9.3)(zod@4.3.6)
@@ -6918,21 +7107,21 @@ snapshots:
'@date-fns/tz@1.4.1': {} '@date-fns/tz@1.4.1': {}
'@daveyplate/better-auth-tanstack@1.3.6(@tanstack/query-core@5.95.2)(@tanstack/react-query@5.95.2(react@19.2.4))(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@daveyplate/better-auth-tanstack@1.3.6(@tanstack/query-core@5.95.2)(@tanstack/react-query@5.95.2(react@19.2.4))(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies: dependencies:
'@tanstack/query-core': 5.95.2 '@tanstack/query-core': 5.95.2
'@tanstack/react-query': 5.95.2(react@19.2.4) '@tanstack/react-query': 5.95.2(react@19.2.4)
better-auth: 1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1))
react: 19.2.4 react: 19.2.4
react-dom: 19.2.4(react@19.2.4) react-dom: 19.2.4(react@19.2.4)
'@daveyplate/better-auth-ui@3.4.0(aed41ba285ba33af5b1a54a1a4efb176)': '@daveyplate/better-auth-ui@3.4.0(43fe9e8f7c24dd7a61ee43b650bb480d)':
dependencies: dependencies:
'@better-auth/api-key': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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/api-key': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)))
'@better-auth/passkey': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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-call@1.3.2(zod@4.3.6))(nanostores@1.2.0) '@better-auth/passkey': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)))(better-call@1.3.2(zod@4.3.6))(nanostores@1.2.0)
'@better-fetch/fetch': 1.1.21 '@better-fetch/fetch': 1.1.21
'@captchafox/react': 1.11.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@captchafox/react': 1.11.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@daveyplate/better-auth-tanstack': 1.3.6(@tanstack/query-core@5.95.2)(@tanstack/react-query@5.95.2(react@19.2.4))(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@daveyplate/better-auth-tanstack': 1.3.6(@tanstack/query-core@5.95.2)(@tanstack/react-query@5.95.2(react@19.2.4))(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@hcaptcha/react-hcaptcha': 2.0.2 '@hcaptcha/react-hcaptcha': 2.0.2
'@hookform/resolvers': 5.2.2(react-hook-form@7.72.0(react@19.2.4)) '@hookform/resolvers': 5.2.2(react-hook-form@7.72.0(react@19.2.4))
'@instantdb/react': 0.22.169(react@19.2.4) '@instantdb/react': 0.22.169(react@19.2.4)
@@ -6957,7 +7146,7 @@ snapshots:
'@triplit/client': 1.0.50(typescript@5.9.3) '@triplit/client': 1.0.50(typescript@5.9.3)
'@triplit/react': 1.0.51(react@19.2.4)(typescript@5.9.3) '@triplit/react': 1.0.51(react@19.2.4)(typescript@5.9.3)
'@wojtekmaj/react-recaptcha-v3': 0.1.4(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@wojtekmaj/react-recaptcha-v3': 0.1.4(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
better-auth: 1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1))
better-call: 2.0.2(zod@4.3.6) better-call: 2.0.2(zod@4.3.6)
bowser: 2.14.1 bowser: 2.14.1
class-variance-authority: 0.7.1 class-variance-authority: 0.7.1
@@ -8171,11 +8360,11 @@ snapshots:
tslib: 2.8.1 tslib: 2.8.1
tsyringe: 4.10.0 tsyringe: 4.10.0
'@polar-sh/better-auth@1.8.3(@polar-sh/sdk@0.46.7)(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@stripe/stripe-js@7.9.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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))(react-dom@19.2.4(react@19.2.4))(react-is@16.13.1)(react@19.2.4)(redux@5.0.1)(zod@4.3.6)': '@polar-sh/better-auth@1.8.3(@polar-sh/sdk@0.46.7)(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@stripe/stripe-js@7.9.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)))(react-dom@19.2.4(react@19.2.4))(react-is@16.13.1)(react@19.2.4)(redux@5.0.1)(zod@4.3.6)':
dependencies: dependencies:
'@polar-sh/checkout': 0.2.0(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@stripe/stripe-js@7.9.0)(@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-is@16.13.1)(react@19.2.4)(redux@5.0.1) '@polar-sh/checkout': 0.2.0(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@stripe/stripe-js@7.9.0)(@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-is@16.13.1)(react@19.2.4)(redux@5.0.1)
'@polar-sh/sdk': 0.46.7 '@polar-sh/sdk': 0.46.7
better-auth: 1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1))
zod: 4.3.6 zod: 4.3.6
transitivePeerDependencies: transitivePeerDependencies:
- '@stripe/react-stripe-js' - '@stripe/react-stripe-js'
@@ -9687,6 +9876,11 @@ snapshots:
tslib: 2.8.1 tslib: 2.8.1
optional: true optional: true
'@types/chai@5.2.3':
dependencies:
'@types/deep-eql': 4.0.2
assertion-error: 2.0.1
'@types/connect@3.4.38': '@types/connect@3.4.38':
dependencies: dependencies:
'@types/node': 20.19.37 '@types/node': 20.19.37
@@ -9730,6 +9924,8 @@ snapshots:
'@types/d3-interpolate': 3.0.4 '@types/d3-interpolate': 3.0.4
'@types/d3-selection': 3.0.11 '@types/d3-selection': 3.0.11
'@types/deep-eql@4.0.2': {}
'@types/eslint-scope@3.7.7': '@types/eslint-scope@3.7.7':
dependencies: dependencies:
'@types/eslint': 9.6.1 '@types/eslint': 9.6.1
@@ -9944,6 +10140,49 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.11.1': '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
optional: true optional: true
'@vitest/expect@3.2.4':
dependencies:
'@types/chai': 5.2.3
'@vitest/spy': 3.2.4
'@vitest/utils': 3.2.4
chai: 5.3.3
tinyrainbow: 2.0.0
'@vitest/mocker@3.2.4(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
msw: 2.12.14(@types/node@20.19.37)(typescript@5.9.3)
vite: 7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)
'@vitest/pretty-format@3.2.4':
dependencies:
tinyrainbow: 2.0.0
'@vitest/runner@3.2.4':
dependencies:
'@vitest/utils': 3.2.4
pathe: 2.0.3
strip-literal: 3.1.0
'@vitest/snapshot@3.2.4':
dependencies:
'@vitest/pretty-format': 3.2.4
magic-string: 0.30.21
pathe: 2.0.3
'@vitest/spy@3.2.4':
dependencies:
tinyspy: 4.0.4
'@vitest/utils@3.2.4':
dependencies:
'@vitest/pretty-format': 3.2.4
loupe: 3.2.1
tinyrainbow: 2.0.0
'@webassemblyjs/ast@1.14.1': '@webassemblyjs/ast@1.14.1':
dependencies: dependencies:
'@webassemblyjs/helper-numbers': 1.13.2 '@webassemblyjs/helper-numbers': 1.13.2
@@ -10204,6 +10443,8 @@ snapshots:
pvutils: 1.1.5 pvutils: 1.1.5
tslib: 2.8.1 tslib: 2.8.1
assertion-error@2.0.1: {}
ast-types-flow@0.0.8: {} ast-types-flow@0.0.8: {}
ast-types@0.16.1: ast-types@0.16.1:
@@ -10230,7 +10471,7 @@ snapshots:
baseline-browser-mapping@2.10.10: {} baseline-browser-mapping@2.10.10: {}
better-auth@1.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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.5.6(@opentelemetry/api@1.9.0)(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(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@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)):
dependencies: dependencies:
'@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0)
'@better-auth/drizzle-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1) '@better-auth/drizzle-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)
@@ -10253,6 +10494,7 @@ snapshots:
next: 16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next: 16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
react: 19.2.4 react: 19.2.4
react-dom: 19.2.4(react@19.2.4) react-dom: 19.2.4(react@19.2.4)
vitest: 3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@cloudflare/workers-types' - '@cloudflare/workers-types'
- '@opentelemetry/api' - '@opentelemetry/api'
@@ -10327,6 +10569,8 @@ snapshots:
bytes@3.1.2: {} bytes@3.1.2: {}
cac@6.7.14: {}
call-bind-apply-helpers@1.0.2: call-bind-apply-helpers@1.0.2:
dependencies: dependencies:
es-errors: 1.3.0 es-errors: 1.3.0
@@ -10348,6 +10592,14 @@ snapshots:
caniuse-lite@1.0.30001781: {} caniuse-lite@1.0.30001781: {}
chai@5.3.3:
dependencies:
assertion-error: 2.0.1
check-error: 2.1.3
deep-eql: 5.0.2
loupe: 3.2.1
pathval: 2.0.1
chalk@4.1.2: chalk@4.1.2:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
@@ -10355,6 +10607,8 @@ snapshots:
chalk@5.6.2: {} chalk@5.6.2: {}
check-error@2.1.3: {}
chrome-trace-event@1.0.4: {} chrome-trace-event@1.0.4: {}
cjs-module-lexer@2.2.0: {} cjs-module-lexer@2.2.0: {}
@@ -10588,6 +10842,8 @@ snapshots:
dedent@1.7.2: {} dedent@1.7.2: {}
deep-eql@5.0.2: {}
deep-is@0.1.4: {} deep-is@0.1.4: {}
deepmerge@4.3.1: {} deepmerge@4.3.1: {}
@@ -10774,6 +11030,8 @@ snapshots:
math-intrinsics: 1.1.0 math-intrinsics: 1.1.0
safe-array-concat: 1.1.3 safe-array-concat: 1.1.3
es-module-lexer@1.7.0: {}
es-module-lexer@2.0.0: {} es-module-lexer@2.0.0: {}
es-object-atoms@1.1.1: es-object-atoms@1.1.1:
@@ -11048,6 +11306,10 @@ snapshots:
estree-walker@2.0.2: {} estree-walker@2.0.2: {}
estree-walker@3.0.3:
dependencies:
'@types/estree': 1.0.8
esutils@2.0.3: {} esutils@2.0.3: {}
etag@1.8.1: {} etag@1.8.1: {}
@@ -11101,6 +11363,8 @@ snapshots:
exif-parser@0.1.12: {} exif-parser@0.1.12: {}
expect-type@1.3.0: {}
express-rate-limit@8.3.1(express@5.2.1): express-rate-limit@8.3.1(express@5.2.1):
dependencies: dependencies:
express: 5.2.1 express: 5.2.1
@@ -11734,6 +11998,8 @@ snapshots:
js-tokens@4.0.0: {} js-tokens@4.0.0: {}
js-tokens@9.0.1: {}
js-yaml@4.1.1: js-yaml@4.1.1:
dependencies: dependencies:
argparse: 2.0.1 argparse: 2.0.1
@@ -11877,6 +12143,8 @@ snapshots:
dependencies: dependencies:
js-tokens: 4.0.0 js-tokens: 4.0.0
loupe@3.2.1: {}
lru-cache@11.2.7: {} lru-cache@11.2.7: {}
lru-cache@5.1.1: lru-cache@5.1.1:
@@ -12248,6 +12516,10 @@ snapshots:
path-to-regexp@8.3.0: {} path-to-regexp@8.3.0: {}
pathe@2.0.3: {}
pathval@2.0.1: {}
peberminta@0.9.0: {} peberminta@0.9.0: {}
peek-readable@4.1.0: {} peek-readable@4.1.0: {}
@@ -12916,6 +13188,8 @@ snapshots:
side-channel-map: 1.0.1 side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2 side-channel-weakmap: 1.0.2
siginfo@2.0.0: {}
signal-exit@3.0.7: {} signal-exit@3.0.7: {}
signal-exit@4.1.0: {} signal-exit@4.1.0: {}
@@ -12942,6 +13216,8 @@ snapshots:
stable-hash@0.0.5: {} stable-hash@0.0.5: {}
stackback@0.0.2: {}
stacktrace-parser@0.1.11: stacktrace-parser@0.1.11:
dependencies: dependencies:
type-fest: 0.7.1 type-fest: 0.7.1
@@ -12955,6 +13231,8 @@ snapshots:
statuses@2.0.2: {} statuses@2.0.2: {}
std-env@3.10.0: {}
stdin-discarder@0.2.2: {} stdin-discarder@0.2.2: {}
stop-iteration-iterator@1.1.0: stop-iteration-iterator@1.1.0:
@@ -13056,6 +13334,10 @@ snapshots:
strip-json-comments@3.1.1: {} strip-json-comments@3.1.1: {}
strip-literal@3.1.0:
dependencies:
js-tokens: 9.0.1
strtok3@6.3.0: strtok3@6.3.0:
dependencies: dependencies:
'@tokenizer/token': 0.3.0 '@tokenizer/token': 0.3.0
@@ -13109,13 +13391,23 @@ snapshots:
tiny-invariant@1.3.3: {} tiny-invariant@1.3.3: {}
tinybench@2.9.0: {}
tinycolor2@1.6.0: {} tinycolor2@1.6.0: {}
tinyexec@0.3.2: {}
tinyglobby@0.2.15: tinyglobby@0.2.15:
dependencies: dependencies:
fdir: 6.5.0(picomatch@4.0.4) fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4 picomatch: 4.0.4
tinypool@1.1.1: {}
tinyrainbow@2.0.0: {}
tinyspy@4.0.4: {}
tldts-core@7.0.27: {} tldts-core@7.0.27: {}
tldts@7.0.27: tldts@7.0.27:
@@ -13355,6 +13647,83 @@ snapshots:
d3-time: 3.1.0 d3-time: 3.1.0
d3-timer: 3.0.1 d3-timer: 3.0.1
vite-node@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1):
dependencies:
cac: 6.7.14
debug: 4.4.3
es-module-lexer: 1.7.0
pathe: 2.0.3
vite: 7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)
transitivePeerDependencies:
- '@types/node'
- jiti
- less
- lightningcss
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- yaml
vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1):
dependencies:
esbuild: 0.27.0
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
postcss: 8.5.8
rollup: 4.60.0
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 20.19.37
fsevents: 2.3.3
jiti: 2.6.1
lightningcss: 1.32.0
terser: 5.46.1
vitest@3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(terser@5.46.1):
dependencies:
'@types/chai': 5.2.3
'@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(msw@2.12.14(@types/node@20.19.37)(typescript@5.9.3))(vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
'@vitest/spy': 3.2.4
'@vitest/utils': 3.2.4
chai: 5.3.3
debug: 4.4.3
expect-type: 1.3.0
magic-string: 0.30.21
pathe: 2.0.3
picomatch: 4.0.4
std-env: 3.10.0
tinybench: 2.9.0
tinyexec: 0.3.2
tinyglobby: 0.2.15
tinypool: 1.1.1
tinyrainbow: 2.0.0
vite: 7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)
vite-node: 3.2.4(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 20.19.37
transitivePeerDependencies:
- jiti
- less
- lightningcss
- msw
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- yaml
warning@4.0.3: warning@4.0.3:
dependencies: dependencies:
loose-envify: 1.4.0 loose-envify: 1.4.0
@@ -13458,6 +13827,11 @@ snapshots:
dependencies: dependencies:
isexe: 3.1.5 isexe: 3.1.5
why-is-node-running@2.3.0:
dependencies:
siginfo: 2.0.0
stackback: 0.0.2
word-wrap@1.2.5: {} word-wrap@1.2.5: {}
wrap-ansi@6.2.0: wrap-ansi@6.2.0:

View File

@@ -0,0 +1,22 @@
import { describe, expect, it } from "vitest";
import { assertNodeBelongsToCanvasOrThrow } from "@/convex/ai-utils";
describe("assertNodeBelongsToCanvasOrThrow", () => {
it("accepts matching node/canvas relation", () => {
expect(() =>
assertNodeBelongsToCanvasOrThrow(
{ canvasId: "canvas_a" },
"canvas_a",
),
).not.toThrow();
});
it("rejects mismatching node/canvas relation", () => {
expect(() =>
assertNodeBelongsToCanvasOrThrow(
{ canvasId: "canvas_b" },
"canvas_a",
),
).toThrow("Node does not belong to canvas");
});
});

View File

@@ -0,0 +1,43 @@
import { describe, expect, it } from "vitest";
import type { Id } from "@/convex/_generated/dataModel";
import { validateBatchNodesForUserOrThrow } from "@/convex/batch-validation-utils";
describe("validateBatchNodesForUserOrThrow", () => {
it("rejects mixed canvas ids in one batch", async () => {
const nodeA = {
_id: "node_a" as Id<"nodes">,
canvasId: "canvas_a" as Id<"canvases">,
};
const nodeB = {
_id: "node_b" as Id<"nodes">,
canvasId: "canvas_b" as Id<"canvases">,
};
await expect(
validateBatchNodesForUserOrThrow({
userId: "user_1",
nodeIds: [nodeA._id, nodeB._id],
getNodeById: async (nodeId) => {
if (nodeId === nodeA._id) return nodeA;
if (nodeId === nodeB._id) return nodeB;
return null;
},
getCanvasById: async () => ({ _id: "canvas_a" as Id<"canvases">, ownerId: "user_1" }),
}),
).rejects.toThrow("All nodes must belong to the same canvas");
});
it("rejects foreign canvas ownership", async () => {
const canvasId = "canvas_a" as Id<"canvases">;
const node = { _id: "node_a" as Id<"nodes">, canvasId };
await expect(
validateBatchNodesForUserOrThrow({
userId: "user_1",
nodeIds: [node._id],
getNodeById: async () => node,
getCanvasById: async () => ({ _id: canvasId, ownerId: "other_user" }),
}),
).rejects.toThrow("Canvas not found");
});
});

View File

@@ -0,0 +1,62 @@
import { describe, expect, it } from "vitest";
import {
buildSubscriptionCycleIdempotencyKey,
buildSubscriptionRevokedIdempotencyKey,
buildTopUpPaidIdempotencyKey,
registerWebhookEventOnce,
type WebhookEventRepo,
} from "@/convex/polar-utils";
describe("polar idempotency helpers", () => {
it("builds stable idempotency keys", () => {
expect(
buildSubscriptionCycleIdempotencyKey({
polarSubscriptionId: "sub_123",
currentPeriodStart: 100,
currentPeriodEnd: 200,
}),
).toBe("polar:subscription_cycle:sub_123:100:200");
expect(
buildSubscriptionRevokedIdempotencyKey({
userId: "user_1",
polarSubscriptionId: "sub_123",
}),
).toBe("polar:subscription_revoked:sub_123");
expect(
buildSubscriptionRevokedIdempotencyKey({
userId: "user_1",
}),
).toBe("polar:subscription_revoked:user:user_1");
expect(buildTopUpPaidIdempotencyKey("order_77")).toBe("polar:order_paid:order_77");
});
it("dedupes repeated webhook events by provider/key", async () => {
const seen = new Set<string>();
let inserted = 0;
const repo: WebhookEventRepo = {
findByProviderAndKey: async ({ provider, idempotencyKey }) => {
const key = `${provider}:${idempotencyKey}`;
return seen.has(key) ? { _id: "evt_1" } : null;
},
insert: async ({ provider, idempotencyKey }) => {
seen.add(`${provider}:${idempotencyKey}`);
inserted += 1;
},
};
const args = {
provider: "polar" as const,
scope: "topup_paid" as const,
idempotencyKey: "polar:order_paid:order_1",
userId: "user_1",
polarOrderId: "order_1",
};
await expect(registerWebhookEventOnce(repo, args, () => 123)).resolves.toBe(true);
await expect(registerWebhookEventOnce(repo, args, () => 123)).resolves.toBe(false);
expect(inserted).toBe(1);
});
});

14
vitest.config.ts Normal file
View File

@@ -0,0 +1,14 @@
import path from "node:path";
import { defineConfig } from "vitest/config";
export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "."),
},
},
test: {
environment: "node",
include: ["tests/**/*.test.ts"],
},
});