From b949d2b43636360b8e1bbd240bcc12686066cfb3 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 18 Apr 2026 23:10:42 +0400 Subject: [PATCH 01/13] feat(core): make provider layer pluggable with batch product sync and preflight checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace single syncProduct with batch syncProducts on PaymentProvider interface. Add provider preflight checks (external customers, cross-provider subscriptions) to push and status CLI commands. Lazy provider customer creation — customers are created on the provider only when needed (subscribe, portal, payment method). Remove checkout fallback from upgrade flow since active subscriptions already have payment methods on file. Exclude scheduled subscriptions from duplicate detection warnings. --- CLAUDE.md | 3 + e2e/smoke/setup.ts | 4 +- packages/paykit/src/cli/commands/push.ts | 20 +++++ packages/paykit/src/cli/commands/status.ts | 18 ++++- packages/paykit/src/cli/utils/shared.ts | 66 ++++++++++++++++ .../__tests__/customer.service.test.ts | 4 +- packages/paykit/src/customer/customer.api.ts | 9 +-- .../paykit/src/customer/customer.service.ts | 12 +-- packages/paykit/src/database/schema.ts | 2 +- .../src/product/product-sync.service.ts | 49 ++++++++---- .../paykit/src/product/product.service.ts | 34 +++------ packages/paykit/src/providers/provider.ts | 33 ++++---- .../src/subscription/subscription.service.ts | 57 ++++++++------ .../src/subscription/subscription.types.ts | 2 +- packages/paykit/src/types/events.ts | 2 +- packages/stripe/src/stripe-provider.ts | 75 ++++++++++--------- 16 files changed, 258 insertions(+), 132 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9b9e9d12..021f3492 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,3 +3,6 @@ - do never commit unless you're asked to. - only commit what you're asked to commit, no adding extra changes unless asked - when person asks the question, but not asking you to do the thing, you only answer the question, you don't do the thing you're not asked to do! +- STOP before any file edit. Ask yourself: "Did they explicitly say to make changes?" If no → DO NOT EDIT. Present findings and wait. +- "find", "check", "investigate", "research", "explain", "look into" = RESEARCH ONLY. Never edit code. +- The ONLY trigger for code changes is explicit words like: "do it", "fix it", "implement", "make the change", "go ahead" diff --git a/e2e/smoke/setup.ts b/e2e/smoke/setup.ts index d44f0f8d..4c40963e 100644 --- a/e2e/smoke/setup.ts +++ b/e2e/smoke/setup.ts @@ -150,11 +150,11 @@ export async function createTestPayKit(): Promise { // confirmation which isn't possible in automated tests. (ctx.provider as unknown as Record).createSubscription = async (data: { providerCustomerId: string; - providerPriceId: string; + providerProduct: Record; }) => { const sub = await stripeClient.subscriptions.create({ customer: data.providerCustomerId, - items: [{ price: data.providerPriceId }], + items: [{ price: data.providerProduct.priceId }], payment_behavior: "allow_incomplete", expand: ["latest_invoice"], }); diff --git a/packages/paykit/src/cli/commands/push.ts b/packages/paykit/src/cli/commands/push.ts index 2c09f05a..d143708b 100644 --- a/packages/paykit/src/cli/commands/push.ts +++ b/packages/paykit/src/cli/commands/push.ts @@ -5,7 +5,9 @@ import { Command } from "commander"; import picocolors from "picocolors"; import { + checkActiveSubscriptionsOnOtherProvider, checkProvider, + checkProviderCustomers, createPool, formatProductDiffs, loadCliDeps, @@ -60,6 +62,24 @@ async function pushAction(options: { config?: string; cwd: string; yes?: boolean const { ctx, diffs } = await loadProductDiffs(config, deps); const hasChanges = diffs.some((d) => d.action !== "unchanged"); + // Preflight checks + s.message("Running preflight checks"); + const providerId = config.options.provider.id; + const [subscriptionErrors, customerErrors] = await Promise.all([ + checkActiveSubscriptionsOnOtherProvider(ctx, providerId), + checkProviderCustomers(ctx, providerResult.customerSample), + ]); + const allErrors = [...providerResult.errors, ...subscriptionErrors, ...customerErrors]; + + if (allErrors.length > 0) { + s.stop(""); + for (const err of allErrors) { + p.log.error(err); + } + p.cancel("Push blocked by preflight checks"); + process.exit(1); + } + s.stop(""); // Render all sections diff --git a/packages/paykit/src/cli/commands/status.ts b/packages/paykit/src/cli/commands/status.ts index 851e552b..aaa7415f 100644 --- a/packages/paykit/src/cli/commands/status.ts +++ b/packages/paykit/src/cli/commands/status.ts @@ -5,8 +5,10 @@ import { Command } from "commander"; import picocolors from "picocolors"; import { + checkActiveSubscriptionsOnOtherProvider, checkDatabase, checkProvider, + checkProviderCustomers, createPool, formatProductDiffs, loadCliDeps, @@ -85,6 +87,8 @@ async function statusAction(options: { const pendingMigrations = dbResult.pendingMigrations; + let preflightErrors: string[] = [...providerResult.errors]; + let webhookStatus: string; if (providerResult.webhookEndpoints === null) { webhookStatus = `${picocolors.dim("?")} Could not check webhook status`; @@ -107,6 +111,13 @@ async function statusAction(options: { } else { const { ctx, diffs } = await loadProductDiffs(config, deps); + const providerId = config.options.provider.id; + const [subscriptionErrors, customerErrors] = await Promise.all([ + checkActiveSubscriptionsOnOtherProvider(ctx, providerId), + checkProviderCustomers(ctx, providerResult.customerSample), + ]); + preflightErrors = [...preflightErrors, ...subscriptionErrors, ...customerErrors]; + if (diffs.length === 0) { productsBlock = `Products\n ${picocolors.dim("No products defined")}`; } else { @@ -149,8 +160,13 @@ async function statusAction(options: { p.log.info(productsBlock); + if (preflightErrors.length > 0) { + const errorLines = preflightErrors.map((err) => ` ${picocolors.red("✖")} ${err}`); + p.log.error(`Preflight\n${errorLines.join("\n")}`); + } + const needsMigration = pendingMigrations > 0; - const hasIssues = needsMigration || needsSync; + const hasIssues = needsMigration || needsSync || preflightErrors.length > 0; if (hasIssues) { const action = diff --git a/packages/paykit/src/cli/utils/shared.ts b/packages/paykit/src/cli/utils/shared.ts index 469192f4..8a699dab 100644 --- a/packages/paykit/src/cli/utils/shared.ts +++ b/packages/paykit/src/cli/utils/shared.ts @@ -103,6 +103,8 @@ export async function checkDatabase( export interface ProviderCheckResult { account: { ok: true; displayName: string; mode: string } | { ok: false; message: string }; + customerSample: Array<{ providerEmail: string; paykitCustomerId: string | null }>; + errors: string[]; webhookEndpoints: Array<{ url: string; status: string }> | null; } @@ -116,6 +118,8 @@ export async function checkProvider( if (!result) { return { account: { ok: true, displayName: providerConfig.name, mode: "unknown" }, + customerSample: [], + errors: [], webhookEndpoints: null, }; } @@ -123,23 +127,85 @@ export async function checkProvider( if (result.ok) { return { account: { ok: true, displayName: result.displayName, mode: result.mode }, + customerSample: result.customerSample ?? [], + errors: result.errors ?? [], webhookEndpoints: result.webhookEndpoints ?? null, }; } return { account: { ok: false, message: result.error ?? "Provider check failed" }, + customerSample: [], + errors: result.errors ?? [], webhookEndpoints: null, }; } catch (error) { const message = error instanceof Error ? error.message : "Provider check failed"; return { account: { ok: false, message }, + customerSample: [], + errors: [], webhookEndpoints: null, }; } } +export async function checkProviderCustomers( + ctx: PayKitContext, + customerSample: ProviderCheckResult["customerSample"], +): Promise { + if (customerSample.length === 0) return []; + + const message = `${ctx.provider.name} account has existing customers that are not synced with PayKit. Use a fresh ${ctx.provider.name} account or remove existing customers from it before proceeding.`; + + const hasUnmanaged = customerSample.some((s) => !s.paykitCustomerId); + if (hasUnmanaged) return [message]; + + const paykitIds = customerSample + .map((s) => s.paykitCustomerId) + .filter((id): id is string => id !== null); + + if (paykitIds.length > 0) { + const { customer } = await import("../../database/schema"); + const { inArray } = await import("drizzle-orm"); + const rows = await ctx.database + .select({ id: customer.id }) + .from(customer) + .where(inArray(customer.id, paykitIds)); + if (rows.length < paykitIds.length) return [message]; + } + + return []; +} + +export async function checkActiveSubscriptionsOnOtherProvider( + ctx: PayKitContext, + currentProviderId: string, +): Promise { + const errors: string[] = []; + const { subscription } = await import("../../database/schema"); + const { and, eq, ne, isNotNull, count } = await import("drizzle-orm"); + const rows = await ctx.database + .select({ count: count(), providerId: subscription.providerId }) + .from(subscription) + .where( + and( + eq(subscription.status, "active"), + isNotNull(subscription.providerId), + ne(subscription.providerId, currentProviderId), + ), + ) + .groupBy(subscription.providerId); + for (const row of rows) { + if (row.count > 0 && row.providerId) { + errors.push( + `Found ${String(row.count)} active subscription${row.count === 1 ? "" : "s"} linked to "${row.providerId}" but current provider is "${currentProviderId}". Existing subscriptions must be canceled before switching providers.`, + ); + } + } + return errors; +} + export async function loadProductDiffs( config: LoadedConfig, deps: Pick, diff --git a/packages/paykit/src/customer/__tests__/customer.service.test.ts b/packages/paykit/src/customer/__tests__/customer.service.test.ts index 92136450..4ae3d942 100644 --- a/packages/paykit/src/customer/__tests__/customer.service.test.ts +++ b/packages/paykit/src/customer/__tests__/customer.service.test.ts @@ -76,7 +76,7 @@ describe("customer/service", () => { listActiveSubscriptions: vi.fn(), resumeSubscription: vi.fn(), scheduleSubscriptionChange: vi.fn(), - syncProduct: vi.fn(), + syncProducts: vi.fn(), updateSubscription: vi.fn(), createCustomer: vi.fn().mockResolvedValue({ providerCustomer: { @@ -173,7 +173,7 @@ describe("customer/service", () => { listActiveSubscriptions: vi.fn(), resumeSubscription: vi.fn(), scheduleSubscriptionChange: vi.fn(), - syncProduct: vi.fn(), + syncProducts: vi.fn(), updateSubscription: vi.fn(), createCustomer: vi.fn().mockResolvedValue({ providerCustomer: { diff --git a/packages/paykit/src/customer/customer.api.ts b/packages/paykit/src/customer/customer.api.ts index b6884fee..3961af88 100644 --- a/packages/paykit/src/customer/customer.api.ts +++ b/packages/paykit/src/customer/customer.api.ts @@ -4,10 +4,10 @@ import { definePayKitMethod, returnUrl } from "../api/define-route"; import { PayKitError, PAYKIT_ERROR_CODES } from "../core/errors"; import { getCustomerWithDetails, - getProviderCustomerIdForCustomer, hardDeleteCustomer, listCustomers, upsertCustomer as upsertCustomerService, + upsertProviderCustomer, } from "./customer.service"; const upsertCustomerSchema = z.object({ @@ -60,15 +60,10 @@ export const customerPortal = definePayKitMethod( }, }, async (ctx) => { - const providerCustomerId = await getProviderCustomerIdForCustomer(ctx.paykit.database, { + const { providerCustomerId } = await upsertProviderCustomer(ctx.paykit, { customerId: ctx.customer.id, - providerId: ctx.paykit.provider.id, }); - if (!providerCustomerId) { - throw PayKitError.from("NOT_FOUND", PAYKIT_ERROR_CODES.PROVIDER_CUSTOMER_NOT_FOUND); - } - const { url } = await ctx.paykit.provider.createPortalSession({ providerCustomerId, returnUrl: ctx.input.returnUrl, diff --git a/packages/paykit/src/customer/customer.service.ts b/packages/paykit/src/customer/customer.service.ts index 97163093..6034da3a 100644 --- a/packages/paykit/src/customer/customer.service.ts +++ b/packages/paykit/src/customer/customer.service.ts @@ -182,17 +182,8 @@ export async function upsertCustomer( ): Promise { const syncedCustomer = await syncCustomer(ctx.database, input); await ensureDefaultPlansForCustomer(ctx, syncedCustomer.id); - const { providerCustomer } = await upsertProviderCustomer(ctx, { - customerId: syncedCustomer.id, - }); - return { - ...syncedCustomer, - provider: { - ...(syncedCustomer.provider ?? {}), - [ctx.provider.id]: providerCustomer, - }, - }; + return syncedCustomer; } export async function getCustomerById( @@ -247,6 +238,7 @@ export async function getCustomerWithDetails( const subscriptionsByGroup = new Map(); for (const row of subRows) { + if (row.status === "scheduled") continue; const currentGroup = subscriptionsByGroup.get(row.planGroup) ?? []; currentGroup.push(row.planId); subscriptionsByGroup.set(row.planGroup, currentGroup); diff --git a/packages/paykit/src/database/schema.ts b/packages/paykit/src/database/schema.ts index d26c530b..6aa0776a 100644 --- a/packages/paykit/src/database/schema.ts +++ b/packages/paykit/src/database/schema.ts @@ -64,7 +64,7 @@ export const feature = pgTable("feature", { updatedAt, }); -type ProviderProductMap = Record; +type ProviderProductMap = Record>; export const product = pgTable( "product", diff --git a/packages/paykit/src/product/product-sync.service.ts b/packages/paykit/src/product/product-sync.service.ts index a43ad0f6..8b8bf7ed 100644 --- a/packages/paykit/src/product/product-sync.service.ts +++ b/packages/paykit/src/product/product-sync.service.ts @@ -95,6 +95,15 @@ export async function syncProducts(ctx: PayKitContext): Promise | null; + storedProductInternalId: string; + }> = []; + for (const plan of ctx.plans.plans) { const existing = await getLatestProductSnapshot(ctx.database, plan.id); const existingProviderProduct = existing @@ -151,24 +160,13 @@ export async function syncProducts(ctx: PayKitContext): Promise 0) { + const providerResults = await ctx.provider.syncProducts({ + products: paidPlansToSync.map((p) => ({ + existingProviderProduct: p.existingProviderProduct, + id: p.id, + name: p.name, + priceAmount: p.priceAmount, + priceInterval: p.priceInterval, + })), + }); + + for (const providerResult of providerResults.results) { + const plan = paidPlansToSync.find((p) => p.id === providerResult.id); + if (plan) { + await upsertProviderProduct(ctx.database, { + productInternalId: plan.storedProductInternalId, + providerId, + providerProduct: providerResult.providerProduct, + }); + } + } + } + return results; } diff --git a/packages/paykit/src/product/product.service.ts b/packages/paykit/src/product/product.service.ts index 2b198c61..22dac021 100644 --- a/packages/paykit/src/product/product.service.ts +++ b/packages/paykit/src/product/product.service.ts @@ -13,23 +13,18 @@ export interface StoredProductSnapshot { } export interface StoredProductWithProvider extends StoredProduct { - providerProductId: string | null; - providerPriceId: string | null; + providerProduct: Record | null; } export function withProviderInfo( storedProduct: StoredProduct, providerId: string, ): StoredProductWithProvider { - const providerMap = (storedProduct.provider ?? {}) as Record< - string, - { productId: string; priceId: string | null } - >; + const providerMap = (storedProduct.provider ?? {}) as Record>; const providerInfo = providerMap[providerId]; return { ...storedProduct, - providerProductId: providerInfo?.productId ?? null, - providerPriceId: providerInfo?.priceId ?? null, + providerProduct: providerInfo ?? null, }; } @@ -208,13 +203,13 @@ export async function getProviderProduct( database: PayKitDatabase, productInternalId: string, providerId: string, -): Promise<{ productId: string; priceId: string | null } | null> { +): Promise | null> { const row = await database.query.product.findFirst({ where: eq(product.internalId, productInternalId), }); if (!row) return null; - const providerMap = row.provider as Record; + const providerMap = row.provider as Record>; return providerMap[providerId] ?? null; } @@ -223,8 +218,7 @@ export async function upsertProviderProduct( input: { productInternalId: string; providerId: string; - providerProductId: string; - providerPriceId?: string | null; + providerProduct: Record; }, ): Promise { const existing = await database.query.product.findFirst({ @@ -232,14 +226,8 @@ export async function upsertProviderProduct( }); if (!existing) return; - const providerMap = (existing.provider ?? {}) as Record< - string, - { productId: string; priceId: string | null } - >; - providerMap[input.providerId] = { - productId: input.providerProductId, - priceId: input.providerPriceId ?? null, - }; + const providerMap = (existing.provider ?? {}) as Record>; + providerMap[input.providerId] = input.providerProduct; await database .update(product) @@ -259,12 +247,12 @@ export async function getDefaultProductInGroup( return row ?? null; } -export async function getProductByProviderPriceId( +export async function getProductByProviderData( database: PayKitDatabase, - input: { providerId: string; providerPriceId: string }, + input: { providerId: string; key: string; value: string }, ): Promise { const row = await database.query.product.findFirst({ - where: sql`${product.provider}->${input.providerId}->>'priceId' = ${input.providerPriceId}`, + where: sql`${product.provider}->${input.providerId}->>${input.key} = ${input.value}`, }); return row ?? null; diff --git a/packages/paykit/src/providers/provider.ts b/packages/paykit/src/providers/provider.ts index ef8a2adf..f61ad4fe 100644 --- a/packages/paykit/src/providers/provider.ts +++ b/packages/paykit/src/providers/provider.ts @@ -49,7 +49,6 @@ export interface ProviderSubscription { currentPeriodEndAt?: Date | null; currentPeriodStartAt?: Date | null; endedAt?: Date | null; - providerPriceId?: string | null; providerSubscriptionId: string; providerSubscriptionScheduleId?: string | null; status: string; @@ -95,7 +94,7 @@ export interface PaymentProvider { createSubscriptionCheckout(data: { providerCustomerId: string; - providerPriceId: string; + providerProduct: Record; successUrl: string; cancelUrl?: string; metadata?: Record; @@ -103,11 +102,11 @@ export interface PaymentProvider { createSubscription(data: { providerCustomerId: string; - providerPriceId: string; + providerProduct: Record; }): Promise; updateSubscription(data: { - providerPriceId: string; + providerProduct: Record; providerSubscriptionId: string; }): Promise; @@ -118,7 +117,7 @@ export interface PaymentProvider { }): Promise; scheduleSubscriptionChange(data: { - providerPriceId?: string | null; + providerProduct?: Record | null; providerSubscriptionScheduleId?: string | null; providerSubscriptionId: string; }): Promise; @@ -140,14 +139,20 @@ export interface PaymentProvider { detachPaymentMethod(data: { providerMethodId: string }): Promise; - syncProduct(data: { - id: string; - name: string; - priceAmount: number; - priceInterval?: string | null; - existingProviderProductId?: string | null; - existingProviderPriceId?: string | null; - }): Promise<{ providerProductId: string; providerPriceId: string }>; + syncProducts(data: { + products: Array<{ + id: string; + name: string; + priceAmount: number; + priceInterval?: string | null; + existingProviderProduct?: Record | null; + }>; + }): Promise<{ + results: Array<{ + id: string; + providerProduct: Record; + }>; + }>; handleWebhook(data: { body: string; @@ -164,6 +169,8 @@ export interface PaymentProvider { displayName: string; mode: string; webhookEndpoints?: Array<{ url: string; status: string }>; + errors?: string[]; + customerSample?: Array<{ providerEmail: string; paykitCustomerId: string | null }>; error?: string; }>; } diff --git a/packages/paykit/src/subscription/subscription.service.ts b/packages/paykit/src/subscription/subscription.service.ts index fa959db0..75c97530 100644 --- a/packages/paykit/src/subscription/subscription.service.ts +++ b/packages/paykit/src/subscription/subscription.service.ts @@ -14,7 +14,7 @@ import { getDefaultPaymentMethod } from "../payment-method/payment-method.servic import { getDefaultProductInGroup, getLatestProduct, - getProductByProviderPriceId, + getProductByProviderData, withProviderInfo, } from "../product/product.service"; import type { ProviderRequiredAction, ProviderSubscription } from "../providers/provider"; @@ -98,7 +98,7 @@ export async function loadSubscribeContext(ctx: PayKitContext, input: SubscribeI const isFreeTarget = storedPlan.priceAmount === null; const isPaidTarget = !isFreeTarget; - if (isPaidTarget && !storedPlan.providerPriceId) { + if (isPaidTarget && !storedPlan.providerProduct) { throw PayKitError.from( "INTERNAL_SERVER_ERROR", PAYKIT_ERROR_CODES.PLAN_NOT_SYNCED, @@ -310,12 +310,19 @@ export async function prepareSubscribeCheckoutCompleted( planId, successUrl: "https://paykit.invalid/checkout", }); - if (subCtx.storedPlan.providerPriceId !== checkoutSubscription.providerPriceId) { - throw PayKitError.from( - "BAD_REQUEST", - PAYKIT_ERROR_CODES.PROVIDER_WEBHOOK_INVALID, - `Checkout price mismatch for plan "${planId}"`, + const checkoutProviderProduct = checkoutSubscription.providerProduct; + const storedProviderProduct = subCtx.storedPlan.providerProduct; + if (checkoutProviderProduct && storedProviderProduct) { + const mismatch = Object.entries(checkoutProviderProduct).some( + ([key, value]) => storedProviderProduct[key] !== value, ); + if (mismatch) { + throw PayKitError.from( + "BAD_REQUEST", + PAYKIT_ERROR_CODES.PROVIDER_WEBHOOK_INVALID, + `Checkout product mismatch for plan "${planId}"`, + ); + } } const completion = { @@ -508,11 +515,19 @@ export async function applySubscriptionWebhookAction( providerId: ctx.provider.id, providerSubscriptionId: action.data.subscription.providerSubscriptionId, }); - const storedProduct = action.data.subscription.providerPriceId - ? await getProductByProviderPriceId(ctx.database, { - providerId: ctx.provider.id, - providerPriceId: action.data.subscription.providerPriceId, - }) + const providerProduct = action.data.subscription.providerProduct; + const storedProduct = providerProduct + ? await (async () => { + for (const [key, value] of Object.entries(providerProduct)) { + const found = await getProductByProviderData(ctx.database, { + providerId: ctx.provider.id, + key, + value, + }); + if (found) return found; + } + return null; + })() : null; const normalizedPlan = storedProduct ? (ctx.plans.planMap.get(storedProduct.id) ?? null) : null; @@ -782,7 +797,7 @@ async function handleInitialSubscribe( const providerResult = await ctx.provider.createSubscription({ providerCustomerId: subCtx.providerCustomerId, - providerPriceId: subCtx.storedPlan.providerPriceId!, + providerProduct: subCtx.storedPlan.providerProduct!, }); await ctx.database.transaction(async (tx) => { @@ -839,7 +854,7 @@ async function handleLocalPlanSwitch( const providerResult = await ctx.provider.createSubscription({ providerCustomerId: subCtx.providerCustomerId, - providerPriceId: subCtx.storedPlan.providerPriceId!, + providerProduct: subCtx.storedPlan.providerProduct!, }); await ctx.database.transaction(async (tx) => { @@ -934,7 +949,7 @@ async function handleScheduledDowngrade( } const providerResult = await ctx.provider.scheduleSubscriptionChange({ - providerPriceId: subCtx.storedPlan.providerPriceId!, + providerProduct: subCtx.storedPlan.providerProduct!, providerSubscriptionId: activeSubscriptionRef.subscriptionId, providerSubscriptionScheduleId: activeSubscriptionRef.subscriptionScheduleId, }); @@ -987,12 +1002,8 @@ async function handleUpgrade( throw PayKitError.from("INTERNAL_SERVER_ERROR", PAYKIT_ERROR_CODES.SUBSCRIPTION_CREATE_FAILED); } - if (subCtx.shouldUseCheckout) { - return createCheckoutSubscribe(ctx, subCtx); - } - const providerResult = await ctx.provider.updateSubscription({ - providerPriceId: subCtx.storedPlan.providerPriceId!, + providerProduct: subCtx.storedPlan.providerProduct!, providerSubscriptionId: activeSubscriptionRef.subscriptionId, }); @@ -1036,7 +1047,7 @@ async function createCheckoutSubscribe( paykit_plan_id: subCtx.storedPlan.id, }, providerCustomerId: subCtx.providerCustomerId, - providerPriceId: subCtx.storedPlan.providerPriceId!, + providerProduct: subCtx.storedPlan.providerProduct!, successUrl: subCtx.successUrl, }); @@ -1163,7 +1174,7 @@ function addResetInterval(date: Date, resetInterval: string): Date { return next; } -type ProviderProductMap = Record; +type ProviderProductMap = Record>; export async function warnOnDuplicateActiveSubscriptionGroups( ctx: PayKitContext, @@ -1225,7 +1236,7 @@ function mapJoinRowToSubscriptionWithCatalog(row: { planName: row.product.name, priceAmount: row.product.priceAmount, priceInterval: row.product.priceInterval, - providerPriceId: Object.values(providerMap ?? {})[0]?.priceId ?? null, + providerProduct: Object.values(providerMap ?? {})[0] ?? null, }; } diff --git a/packages/paykit/src/subscription/subscription.types.ts b/packages/paykit/src/subscription/subscription.types.ts index 3db7f28b..c52becfd 100644 --- a/packages/paykit/src/subscription/subscription.types.ts +++ b/packages/paykit/src/subscription/subscription.types.ts @@ -39,5 +39,5 @@ export interface SubscriptionWithCatalog extends StoredSubscription { planName: string; priceAmount: number | null; priceInterval: string | null; - providerPriceId: string | null; + providerProduct: Record | null; } diff --git a/packages/paykit/src/types/events.ts b/packages/paykit/src/types/events.ts index 14903a7e..a9289996 100644 --- a/packages/paykit/src/types/events.ts +++ b/packages/paykit/src/types/events.ts @@ -24,7 +24,7 @@ export interface NormalizedSubscription { currentPeriodEndAt?: Date | null; currentPeriodStartAt?: Date | null; endedAt?: Date | null; - providerPriceId?: string | null; + providerProduct?: Record | null; providerSubscriptionId: string; providerSubscriptionScheduleId?: string | null; status: string; diff --git a/packages/stripe/src/stripe-provider.ts b/packages/stripe/src/stripe-provider.ts index be624ad2..a6d2e1a0 100644 --- a/packages/stripe/src/stripe-provider.ts +++ b/packages/stripe/src/stripe-provider.ts @@ -119,7 +119,7 @@ function normalizeStripeSubscription(subscription: StripeSubscriptionWithExtras) currentPeriodEndAt: toDate(periodEnd), currentPeriodStartAt: toDate(periodStart), endedAt: toDate(subscription.ended_at), - providerPriceId: providerPriceId ?? null, + providerProduct: providerPriceId ? { priceId: providerPriceId } : null, providerSubscriptionId: subscription.id, providerSubscriptionScheduleId: (typeof subscription.schedule === "string" @@ -597,7 +597,7 @@ export function createStripeProvider(client: StripeSdk, options: StripeOptions): cancel_url: data.cancelUrl ?? data.successUrl, client_reference_id: data.providerCustomerId, customer: data.providerCustomerId, - line_items: [{ price: data.providerPriceId, quantity: 1 }], + line_items: [{ price: data.providerProduct.priceId, quantity: 1 }], metadata: data.metadata, mode: "subscription", success_url: data.successUrl, @@ -617,7 +617,7 @@ export function createStripeProvider(client: StripeSdk, options: StripeOptions): async createSubscription(data) { const createParams: StripeSdk.SubscriptionCreateParams = { customer: data.providerCustomerId, - items: [{ price: data.providerPriceId }], + items: [{ price: data.providerProduct.priceId }], payment_behavior: "default_incomplete", expand: ["latest_invoice.payment_intent"], }; @@ -660,7 +660,7 @@ export function createStripeProvider(client: StripeSdk, options: StripeOptions): items: [ { id: currentItem.id, - price: data.providerPriceId, + price: data.providerProduct.priceId, }, ], payment_behavior: "pending_if_incomplete", @@ -687,7 +687,7 @@ export function createStripeProvider(client: StripeSdk, options: StripeOptions): }, async scheduleSubscriptionChange(data) { - if (!data.providerPriceId) { + if (!data.providerProduct?.priceId) { throw PayKitError.from("BAD_REQUEST", PAYKIT_ERROR_CODES.PROVIDER_PRICE_REQUIRED); } @@ -735,7 +735,7 @@ export function createStripeProvider(client: StripeSdk, options: StripeOptions): end_date: periodEndSeconds, }, { - items: [{ price: data.providerPriceId, quantity: 1 }], + items: [{ price: data.providerProduct.priceId, quantity: 1 }], start_date: periodEndSeconds, }, ], @@ -821,35 +821,42 @@ export function createStripeProvider(client: StripeSdk, options: StripeOptions): await client.paymentMethods.detach(data.providerMethodId); }, - async syncProduct(data) { - let providerProductId = data.existingProviderProductId; - if (!providerProductId) { - const stripeProduct = await client.products.create({ - metadata: { paykit_product_id: data.id }, - name: data.name, - }); - providerProductId = stripeProduct.id; - } else { - await client.products.update(providerProductId, { name: data.name }); - } - - if (data.existingProviderPriceId) { - return { providerPriceId: data.existingProviderPriceId, providerProductId }; - } - - const priceParams: StripeSdk.PriceCreateParams = { - currency, - product: providerProductId, - unit_amount: data.priceAmount, - }; - if (data.priceInterval) { - priceParams.recurring = { - interval: data.priceInterval as "month" | "year", - }; - } - const stripePrice = await client.prices.create(priceParams); + async syncProducts(data) { + const results = await Promise.all( + data.products.map(async (product) => { + let productId = product.existingProviderProduct?.productId ?? null; + if (!productId) { + const stripeProduct = await client.products.create({ + metadata: { paykit_product_id: product.id }, + name: product.name, + }); + productId = stripeProduct.id; + } else { + await client.products.update(productId, { name: product.name }); + } + + const existingPriceId = product.existingProviderProduct?.priceId ?? null; + if (existingPriceId) { + return { id: product.id, providerProduct: { productId, priceId: existingPriceId } }; + } + + const priceParams: StripeSdk.PriceCreateParams = { + currency, + product: productId, + unit_amount: product.priceAmount, + }; + if (product.priceInterval) { + priceParams.recurring = { + interval: product.priceInterval as "month" | "year", + }; + } + const stripePrice = await client.prices.create(priceParams); + + return { id: product.id, providerProduct: { productId, priceId: stripePrice.id } }; + }), + ); - return { providerPriceId: stripePrice.id, providerProductId }; + return { results }; }, async createInvoice(data) { From cb55cc29feab7b2a7b01ba40dd032e32f51d947c Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 18 Apr 2026 23:10:55 +0400 Subject: [PATCH 02/13] feat(polar): add Polar payment provider adapter Full Polar adapter with customer CRUD (metadata-based linking, duplicate email fallback), checkout with pre-filled customer, subscription management (upgrade with invoice proration, downgrade with next_period scheduling, cancel at period end, resume with un-cancel + clear pending changes), webhook handling with Standard Webhooks signature verification and event deduplication, batch product sync with orphan archival and org settings auto-configuration, and customer portal via Polar's session API. --- packages/polar/package.json | 40 ++ packages/polar/src/index.ts | 2 + packages/polar/src/polar-provider.ts | 546 +++++++++++++++++++++++++++ packages/polar/tsconfig.json | 7 + packages/polar/tsdown.config.ts | 19 + 5 files changed, 614 insertions(+) create mode 100644 packages/polar/package.json create mode 100644 packages/polar/src/index.ts create mode 100644 packages/polar/src/polar-provider.ts create mode 100644 packages/polar/tsconfig.json create mode 100644 packages/polar/tsdown.config.ts diff --git a/packages/polar/package.json b/packages/polar/package.json new file mode 100644 index 00000000..edb5614e --- /dev/null +++ b/packages/polar/package.json @@ -0,0 +1,40 @@ +{ + "name": "@paykitjs/polar", + "version": "0.0.1", + "description": "Polar provider adapter for PayKit", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/getpaykit/paykit.git" + }, + "files": [ + "dist" + ], + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "publishConfig": { + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "scripts": { + "build": "tsdown --config tsdown.config.ts", + "typecheck": "tsc --build" + }, + "dependencies": { + "@polar-sh/sdk": "^0.47.0", + "paykitjs": "workspace:*" + }, + "devDependencies": { + "tsdown": "^0.21.1", + "typescript": "^5.9.2", + "vitest": "^4.0.18" + } +} diff --git a/packages/polar/src/index.ts b/packages/polar/src/index.ts new file mode 100644 index 00000000..03f68ef3 --- /dev/null +++ b/packages/polar/src/index.ts @@ -0,0 +1,2 @@ +export { polar } from "./polar-provider"; +export type { PolarOptions } from "./polar-provider"; diff --git a/packages/polar/src/polar-provider.ts b/packages/polar/src/polar-provider.ts new file mode 100644 index 00000000..d8bc38d2 --- /dev/null +++ b/packages/polar/src/polar-provider.ts @@ -0,0 +1,546 @@ +import { Polar } from "@polar-sh/sdk"; +import { SDKValidationError } from "@polar-sh/sdk/models/errors/sdkvalidationerror"; +import { validateEvent, WebhookVerificationError } from "@polar-sh/sdk/webhooks"; +import { PayKitError, PAYKIT_ERROR_CODES } from "paykitjs"; +import type { NormalizedWebhookEvent, PayKitProviderConfig, PaymentProvider } from "paykitjs"; + +export interface PolarOptions { + accessToken: string; + webhookSecret: string; + server?: "production" | "sandbox"; +} + +type PolarWebhookEvent = ReturnType; +type PolarSubscriptionEvent = Extract; +type PolarCheckoutEvent = Extract; + +function normalizePolarSubscription(sub: PolarSubscriptionEvent["data"]) { + return { + cancelAtPeriodEnd: sub.cancelAtPeriodEnd, + canceledAt: sub.canceledAt ?? null, + currentPeriodEndAt: sub.currentPeriodEnd ?? null, + currentPeriodStartAt: sub.currentPeriodStart, + endedAt: sub.endedAt ?? null, + providerProduct: { productId: sub.productId }, + providerSubscriptionId: sub.id, + providerSubscriptionScheduleId: null, + status: sub.status, + }; +} + +function createSubscriptionEvents( + event: { type?: string; data: PolarSubscriptionEvent["data"] }, + webhookId: string, +): NormalizedWebhookEvent[] { + const sub = event.data; + + // `subscription.revoked` = immediately terminated (like Stripe delete) + // `subscription.canceled` = will cancel at period end (like Stripe cancel_at_period_end) + if (event.type === "subscription.revoked") { + return [ + { + actions: [ + { + data: { + providerCustomerId: sub.customerId, + providerSubscriptionId: sub.id, + }, + type: "subscription.delete", + }, + ], + name: "subscription.deleted", + payload: { + providerCustomerId: sub.customerId, + providerEventId: webhookId, + providerSubscriptionId: sub.id, + }, + }, + ]; + } + + const normalized = normalizePolarSubscription(sub); + return [ + { + actions: [ + { + data: { + providerCustomerId: sub.customerId, + subscription: normalized, + }, + type: "subscription.upsert", + }, + ], + name: "subscription.updated", + payload: { + providerCustomerId: sub.customerId, + providerEventId: webhookId, + subscription: normalized, + }, + }, + ]; +} + +function createCheckoutEvents( + event: { type?: string; data: PolarCheckoutEvent["data"] }, + webhookId: string, +): NormalizedWebhookEvent[] { + const checkout = event.data; + if (checkout.status !== "succeeded") return []; + + const providerCustomerId = checkout.customerId; + if (!providerCustomerId) return []; + + return [ + { + name: "checkout.completed", + payload: { + checkoutSessionId: checkout.id, + mode: "subscription", + paymentStatus: "paid", + providerCustomerId, + providerEventId: webhookId, + providerSubscriptionId: checkout.subscriptionId ?? undefined, + status: checkout.status, + metadata: checkout.metadata + ? Object.fromEntries(Object.entries(checkout.metadata).map(([k, v]) => [k, String(v)])) + : undefined, + }, + }, + ]; +} + +function notSupported(method: string): never { + throw PayKitError.from( + "BAD_REQUEST", + PAYKIT_ERROR_CODES.PROVIDER_WEBHOOK_INVALID, + `${method} is not supported by the Polar provider`, + ); +} + +export function createPolarProvider(client: Polar, options: PolarOptions): PaymentProvider { + return { + id: "polar", + name: "Polar", + + async createCustomer(data) { + const customerMetadata = { + ...data.metadata, + paykitCustomerId: data.id, + }; + + try { + const customer = await client.customers.create({ + email: data.email ?? "", + name: data.name, + metadata: customerMetadata, + }); + + return { + providerCustomer: { id: customer.id }, + }; + } catch { + // Customer already exists with this email. Find and re-link. + const list = await client.customers.list({ query: data.email ?? "", limit: 1 }); + const existing = list.result.items[0]; + + if (!existing) { + throw PayKitError.from( + "INTERNAL_SERVER_ERROR", + PAYKIT_ERROR_CODES.PROVIDER_CUSTOMER_NOT_FOUND, + "Failed to create or find customer on Polar", + ); + } + + await client.customers.update({ + id: existing.id, + customerUpdate: { + name: data.name, + metadata: customerMetadata, + }, + }); + + return { + providerCustomer: { id: existing.id }, + }; + } + }, + + async updateCustomer(data) { + await client.customers.update({ + id: data.providerCustomerId, + customerUpdate: { + email: data.email, + name: data.name, + metadata: data.metadata ?? {}, + }, + }); + }, + + async deleteCustomer(data) { + await client.customers.delete({ id: data.providerCustomerId }); + }, + + getTestClock() { + return notSupported("getTestClock"); + }, + + advanceTestClock() { + return notSupported("advanceTestClock"); + }, + + attachPaymentMethod() { + return notSupported("attachPaymentMethod"); + }, + + async createSubscriptionCheckout(data) { + const checkout = await client.checkouts.create({ + products: [data.providerProduct.productId!], + customerId: data.providerCustomerId, + successUrl: data.successUrl, + }); + + if (!checkout.url) { + throw PayKitError.from("BAD_REQUEST", PAYKIT_ERROR_CODES.PROVIDER_SESSION_INVALID); + } + + return { + paymentUrl: checkout.url, + providerCheckoutSessionId: checkout.id, + }; + }, + + createSubscription() { + return notSupported("createSubscription (use checkout instead)"); + }, + + async updateSubscription(data) { + const sub = await client.subscriptions.update({ + id: data.providerSubscriptionId, + subscriptionUpdate: { + productId: data.providerProduct.productId!, + prorationBehavior: "invoice", + }, + }); + + return { + paymentUrl: null, + subscription: { + cancelAtPeriodEnd: sub.cancelAtPeriodEnd, + currentPeriodEndAt: sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd) : null, + currentPeriodStartAt: sub.currentPeriodStart ? new Date(sub.currentPeriodStart) : null, + providerSubscriptionId: sub.id, + status: sub.status, + }, + }; + }, + + createInvoice() { + return notSupported("createInvoice"); + }, + + async scheduleSubscriptionChange(data) { + const current = await client.subscriptions.get({ id: data.providerSubscriptionId }); + const wasCanceled = current.cancelAtPeriodEnd; + + // Un-cancel to allow product update (Polar rejects updates on canceled subs) + if (wasCanceled) { + await client.subscriptions.update({ + id: data.providerSubscriptionId, + subscriptionUpdate: { cancelAtPeriodEnd: false }, + }); + } + + await client.subscriptions.update({ + id: data.providerSubscriptionId, + subscriptionUpdate: { + productId: data.providerProduct?.productId!, + prorationBehavior: "next_period", + }, + }); + + // Re-cancel if it was previously canceled (preserve cancel-at-period-end intent) + if (wasCanceled) { + await client.subscriptions.update({ + id: data.providerSubscriptionId, + subscriptionUpdate: { cancelAtPeriodEnd: true }, + }); + } + + const sub = await client.subscriptions.get({ id: data.providerSubscriptionId }); + + return { + paymentUrl: null, + subscription: { + cancelAtPeriodEnd: sub.cancelAtPeriodEnd, + currentPeriodEndAt: sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd) : null, + currentPeriodStartAt: sub.currentPeriodStart ? new Date(sub.currentPeriodStart) : null, + providerSubscriptionId: sub.id, + status: sub.status, + }, + }; + }, + + async cancelSubscription(data) { + const sub = await client.subscriptions.update({ + id: data.providerSubscriptionId, + subscriptionUpdate: { + cancelAtPeriodEnd: true, + }, + }); + + return { + paymentUrl: null, + subscription: { + cancelAtPeriodEnd: sub.cancelAtPeriodEnd, + currentPeriodEndAt: sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd) : null, + currentPeriodStartAt: sub.currentPeriodStart ? new Date(sub.currentPeriodStart) : null, + providerSubscriptionId: sub.id, + status: sub.status, + }, + }; + }, + + async listActiveSubscriptions(data) { + const result = await client.subscriptions.list({ + customerId: data.providerCustomerId, + }); + + return (result.result.items ?? []) + .filter((sub) => sub.status === "active" || sub.status === "trialing") + .map((sub) => ({ providerSubscriptionId: sub.id })); + }, + + async resumeSubscription(data) { + const current = await client.subscriptions.get({ id: data.providerSubscriptionId }); + + // Un-cancel first if pending cancellation + if (current.cancelAtPeriodEnd) { + await client.subscriptions.update({ + id: data.providerSubscriptionId, + subscriptionUpdate: { cancelAtPeriodEnd: false }, + }); + } + + // Clear pending product change if any + const sub = current.pendingUpdate + ? await client.subscriptions.update({ + id: data.providerSubscriptionId, + subscriptionUpdate: { productId: current.productId }, + }) + : await client.subscriptions.get({ id: data.providerSubscriptionId }); + + return { + paymentUrl: null, + subscription: { + cancelAtPeriodEnd: sub.cancelAtPeriodEnd, + currentPeriodEndAt: sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd) : null, + currentPeriodStartAt: sub.currentPeriodStart ? new Date(sub.currentPeriodStart) : null, + providerSubscriptionId: sub.id, + status: sub.status, + }, + }; + }, + + detachPaymentMethod() { + return notSupported("detachPaymentMethod"); + }, + + async syncProducts(data) { + const [allPolarProducts, orgs] = await Promise.all([ + client.products.list({ isArchived: false, limit: 100 }), + client.organizations.list({ limit: 1 }), + ]); + + const org = orgs.result.items?.[0]; + const polarProductMap = new Map((allPolarProducts.result.items ?? []).map((p) => [p.id, p])); + + const activeProductIds = new Set(); + + const results = await Promise.all( + data.products.map(async (product) => { + const existingProductId = product.existingProviderProduct?.productId ?? null; + const existingPolarProduct = existingProductId + ? polarProductMap.get(existingProductId) + : null; + + if (existingPolarProduct) { + const intervalMatches = + existingPolarProduct.recurringInterval === (product.priceInterval ?? null); + + if (intervalMatches) { + const updated = await client.products.update({ + id: existingPolarProduct.id, + productUpdate: { + name: product.name, + visibility: "private", + prices: [ + { + amountType: "fixed" as const, + priceAmount: product.priceAmount, + priceCurrency: "usd", + }, + ], + }, + }); + activeProductIds.add(updated.id); + return { id: product.id, providerProduct: { productId: updated.id } }; + } + + // Interval changed — archive old, create new + await client.products.update({ + id: existingPolarProduct.id, + productUpdate: { isArchived: true }, + }); + } + + const created = await client.products.create({ + name: product.name, + visibility: "private", + recurringInterval: (product.priceInterval as "month" | "year") ?? null, + prices: [ + { + amountType: "fixed" as const, + priceAmount: product.priceAmount, + priceCurrency: "usd", + }, + ], + }); + activeProductIds.add(created.id); + return { id: product.id, providerProduct: { productId: created.id } }; + }), + ); + + // Archive orphans + configure org settings in parallel + const cleanup: Promise[] = []; + + for (const [polarId] of polarProductMap) { + if (!activeProductIds.has(polarId)) { + cleanup.push( + client.products.update({ + id: polarId, + productUpdate: { isArchived: true }, + }), + ); + } + } + + if (org) { + cleanup.push( + client.organizations.update({ + id: org.id, + organizationUpdate: { + subscriptionSettings: { + allowMultipleSubscriptions: true, + allowCustomerUpdates: false, + prorationBehavior: "invoice", + benefitRevocationGracePeriod: org.subscriptionSettings.benefitRevocationGracePeriod, + preventTrialAbuse: org.subscriptionSettings.preventTrialAbuse, + }, + customerPortalSettings: { + subscription: { updateSeats: false, updatePlan: false }, + usage: org.customerPortalSettings.usage, + }, + }, + }), + ); + } + + await Promise.all(cleanup); + + return { results }; + }, + + async handleWebhook(data): Promise { + const webhookIdKey = Object.keys(data.headers).find((k) => k.toLowerCase() === "webhook-id"); + const webhookId = webhookIdKey ? data.headers[webhookIdKey]! : ""; + + let event: ReturnType; + try { + event = validateEvent(data.body, data.headers, options.webhookSecret); + } catch (error) { + if (error instanceof WebhookVerificationError) { + throw PayKitError.from( + "BAD_REQUEST", + PAYKIT_ERROR_CODES.PROVIDER_SIGNATURE_MISSING, + "Invalid Polar webhook signature", + ); + } + // Unknown event types (e.g. member.created) — ignore silently + if (error instanceof SDKValidationError) { + return []; + } + throw error; + } + + switch (event.type) { + case "subscription.created": + case "subscription.updated": + case "subscription.active": + case "subscription.uncanceled": + case "subscription.canceled": + case "subscription.revoked": + return createSubscriptionEvents(event, webhookId); + case "checkout.created": + case "checkout.updated": + return createCheckoutEvents(event, webhookId); + default: + return []; + } + }, + + async createPortalSession(data) { + const session = await client.customerSessions.create({ + customerId: data.providerCustomerId, + }); + + return { + url: session.customerPortalUrl, + }; + }, + + async check() { + try { + await client.products.list({ limit: 1 }); + + const customers = await client.customers.list({ + limit: 5, + sorting: ["created_at"], + }); + const customerSample = (customers.result.items ?? []).map((c) => ({ + providerEmail: c.email ?? "", + paykitCustomerId: (c.metadata?.paykitCustomerId as string) ?? null, + })); + + return { + ok: true, + displayName: "Polar", + mode: options.server === "sandbox" ? "sandbox" : "production", + webhookEndpoints: [], + customerSample, + }; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return { + ok: false, + displayName: "Polar", + mode: options.server === "sandbox" ? "sandbox" : "production", + error: message, + }; + } + }, + }; +} + +export function polar(polarOptions: PolarOptions): PayKitProviderConfig { + return { + id: "polar", + name: "Polar", + createAdapter(): PaymentProvider { + const client = new Polar({ + accessToken: polarOptions.accessToken, + server: polarOptions.server ?? "production", + }); + return createPolarProvider(client, polarOptions); + }, + }; +} diff --git a/packages/polar/tsconfig.json b/packages/polar/tsconfig.json new file mode 100644 index 00000000..da8829fe --- /dev/null +++ b/packages/polar/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/polar/tsdown.config.ts b/packages/polar/tsdown.config.ts new file mode 100644 index 00000000..7b525feb --- /dev/null +++ b/packages/polar/tsdown.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + clean: true, + deps: { + onlyAllowBundle: false, + skipNodeModulesBundle: true, + }, + dts: true, + entry: { + index: "src/index.ts", + }, + fixedExtension: false, + format: "esm", + outDir: "dist", + platform: "node", + target: "node22", + unbundle: true, +}); From 94a8992846743cdc2ee731c99dec78ca3bedf641 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 18 Apr 2026 23:11:07 +0400 Subject: [PATCH 03/13] chore(demo): switch demo app to Polar provider Configure demo app with Polar adapter, add POLAR_ACCESS_TOKEN and POLAR_WEBHOOK_SECRET env vars, simplify auth form by removing name field. --- apps/demo/package.json | 1 + apps/demo/src/app/_components/auth-form.tsx | 14 +- apps/demo/src/env.js | 4 + apps/demo/src/server/paykit.ts | 14 +- pnpm-lock.yaml | 985 +++++++++++--------- 5 files changed, 537 insertions(+), 481 deletions(-) diff --git a/apps/demo/package.json b/apps/demo/package.json index 0c6c9e61..ae5054ba 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -19,6 +19,7 @@ "dependencies": { "@base-ui/react": "^1.2.0", "@paykitjs/dash": "workspace:^", + "@paykitjs/polar": "workspace:*", "@paykitjs/stripe": "workspace:*", "@t3-oss/env-nextjs": "^0.12.0", "@tanstack/react-query": "^5.69.0", diff --git a/apps/demo/src/app/_components/auth-form.tsx b/apps/demo/src/app/_components/auth-form.tsx index 94a2e306..24def59e 100644 --- a/apps/demo/src/app/_components/auth-form.tsx +++ b/apps/demo/src/app/_components/auth-form.tsx @@ -13,7 +13,6 @@ export function AuthForm({ redirectTo }: { redirectTo: string }) { const router = useRouter(); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); - const [name, setName] = useState(""); const [isSignUp, setIsSignUp] = useState(false); const [error, setError] = useState(""); const [loading, setLoading] = useState(false); @@ -28,7 +27,7 @@ export function AuthForm({ redirectTo }: { redirectTo: string }) { const result = await authClient.signUp.email({ email, password, - name, + name: "Demo User", }); if (result.error) { setError(result.error.message ?? "Sign up failed"); @@ -60,17 +59,6 @@ export function AuthForm({ redirectTo }: { redirectTo: string }) {
- {isSignUp ? ( -
- - setName(event.target.value)} - placeholder="Your name" - value={name} - /> -
- ) : null}
=10'} '@asamuzakjp/css-color@5.1.9': - resolution: {integrity: sha512-zd9c/Wdso6v1U7v6w3i/hbAr4K7NaSHImdpvmLt+Y9ea5BhilnIGNkfhOJ7FEIuPipAnE9tZeDOll05WDT0kgg==} + resolution: {integrity: sha512-zd9c/Wdso6v1U7v6w3i/hbAr4K7NaSHImdpvmLt+Y9ea5BhilnIGNkfhOJ7FEIuPipAnE9tZeDOll05WDT0kgg==, tarball: https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.9.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} '@asamuzakjp/dom-selector@6.8.1': - resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==, tarball: https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz} '@asamuzakjp/nwsapi@2.3.9': - resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==, tarball: https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz} '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} @@ -778,71 +800,71 @@ packages: hasBin: true '@biomejs/cli-darwin-arm64@2.4.6': - resolution: {integrity: sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ==} + resolution: {integrity: sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ==, tarball: https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.6.tgz} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] '@biomejs/cli-darwin-x64@2.4.6': - resolution: {integrity: sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw==} + resolution: {integrity: sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw==, tarball: https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.6.tgz} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] '@biomejs/cli-linux-arm64-musl@2.4.6': - resolution: {integrity: sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A==} + resolution: {integrity: sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.6.tgz} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] '@biomejs/cli-linux-arm64@2.4.6': - resolution: {integrity: sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew==} + resolution: {integrity: sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.6.tgz} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] '@biomejs/cli-linux-x64-musl@2.4.6': - resolution: {integrity: sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg==} + resolution: {integrity: sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.6.tgz} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] '@biomejs/cli-linux-x64@2.4.6': - resolution: {integrity: sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw==} + resolution: {integrity: sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw==, tarball: https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.6.tgz} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] '@biomejs/cli-win32-arm64@2.4.6': - resolution: {integrity: sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg==} + resolution: {integrity: sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg==, tarball: https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.6.tgz} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] '@biomejs/cli-win32-x64@2.4.6': - resolution: {integrity: sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg==} + resolution: {integrity: sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg==, tarball: https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.6.tgz} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] '@borewit/text-codec@0.2.2': - resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} + resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==, tarball: https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz} '@bramus/specificity@2.4.2': - resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==, tarball: https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz} hasBin: true '@chevrotain/cst-dts-gen@10.5.0': - resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==} + resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==, tarball: https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz} '@chevrotain/gast@10.5.0': - resolution: {integrity: sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==} + resolution: {integrity: sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==, tarball: https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz} '@chevrotain/types@10.5.0': - resolution: {integrity: sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==} + resolution: {integrity: sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==, tarball: https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz} '@chevrotain/utils@10.5.0': - resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==} + resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==, tarball: https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz} '@clack/core@1.1.0': resolution: {integrity: sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==} @@ -851,31 +873,31 @@ packages: resolution: {integrity: sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==} '@csstools/color-helpers@6.0.2': - resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==, tarball: https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz} engines: {node: '>=20.19.0'} '@csstools/css-calc@3.1.1': - resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} + resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==, tarball: https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 '@csstools/css-color-parser@4.0.2': - resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} + resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==, tarball: https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz} 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==} + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==, tarball: https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-tokenizer': ^4.0.0 '@csstools/css-syntax-patches-for-csstree@1.1.2': - resolution: {integrity: sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==} + resolution: {integrity: sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==, tarball: https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.2.tgz} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -883,7 +905,7 @@ packages: optional: true '@csstools/css-tokenizer@4.0.0': - resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==, tarball: https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz} engines: {node: '>=20.19.0'} '@date-fns/tz@1.4.1': @@ -903,27 +925,27 @@ packages: '@noble/ciphers': ^1.0.0 '@electric-sql/pglite-socket@0.0.20': - resolution: {integrity: sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==} + resolution: {integrity: sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==, tarball: https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.0.20.tgz} hasBin: true peerDependencies: '@electric-sql/pglite': 0.3.15 '@electric-sql/pglite-tools@0.2.20': - resolution: {integrity: sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==} + resolution: {integrity: sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==, tarball: https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.2.20.tgz} peerDependencies: '@electric-sql/pglite': 0.3.15 '@electric-sql/pglite@0.3.15': - resolution: {integrity: sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==} + resolution: {integrity: sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==, tarball: https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz} '@emnapi/core@1.8.1': - resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==, tarball: https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz} '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz} '@emnapi/wasi-threads@1.1.0': - resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==, tarball: https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz} '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} @@ -934,451 +956,451 @@ packages: deprecated: 'Merged into tsx: https://tsx.is' '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==, tarball: https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==, tarball: https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/android-arm64@0.18.20': - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm64] os: [android] '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz} engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz} engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm@0.18.20': - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm] os: [android] '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz} engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz} engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-x64@0.18.20': - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [android] '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz} engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz} engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/darwin-arm64@0.18.20': - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm64] os: [darwin] '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz} engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz} engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.18.20': - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [darwin] '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz} engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz} engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/freebsd-arm64@0.18.20': - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.18.20': - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [freebsd] '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz} engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz} engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/linux-arm64@0.18.20': - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm64] os: [linux] '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz} engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz} engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.18.20': - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm] os: [linux] '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz} engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz} engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.18.20': - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz} engines: {node: '>=12'} cpu: [ia32] os: [linux] '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz} engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz} engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.18.20': - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz} engines: {node: '>=12'} cpu: [loong64] os: [linux] '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz} engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz} engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.18.20': - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz} engines: {node: '>=12'} cpu: [mips64el] os: [linux] '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz} engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz} engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.18.20': - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz} engines: {node: '>=12'} cpu: [ppc64] os: [linux] '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz} engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.18.20': - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz} engines: {node: '>=12'} cpu: [riscv64] os: [linux] '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz} engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz} engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.18.20': - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz} engines: {node: '>=12'} cpu: [s390x] os: [linux] '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz} engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz} engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.18.20': - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [linux] '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz} engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz} engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==, tarball: https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==, tarball: https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.18.20': - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [netbsd] '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz} engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz} engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==, tarball: https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==, tarball: https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.18.20': - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [openbsd] '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz} engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz} engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==, tarball: https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==, tarball: https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] '@esbuild/sunos-x64@0.18.20': - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [sunos] '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz} engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz} engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/win32-arm64@0.18.20': - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm64] os: [win32] '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz} engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz} engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.18.20': - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz} engines: {node: '>=12'} cpu: [ia32] os: [win32] '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz} engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz} engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.18.20': - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [win32] '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz} engines: {node: '>=18'} cpu: [x64] os: [win32] '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz} engines: {node: '>=18'} cpu: [x64] os: [win32] '@exodus/bytes@1.15.0': - resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} + resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==, tarball: https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: '@noble/hashes': ^1.8.0 || ^2.0.0 @@ -1437,134 +1459,134 @@ packages: engines: {node: '>=18'} '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==, tarball: https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==, tarball: https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==, tarball: https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz} cpu: [arm64] os: [darwin] '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==, tarball: https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz} cpu: [x64] os: [darwin] '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz} cpu: [arm64] os: [linux] '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz} cpu: [arm] os: [linux] '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz} cpu: [ppc64] os: [linux] '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz} cpu: [riscv64] os: [linux] '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz} cpu: [s390x] os: [linux] '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz} cpu: [x64] os: [linux] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz} cpu: [arm64] os: [linux] '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz} cpu: [x64] os: [linux] '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==, tarball: https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==, tarball: https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==, tarball: https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==, tarball: https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==, tarball: https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==, tarball: https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==, tarball: https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==, tarball: https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==, tarball: https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==, tarball: https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==, tarball: https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==, tarball: https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] @@ -1740,7 +1762,7 @@ packages: optional: true '@mrleebo/prisma-ast@0.13.1': - resolution: {integrity: sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==} + resolution: {integrity: sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==, tarball: https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.13.1.tgz} engines: {node: '>=16'} '@mswjs/interceptors@0.41.3': @@ -1748,10 +1770,10 @@ packages: engines: {node: '>=18'} '@napi-rs/wasm-runtime@1.1.1': - resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==, tarball: https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz} '@napi-rs/wasm-runtime@1.1.3': - resolution: {integrity: sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==} + resolution: {integrity: sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==, tarball: https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 @@ -1760,49 +1782,49 @@ packages: resolution: {integrity: sha512-ZWXyj4uNu4GCWQw9cjRxWlbD+33mcDszIo9iQxFnBX3Wmgq9ulaSJcl6VhuWx5pCWqqD+9W6Wfz7N0lM5lYPMA==} '@next/swc-darwin-arm64@16.2.3': - resolution: {integrity: sha512-u37KDKTKQ+OQLvY+z7SNXixwo4Q2/IAJFDzU1fYe66IbCE51aDSAzkNDkWmLN0yjTUh4BKBd+hb69jYn6qqqSg==} + resolution: {integrity: sha512-u37KDKTKQ+OQLvY+z7SNXixwo4Q2/IAJFDzU1fYe66IbCE51aDSAzkNDkWmLN0yjTUh4BKBd+hb69jYn6qqqSg==, tarball: https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.3.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] '@next/swc-darwin-x64@16.2.3': - resolution: {integrity: sha512-gHjL/qy6Q6CG3176FWbAKyKh9IfntKZTB3RY/YOJdDFpHGsUDXVH38U4mMNpHVGXmeYW4wj22dMp1lTfmu/bTQ==} + resolution: {integrity: sha512-gHjL/qy6Q6CG3176FWbAKyKh9IfntKZTB3RY/YOJdDFpHGsUDXVH38U4mMNpHVGXmeYW4wj22dMp1lTfmu/bTQ==, tarball: https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.3.tgz} engines: {node: '>= 10'} cpu: [x64] os: [darwin] '@next/swc-linux-arm64-gnu@16.2.3': - resolution: {integrity: sha512-U6vtblPtU/P14Y/b/n9ZY0GOxbbIhTFuaFR7F4/uMBidCi2nSdaOFhA0Go81L61Zd6527+yvuX44T4ksnf8T+Q==} + resolution: {integrity: sha512-U6vtblPtU/P14Y/b/n9ZY0GOxbbIhTFuaFR7F4/uMBidCi2nSdaOFhA0Go81L61Zd6527+yvuX44T4ksnf8T+Q==, tarball: https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.3.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [linux] '@next/swc-linux-arm64-musl@16.2.3': - resolution: {integrity: sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==} + resolution: {integrity: sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==, tarball: https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.3.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [linux] '@next/swc-linux-x64-gnu@16.2.3': - resolution: {integrity: sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==} + resolution: {integrity: sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==, tarball: https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.3.tgz} engines: {node: '>= 10'} cpu: [x64] os: [linux] '@next/swc-linux-x64-musl@16.2.3': - resolution: {integrity: sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==} + resolution: {integrity: sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==, tarball: https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.3.tgz} engines: {node: '>= 10'} cpu: [x64] os: [linux] '@next/swc-win32-arm64-msvc@16.2.3': - resolution: {integrity: sha512-O2NZ9ie3Tq6xj5Z5CSwBT3+aWAMW2PIZ4egUi9MaWLkwaehgtB7YZjPm+UpcNpKOme0IQuqDcor7BsW6QBiQBw==} + resolution: {integrity: sha512-O2NZ9ie3Tq6xj5Z5CSwBT3+aWAMW2PIZ4egUi9MaWLkwaehgtB7YZjPm+UpcNpKOme0IQuqDcor7BsW6QBiQBw==, tarball: https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.3.tgz} engines: {node: '>= 10'} cpu: [arm64] os: [win32] '@next/swc-win32-x64-msvc@16.2.3': - resolution: {integrity: sha512-Ibm29/GgB/ab5n7XKqlStkm54qqZE8v2FnijUPBgrd67FWrac45o/RsNlaOWjme/B5UqeWt/8KM4aWBwA1D2Kw==} + resolution: {integrity: sha512-Ibm29/GgB/ab5n7XKqlStkm54qqZE8v2FnijUPBgrd67FWrac45o/RsNlaOWjme/B5UqeWt/8KM4aWBwA1D2Kw==, tarball: https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.3.tgz} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1849,7 +1871,7 @@ packages: resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} '@opentelemetry/api@1.9.0': - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==, tarball: https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz} engines: {node: '>=8.0.0'} '@opentelemetry/semantic-conventions@1.40.0': @@ -1879,120 +1901,120 @@ packages: resolution: {integrity: sha512-bLE7BtHrURedCn4Mco3ma9L4Y1GR2SMBuIvjWr7rmQ4/W/4Jy70TIAgZ+0nIlk0xHz1vNP8x8DCns45Sb2XRbg==} '@oxc-parser/binding-android-arm-eabi@0.124.0': - resolution: {integrity: sha512-+R9zCafSL8ovjokdPtorUp3sXrh8zQ2AC2L0ivXNvlLR0WS+5WdPkNVrnENq5UvzagM4Xgl0NPsJKz3Hv9+y8g==} + resolution: {integrity: sha512-+R9zCafSL8ovjokdPtorUp3sXrh8zQ2AC2L0ivXNvlLR0WS+5WdPkNVrnENq5UvzagM4Xgl0NPsJKz3Hv9+y8g==, tarball: https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@oxc-parser/binding-android-arm64@0.124.0': - resolution: {integrity: sha512-ULHC/gVZ+nP4pd3kNNQTYaQ/e066BW/KuY5qUsvwkVWwOUQGDg+WpfyVOmQ4xfxoue6cMlkKkJ+ntdzfDXpNlg==} + resolution: {integrity: sha512-ULHC/gVZ+nP4pd3kNNQTYaQ/e066BW/KuY5qUsvwkVWwOUQGDg+WpfyVOmQ4xfxoue6cMlkKkJ+ntdzfDXpNlg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@oxc-parser/binding-darwin-arm64@0.124.0': - resolution: {integrity: sha512-fGJ2hw7bnbUYn6UvTjp0m4WJ9zXz3cohgcwcgeo7gUZehpPNpvcVEVeIVHNmHnAuAw/ysf4YJR8DA1E+xCA4Lw==} + resolution: {integrity: sha512-fGJ2hw7bnbUYn6UvTjp0m4WJ9zXz3cohgcwcgeo7gUZehpPNpvcVEVeIVHNmHnAuAw/ysf4YJR8DA1E+xCA4Lw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@oxc-parser/binding-darwin-x64@0.124.0': - resolution: {integrity: sha512-j0+re9pgps5BH2Tk3fm59Hi3QuLP3C4KhqXi6A+wRHHHJWDFR8mc/KI9mBrfk2JRT+15doGo+zv1eN75/9DuOw==} + resolution: {integrity: sha512-j0+re9pgps5BH2Tk3fm59Hi3QuLP3C4KhqXi6A+wRHHHJWDFR8mc/KI9mBrfk2JRT+15doGo+zv1eN75/9DuOw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@oxc-parser/binding-freebsd-x64@0.124.0': - resolution: {integrity: sha512-0k5mS0npnrhKy72UfF51lpOZ2ESoPWn6gdFw+RdeRWcokraDW1O2kSx3laQ+yk7cCEavQdJSpWCYS/GvBbUCXQ==} + resolution: {integrity: sha512-0k5mS0npnrhKy72UfF51lpOZ2ESoPWn6gdFw+RdeRWcokraDW1O2kSx3laQ+yk7cCEavQdJSpWCYS/GvBbUCXQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@oxc-parser/binding-linux-arm-gnueabihf@0.124.0': - resolution: {integrity: sha512-P/i4eguRWvAUfGdfhQYg1jpwYkyUV6D3gefIH7HhmRl1Ph6P4IqTIEVcyJr1i/3vr1V5OHU4wonH6/ue/Qzvrw==} + resolution: {integrity: sha512-P/i4eguRWvAUfGdfhQYg1jpwYkyUV6D3gefIH7HhmRl1Ph6P4IqTIEVcyJr1i/3vr1V5OHU4wonH6/ue/Qzvrw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxc-parser/binding-linux-arm-musleabihf@0.124.0': - resolution: {integrity: sha512-/ameqFQH5fFP+66Atr8Ynv/2rYe4utcU7L4MoWS5JtrFLVO78g4qDLavyIlJxa6caSwYOvG/eO3c/DXqY5/6Rw==} + resolution: {integrity: sha512-/ameqFQH5fFP+66Atr8Ynv/2rYe4utcU7L4MoWS5JtrFLVO78g4qDLavyIlJxa6caSwYOvG/eO3c/DXqY5/6Rw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxc-parser/binding-linux-arm64-gnu@0.124.0': - resolution: {integrity: sha512-gNeyEcXTtfrRCbj2EfxWU85Fs0wIX3p44Y3twnvuMfkWlLrb9M1Z25AYNSKjJM+fdAjeeQCjw0on47zFuBYwQw==} + resolution: {integrity: sha512-gNeyEcXTtfrRCbj2EfxWU85Fs0wIX3p44Y3twnvuMfkWlLrb9M1Z25AYNSKjJM+fdAjeeQCjw0on47zFuBYwQw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@oxc-parser/binding-linux-arm64-musl@0.124.0': - resolution: {integrity: sha512-uvG7v4Tz9S8/PVqY0SP0DLHxo4hZGe+Pv2tGVnwcsjKCCUPjplbrFVvDzXq+kOaEoUkiCY0Kt1hlZ6FDJ1LKNQ==} + resolution: {integrity: sha512-uvG7v4Tz9S8/PVqY0SP0DLHxo4hZGe+Pv2tGVnwcsjKCCUPjplbrFVvDzXq+kOaEoUkiCY0Kt1hlZ6FDJ1LKNQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@oxc-parser/binding-linux-ppc64-gnu@0.124.0': - resolution: {integrity: sha512-t7KZaaUhfp2au0MRpoENEFqwLKYDdptEry6V7pTAVdPEcFG4P6ii8yeGU9m6p5vb+b8WEKmdpGMNXBEYy7iJdw==} + resolution: {integrity: sha512-t7KZaaUhfp2au0MRpoENEFqwLKYDdptEry6V7pTAVdPEcFG4P6ii8yeGU9m6p5vb+b8WEKmdpGMNXBEYy7iJdw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] '@oxc-parser/binding-linux-riscv64-gnu@0.124.0': - resolution: {integrity: sha512-eurGGaxHZiIQ+fBSageS8TAkRqZgdOiBeqNrWAqAPup9hXBTmQ0WcBjwsLElf+3jvDL9NhnX0dOgOqPfsjSjdg==} + resolution: {integrity: sha512-eurGGaxHZiIQ+fBSageS8TAkRqZgdOiBeqNrWAqAPup9hXBTmQ0WcBjwsLElf+3jvDL9NhnX0dOgOqPfsjSjdg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] '@oxc-parser/binding-linux-riscv64-musl@0.124.0': - resolution: {integrity: sha512-d1V7/ll1i/LhqE/gZy6Wbz6evlk0egh2XKkwMI3epiojtbtUwQSLIER0Y3yDBBocPuWOjJdvmjtEmPTTLXje/w==} + resolution: {integrity: sha512-d1V7/ll1i/LhqE/gZy6Wbz6evlk0egh2XKkwMI3epiojtbtUwQSLIER0Y3yDBBocPuWOjJdvmjtEmPTTLXje/w==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] '@oxc-parser/binding-linux-s390x-gnu@0.124.0': - resolution: {integrity: sha512-w1+cBvriUteOpox6ATqCFVkpGL47PFdcfCPGmgUZbd78Fw44U0gQkc+kVGvAOTvGrptMYgwomD1c6OTVvkrpGg==} + resolution: {integrity: sha512-w1+cBvriUteOpox6ATqCFVkpGL47PFdcfCPGmgUZbd78Fw44U0gQkc+kVGvAOTvGrptMYgwomD1c6OTVvkrpGg==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] '@oxc-parser/binding-linux-x64-gnu@0.124.0': - resolution: {integrity: sha512-RRB1evQiXRtMCsQQiAh9U0H3HzguLpE0ytfStuhRgmOj7tqUCOVxkHsvM9geZjAax6NqVRj7VXx32qjjkZPsBw==} + resolution: {integrity: sha512-RRB1evQiXRtMCsQQiAh9U0H3HzguLpE0ytfStuhRgmOj7tqUCOVxkHsvM9geZjAax6NqVRj7VXx32qjjkZPsBw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@oxc-parser/binding-linux-x64-musl@0.124.0': - resolution: {integrity: sha512-asVYN0qmSHlCU8H9Q47SmeJ/Z5EG4IWCC+QGxkfFboI5qh15aLlJnHmnrV61MwQRPXGnVC/sC3qKhrUyqGxUqw==} + resolution: {integrity: sha512-asVYN0qmSHlCU8H9Q47SmeJ/Z5EG4IWCC+QGxkfFboI5qh15aLlJnHmnrV61MwQRPXGnVC/sC3qKhrUyqGxUqw==, tarball: https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@oxc-parser/binding-openharmony-arm64@0.124.0': - resolution: {integrity: sha512-nhwuxm6B8pn9lzAzMUfa571L5hCXYwQo8C8cx5aGOuHWCzruR8gPJnRRXGBci+uGaIIQEZDyU/U6HDgrSp/JlQ==} + resolution: {integrity: sha512-nhwuxm6B8pn9lzAzMUfa571L5hCXYwQo8C8cx5aGOuHWCzruR8gPJnRRXGBci+uGaIIQEZDyU/U6HDgrSp/JlQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@oxc-parser/binding-wasm32-wasi@0.124.0': - resolution: {integrity: sha512-LWuq4Dl9tff7n+HjJcqoBjDlVCtruc0shgtdtGM+rTUIE9aFxHA/P+wCYR+aWMjN8m9vNaRME/sKXErmhmeKrA==} + resolution: {integrity: sha512-LWuq4Dl9tff7n+HjJcqoBjDlVCtruc0shgtdtGM+rTUIE9aFxHA/P+wCYR+aWMjN8m9vNaRME/sKXErmhmeKrA==, tarball: https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.124.0.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] '@oxc-parser/binding-win32-arm64-msvc@0.124.0': - resolution: {integrity: sha512-aOh3Lf3AeH0dgzT4yBXcArFZ8VhqNXwZ/xlN0GqBtgVaGoHOOqL2YHlcVIgT+ghsXPVR2PTtYgBiQ1CNK7jp5A==} + resolution: {integrity: sha512-aOh3Lf3AeH0dgzT4yBXcArFZ8VhqNXwZ/xlN0GqBtgVaGoHOOqL2YHlcVIgT+ghsXPVR2PTtYgBiQ1CNK7jp5A==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@oxc-parser/binding-win32-ia32-msvc@0.124.0': - resolution: {integrity: sha512-sib5xC0nz/+SCpaETBuHBz4SXS02KuG5HtyOcHsO/SK5ZvLRGhOZx0elDKawjb6adFkD7dQCqpXUS25wY6ELKQ==} + resolution: {integrity: sha512-sib5xC0nz/+SCpaETBuHBz4SXS02KuG5HtyOcHsO/SK5ZvLRGhOZx0elDKawjb6adFkD7dQCqpXUS25wY6ELKQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] '@oxc-parser/binding-win32-x64-msvc@0.124.0': - resolution: {integrity: sha512-UgojtjGUgZgAZQYt7SC6VO65OVdxEkRe2q+2vbHJO//18qw3Hrk6UvHGQKldsQKgbVcIBT/YBrt85YberiYIPQ==} + resolution: {integrity: sha512-UgojtjGUgZgAZQYt7SC6VO65OVdxEkRe2q+2vbHJO//18qw3Hrk6UvHGQKldsQKgbVcIBT/YBrt85YberiYIPQ==, tarball: https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.124.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2007,347 +2029,350 @@ packages: resolution: {integrity: sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==} '@oxc-resolver/binding-android-arm-eabi@11.19.1': - resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} + resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.19.1.tgz} cpu: [arm] os: [android] '@oxc-resolver/binding-android-arm64@11.19.1': - resolution: {integrity: sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA==} + resolution: {integrity: sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.19.1.tgz} cpu: [arm64] os: [android] '@oxc-resolver/binding-darwin-arm64@11.19.1': - resolution: {integrity: sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ==} + resolution: {integrity: sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.19.1.tgz} cpu: [arm64] os: [darwin] '@oxc-resolver/binding-darwin-x64@11.19.1': - resolution: {integrity: sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ==} + resolution: {integrity: sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.19.1.tgz} cpu: [x64] os: [darwin] '@oxc-resolver/binding-freebsd-x64@11.19.1': - resolution: {integrity: sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw==} + resolution: {integrity: sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.19.1.tgz} cpu: [x64] os: [freebsd] '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': - resolution: {integrity: sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A==} + resolution: {integrity: sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.19.1.tgz} cpu: [arm] os: [linux] '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': - resolution: {integrity: sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ==} + resolution: {integrity: sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.19.1.tgz} cpu: [arm] os: [linux] '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': - resolution: {integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==} + resolution: {integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.19.1.tgz} cpu: [arm64] os: [linux] '@oxc-resolver/binding-linux-arm64-musl@11.19.1': - resolution: {integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==} + resolution: {integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.19.1.tgz} cpu: [arm64] os: [linux] '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': - resolution: {integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==} + resolution: {integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.19.1.tgz} cpu: [ppc64] os: [linux] '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': - resolution: {integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==} + resolution: {integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.19.1.tgz} cpu: [riscv64] os: [linux] '@oxc-resolver/binding-linux-riscv64-musl@11.19.1': - resolution: {integrity: sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw==} + resolution: {integrity: sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.19.1.tgz} cpu: [riscv64] os: [linux] '@oxc-resolver/binding-linux-s390x-gnu@11.19.1': - resolution: {integrity: sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA==} + resolution: {integrity: sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.19.1.tgz} cpu: [s390x] os: [linux] '@oxc-resolver/binding-linux-x64-gnu@11.19.1': - resolution: {integrity: sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ==} + resolution: {integrity: sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.19.1.tgz} cpu: [x64] os: [linux] '@oxc-resolver/binding-linux-x64-musl@11.19.1': - resolution: {integrity: sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw==} + resolution: {integrity: sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.19.1.tgz} cpu: [x64] os: [linux] '@oxc-resolver/binding-openharmony-arm64@11.19.1': - resolution: {integrity: sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA==} + resolution: {integrity: sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.19.1.tgz} cpu: [arm64] os: [openharmony] '@oxc-resolver/binding-wasm32-wasi@11.19.1': - resolution: {integrity: sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg==} + resolution: {integrity: sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.19.1.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] '@oxc-resolver/binding-win32-arm64-msvc@11.19.1': - resolution: {integrity: sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ==} + resolution: {integrity: sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.19.1.tgz} cpu: [arm64] os: [win32] '@oxc-resolver/binding-win32-ia32-msvc@11.19.1': - resolution: {integrity: sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA==} + resolution: {integrity: sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.19.1.tgz} cpu: [ia32] os: [win32] '@oxc-resolver/binding-win32-x64-msvc@11.19.1': - resolution: {integrity: sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw==} + resolution: {integrity: sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw==, tarball: https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.19.1.tgz} cpu: [x64] os: [win32] '@oxfmt/binding-android-arm-eabi@0.36.0': - resolution: {integrity: sha512-Z4yVHJWx/swHHjtr0dXrBZb6LxS+qNz1qdza222mWwPTUK4L790+5i3LTgjx3KYGBzcYpjaiZBw4vOx94dH7MQ==} + resolution: {integrity: sha512-Z4yVHJWx/swHHjtr0dXrBZb6LxS+qNz1qdza222mWwPTUK4L790+5i3LTgjx3KYGBzcYpjaiZBw4vOx94dH7MQ==, tarball: https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@oxfmt/binding-android-arm64@0.36.0': - resolution: {integrity: sha512-3ElCJRFNPQl7jexf2CAa9XmAm8eC5JPrIDSjc9jSchkVSFTEqyL0NtZinBB2h1a4i4JgP1oGl/5G5n8YR4FN8Q==} + resolution: {integrity: sha512-3ElCJRFNPQl7jexf2CAa9XmAm8eC5JPrIDSjc9jSchkVSFTEqyL0NtZinBB2h1a4i4JgP1oGl/5G5n8YR4FN8Q==, tarball: https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@oxfmt/binding-darwin-arm64@0.36.0': - resolution: {integrity: sha512-nak4znWCqIExKhYSY/mz/lWsqWIpdsS7o0+SRzXR1Q0m7GrMcG1UrF1pS7TLGZhhkf7nTfEF7q6oZzJiodRDuw==} + resolution: {integrity: sha512-nak4znWCqIExKhYSY/mz/lWsqWIpdsS7o0+SRzXR1Q0m7GrMcG1UrF1pS7TLGZhhkf7nTfEF7q6oZzJiodRDuw==, tarball: https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@oxfmt/binding-darwin-x64@0.36.0': - resolution: {integrity: sha512-V4GP96thDnpKx6ADnMDnhIXNdtV+Ql9D4HUU+a37VTeVbs5qQSF/s6hhUP1b3xUqU7iRcwh72jUU2Y12rtGHAw==} + resolution: {integrity: sha512-V4GP96thDnpKx6ADnMDnhIXNdtV+Ql9D4HUU+a37VTeVbs5qQSF/s6hhUP1b3xUqU7iRcwh72jUU2Y12rtGHAw==, tarball: https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@oxfmt/binding-freebsd-x64@0.36.0': - resolution: {integrity: sha512-/xapWCADfI5wrhxpEUjhI9fnw7MV5BUZizVa8e24n3VSK6A3Y1TB/ClOP1tfxNspykFKXp4NBWl6NtDJP3osqQ==} + resolution: {integrity: sha512-/xapWCADfI5wrhxpEUjhI9fnw7MV5BUZizVa8e24n3VSK6A3Y1TB/ClOP1tfxNspykFKXp4NBWl6NtDJP3osqQ==, tarball: https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@oxfmt/binding-linux-arm-gnueabihf@0.36.0': - resolution: {integrity: sha512-1lOmv61XMFIH5uNm27620kRRzWt/RK6tdn250BRDoG9W7OXGOQ5UyI1HVT+SFkoOoKztBiinWgi68+NA1MjBVQ==} + resolution: {integrity: sha512-1lOmv61XMFIH5uNm27620kRRzWt/RK6tdn250BRDoG9W7OXGOQ5UyI1HVT+SFkoOoKztBiinWgi68+NA1MjBVQ==, tarball: https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxfmt/binding-linux-arm-musleabihf@0.36.0': - resolution: {integrity: sha512-vMH23AskdR1ujUS9sPck2Df9rBVoZUnCVY86jisILzIQ/QQ/yKUTi7tgnIvydPx7TyB/48wsQ5QMr5Knq5p/aw==} + resolution: {integrity: sha512-vMH23AskdR1ujUS9sPck2Df9rBVoZUnCVY86jisILzIQ/QQ/yKUTi7tgnIvydPx7TyB/48wsQ5QMr5Knq5p/aw==, tarball: https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxfmt/binding-linux-arm64-gnu@0.36.0': - resolution: {integrity: sha512-Hy1V+zOBHpBiENRx77qrUTt5aPDHeCASRc8K5KwwAHkX2AKP0nV89eL17hsZrE9GmnXFjsNmd80lyf7aRTXsbw==} + resolution: {integrity: sha512-Hy1V+zOBHpBiENRx77qrUTt5aPDHeCASRc8K5KwwAHkX2AKP0nV89eL17hsZrE9GmnXFjsNmd80lyf7aRTXsbw==, tarball: https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@oxfmt/binding-linux-arm64-musl@0.36.0': - resolution: {integrity: sha512-SPGLJkOIHSIC6ABUQ5V8NqJpvYhMJueJv26NYqfCnwi/Mn6A61amkpJJ9Suy0Nmvs+OWESJpcebrBUbXPGZyQQ==} + resolution: {integrity: sha512-SPGLJkOIHSIC6ABUQ5V8NqJpvYhMJueJv26NYqfCnwi/Mn6A61amkpJJ9Suy0Nmvs+OWESJpcebrBUbXPGZyQQ==, tarball: https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@oxfmt/binding-linux-ppc64-gnu@0.36.0': - resolution: {integrity: sha512-3EuoyB8x9x8ysYJjbEO/M9fkSk72zQKnXCvpZMDHXlnY36/1qMp55Nm0PrCwjGO/1pen5hdOVkz9WmP3nAp2IQ==} + resolution: {integrity: sha512-3EuoyB8x9x8ysYJjbEO/M9fkSk72zQKnXCvpZMDHXlnY36/1qMp55Nm0PrCwjGO/1pen5hdOVkz9WmP3nAp2IQ==, tarball: https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] '@oxfmt/binding-linux-riscv64-gnu@0.36.0': - resolution: {integrity: sha512-MpY3itLwpGh8dnywtrZtaZ604T1m715SydCKy0+qTxetv+IHzuA+aO/AGzrlzUNYZZmtWtmDBrChZGibvZxbRQ==} + resolution: {integrity: sha512-MpY3itLwpGh8dnywtrZtaZ604T1m715SydCKy0+qTxetv+IHzuA+aO/AGzrlzUNYZZmtWtmDBrChZGibvZxbRQ==, tarball: https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] '@oxfmt/binding-linux-riscv64-musl@0.36.0': - resolution: {integrity: sha512-mmDhe4Vtx+XwQPRPn/V25+APnkApYgZ23q+6GVsNYY98pf3aU0aI3Me96pbRs/AfJ1jIiGC+/6q71FEu8dHcHw==} + resolution: {integrity: sha512-mmDhe4Vtx+XwQPRPn/V25+APnkApYgZ23q+6GVsNYY98pf3aU0aI3Me96pbRs/AfJ1jIiGC+/6q71FEu8dHcHw==, tarball: https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] '@oxfmt/binding-linux-s390x-gnu@0.36.0': - resolution: {integrity: sha512-AYXhU+DmNWLSnvVwkHM92fuYhogtVHab7UQrPNaDf1sxadugg9gWVmcgJDlIwxJdpk5CVW/TFvwUKwI432zhhA==} + resolution: {integrity: sha512-AYXhU+DmNWLSnvVwkHM92fuYhogtVHab7UQrPNaDf1sxadugg9gWVmcgJDlIwxJdpk5CVW/TFvwUKwI432zhhA==, tarball: https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] '@oxfmt/binding-linux-x64-gnu@0.36.0': - resolution: {integrity: sha512-H16QhhQ3usoakMleiAAQ2mg0NsBDAdyE9agUgfC8IHHh3jZEbr0rIKwjEqwbOHK5M0EmfhJmr+aGO/MgZPsneA==} + resolution: {integrity: sha512-H16QhhQ3usoakMleiAAQ2mg0NsBDAdyE9agUgfC8IHHh3jZEbr0rIKwjEqwbOHK5M0EmfhJmr+aGO/MgZPsneA==, tarball: https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@oxfmt/binding-linux-x64-musl@0.36.0': - resolution: {integrity: sha512-EFFGkixA39BcmHiCe2ECdrq02D6FCve5ka6ObbvrheXl4V+R0U/E+/uLyVx1X65LW8TA8QQHdnbdDallRekohw==} + resolution: {integrity: sha512-EFFGkixA39BcmHiCe2ECdrq02D6FCve5ka6ObbvrheXl4V+R0U/E+/uLyVx1X65LW8TA8QQHdnbdDallRekohw==, tarball: https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@oxfmt/binding-openharmony-arm64@0.36.0': - resolution: {integrity: sha512-zr/t369wZWFOj1qf06Z5gGNjFymfUNDrxKMmr7FKiDRVI1sNsdKRCuRL4XVjtcptKQ+ao3FfxLN1vrynivmCYg==} + resolution: {integrity: sha512-zr/t369wZWFOj1qf06Z5gGNjFymfUNDrxKMmr7FKiDRVI1sNsdKRCuRL4XVjtcptKQ+ao3FfxLN1vrynivmCYg==, tarball: https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@oxfmt/binding-win32-arm64-msvc@0.36.0': - resolution: {integrity: sha512-FxO7UksTv8h4olzACgrqAXNF6BP329+H322323iDrMB5V/+a1kcAw07fsOsUmqNrb9iJBsCQgH/zqcqp5903ag==} + resolution: {integrity: sha512-FxO7UksTv8h4olzACgrqAXNF6BP329+H322323iDrMB5V/+a1kcAw07fsOsUmqNrb9iJBsCQgH/zqcqp5903ag==, tarball: https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@oxfmt/binding-win32-ia32-msvc@0.36.0': - resolution: {integrity: sha512-OjoMQ89H01M0oLMfr/CPNH1zi48ZIwxAKObUl57oh7ssUBNDp/2Vjf7E1TQ8M4oj4VFQ/byxl2SmcPNaI2YNDg==} + resolution: {integrity: sha512-OjoMQ89H01M0oLMfr/CPNH1zi48ZIwxAKObUl57oh7ssUBNDp/2Vjf7E1TQ8M4oj4VFQ/byxl2SmcPNaI2YNDg==, tarball: https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] '@oxfmt/binding-win32-x64-msvc@0.36.0': - resolution: {integrity: sha512-MoyeQ9S36ZTz/4bDhOKJgOBIDROd4dQ5AkT9iezhEaUBxAPdNX9Oq0jD8OSnCj3G4wam/XNxVWKMA52kmzmPtQ==} + resolution: {integrity: sha512-MoyeQ9S36ZTz/4bDhOKJgOBIDROd4dQ5AkT9iezhEaUBxAPdNX9Oq0jD8OSnCj3G4wam/XNxVWKMA52kmzmPtQ==, tarball: https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.36.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@oxlint/binding-android-arm-eabi@1.51.0': - resolution: {integrity: sha512-jJYIqbx4sX+suIxWstc4P7SzhEwb4ArWA2KVrmEuu9vH2i0qM6QIHz/ehmbGE4/2fZbpuMuBzTl7UkfNoqiSgw==} + resolution: {integrity: sha512-jJYIqbx4sX+suIxWstc4P7SzhEwb4ArWA2KVrmEuu9vH2i0qM6QIHz/ehmbGE4/2fZbpuMuBzTl7UkfNoqiSgw==, tarball: https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@oxlint/binding-android-arm64@1.51.0': - resolution: {integrity: sha512-GtXyBCcH4ti98YdiMNCrpBNGitx87EjEWxevnyhcBK12k/Vu4EzSB45rzSC4fGFUD6sQgeaxItRCEEWeVwPafw==} + resolution: {integrity: sha512-GtXyBCcH4ti98YdiMNCrpBNGitx87EjEWxevnyhcBK12k/Vu4EzSB45rzSC4fGFUD6sQgeaxItRCEEWeVwPafw==, tarball: https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@oxlint/binding-darwin-arm64@1.51.0': - resolution: {integrity: sha512-3QJbeYaMHn6Bh2XeBXuITSsbnIctyTjvHf5nRjKYrT9pPeErNIpp5VDEeAXC0CZSwSVTsc8WOSDwgrAI24JolQ==} + resolution: {integrity: sha512-3QJbeYaMHn6Bh2XeBXuITSsbnIctyTjvHf5nRjKYrT9pPeErNIpp5VDEeAXC0CZSwSVTsc8WOSDwgrAI24JolQ==, tarball: https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@oxlint/binding-darwin-x64@1.51.0': - resolution: {integrity: sha512-NzErhMaTEN1cY0E8C5APy74lw5VwsNfJfVPBMWPVQLqAbO0k4FFLjvHURvkUL+Y18Wu+8Vs1kbqPh2hjXYA4pg==} + resolution: {integrity: sha512-NzErhMaTEN1cY0E8C5APy74lw5VwsNfJfVPBMWPVQLqAbO0k4FFLjvHURvkUL+Y18Wu+8Vs1kbqPh2hjXYA4pg==, tarball: https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@oxlint/binding-freebsd-x64@1.51.0': - resolution: {integrity: sha512-msAIh3vPAoKoHlOE/oe6Q5C/n9umypv/k81lED82ibrJotn+3YG2Qp1kiR8o/Dg5iOEU97c6tl0utxcyFenpFw==} + resolution: {integrity: sha512-msAIh3vPAoKoHlOE/oe6Q5C/n9umypv/k81lED82ibrJotn+3YG2Qp1kiR8o/Dg5iOEU97c6tl0utxcyFenpFw==, tarball: https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@oxlint/binding-linux-arm-gnueabihf@1.51.0': - resolution: {integrity: sha512-CqQPcvqYyMe9ZBot2stjGogEzk1z8gGAngIX7srSzrzexmXixwVxBdFZyxTVM0CjGfDeV+Ru0w25/WNjlMM2Hw==} + resolution: {integrity: sha512-CqQPcvqYyMe9ZBot2stjGogEzk1z8gGAngIX7srSzrzexmXixwVxBdFZyxTVM0CjGfDeV+Ru0w25/WNjlMM2Hw==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxlint/binding-linux-arm-musleabihf@1.51.0': - resolution: {integrity: sha512-dstrlYQgZMnyOssxSbolGCge/sDbko12N/35RBNuqLpoPbft2aeBidBAb0dvQlyBd9RJ6u8D4o4Eh8Un6iTgyQ==} + resolution: {integrity: sha512-dstrlYQgZMnyOssxSbolGCge/sDbko12N/35RBNuqLpoPbft2aeBidBAb0dvQlyBd9RJ6u8D4o4Eh8Un6iTgyQ==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxlint/binding-linux-arm64-gnu@1.51.0': - resolution: {integrity: sha512-QEjUpXO7d35rP1/raLGGbAsBLLGZIzV3ZbeSjqWlD3oRnxpRIZ6iL4o51XQHkconn3uKssc+1VKdtHJ81BBhDA==} + resolution: {integrity: sha512-QEjUpXO7d35rP1/raLGGbAsBLLGZIzV3ZbeSjqWlD3oRnxpRIZ6iL4o51XQHkconn3uKssc+1VKdtHJ81BBhDA==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@oxlint/binding-linux-arm64-musl@1.51.0': - resolution: {integrity: sha512-YSJua5irtG4DoMAjUapDTPhkQLHhBIY0G9JqlZS6/SZPzqDkPku/1GdWs0D6h/wyx0Iz31lNCfIaWKBQhzP0wQ==} + resolution: {integrity: sha512-YSJua5irtG4DoMAjUapDTPhkQLHhBIY0G9JqlZS6/SZPzqDkPku/1GdWs0D6h/wyx0Iz31lNCfIaWKBQhzP0wQ==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@oxlint/binding-linux-ppc64-gnu@1.51.0': - resolution: {integrity: sha512-7L4Wj2IEUNDETKssB9IDYt16T6WlF+X2jgC/hBq3diGHda9vJLpAgb09+D3quFq7TdkFtI7hwz/jmuQmQFPc1Q==} + resolution: {integrity: sha512-7L4Wj2IEUNDETKssB9IDYt16T6WlF+X2jgC/hBq3diGHda9vJLpAgb09+D3quFq7TdkFtI7hwz/jmuQmQFPc1Q==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] '@oxlint/binding-linux-riscv64-gnu@1.51.0': - resolution: {integrity: sha512-cBUHqtOXy76G41lOB401qpFoKx1xq17qYkhWrLSM7eEjiHM9sOtYqpr6ZdqCnN9s6ZpzudX4EkeHOFH2E9q0vA==} + resolution: {integrity: sha512-cBUHqtOXy76G41lOB401qpFoKx1xq17qYkhWrLSM7eEjiHM9sOtYqpr6ZdqCnN9s6ZpzudX4EkeHOFH2E9q0vA==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] '@oxlint/binding-linux-riscv64-musl@1.51.0': - resolution: {integrity: sha512-WKbg8CysgZcHfZX0ixQFBRSBvFZUHa3SBnEjHY2FVYt2nbNJEjzTxA3ZR5wMU0NOCNKIAFUFvAh5/XJKPRJuJg==} + resolution: {integrity: sha512-WKbg8CysgZcHfZX0ixQFBRSBvFZUHa3SBnEjHY2FVYt2nbNJEjzTxA3ZR5wMU0NOCNKIAFUFvAh5/XJKPRJuJg==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] '@oxlint/binding-linux-s390x-gnu@1.51.0': - resolution: {integrity: sha512-N1QRUvJTxqXNSu35YOufdjsAVmKVx5bkrggOWAhTWBc3J4qjcBwr1IfyLh/6YCg8sYRSR1GraldS9jUgJL/U4A==} + resolution: {integrity: sha512-N1QRUvJTxqXNSu35YOufdjsAVmKVx5bkrggOWAhTWBc3J4qjcBwr1IfyLh/6YCg8sYRSR1GraldS9jUgJL/U4A==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] '@oxlint/binding-linux-x64-gnu@1.51.0': - resolution: {integrity: sha512-e0Mz0DizsCoqNIjeOg6OUKe8JKJWZ5zZlwsd05Bmr51Jo3AOL4UJnPvwKumr4BBtBrDZkCmOLhCvDGm95nJM2g==} + resolution: {integrity: sha512-e0Mz0DizsCoqNIjeOg6OUKe8JKJWZ5zZlwsd05Bmr51Jo3AOL4UJnPvwKumr4BBtBrDZkCmOLhCvDGm95nJM2g==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@oxlint/binding-linux-x64-musl@1.51.0': - resolution: {integrity: sha512-wD8HGTWhYBKXvRDvoBVB1y+fEYV01samhWQSy1Zkxq2vpezvMnjaFKRuiP6tBNITLGuffbNDEXOwcAhJ3gI5Ug==} + resolution: {integrity: sha512-wD8HGTWhYBKXvRDvoBVB1y+fEYV01samhWQSy1Zkxq2vpezvMnjaFKRuiP6tBNITLGuffbNDEXOwcAhJ3gI5Ug==, tarball: https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@oxlint/binding-openharmony-arm64@1.51.0': - resolution: {integrity: sha512-5NSwQ2hDEJ0GPXqikjWtwzgAQCsS7P9aLMNenjjKa+gknN3lTCwwwERsT6lKXSirfU3jLjexA2XQvQALh5h27w==} + resolution: {integrity: sha512-5NSwQ2hDEJ0GPXqikjWtwzgAQCsS7P9aLMNenjjKa+gknN3lTCwwwERsT6lKXSirfU3jLjexA2XQvQALh5h27w==, tarball: https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@oxlint/binding-win32-arm64-msvc@1.51.0': - resolution: {integrity: sha512-JEZyah1M0RHMw8d+jjSSJmSmO8sABA1J1RtrHYujGPeCkYg1NeH0TGuClpe2h5QtioRTaF57y/TZfn/2IFV6fA==} + resolution: {integrity: sha512-JEZyah1M0RHMw8d+jjSSJmSmO8sABA1J1RtrHYujGPeCkYg1NeH0TGuClpe2h5QtioRTaF57y/TZfn/2IFV6fA==, tarball: https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@oxlint/binding-win32-ia32-msvc@1.51.0': - resolution: {integrity: sha512-q3cEoKH6kwjz/WRyHwSf0nlD2F5Qw536kCXvmlSu+kaShzgrA0ojmh45CA81qL+7udfCaZL2SdKCZlLiGBVFlg==} + resolution: {integrity: sha512-q3cEoKH6kwjz/WRyHwSf0nlD2F5Qw536kCXvmlSu+kaShzgrA0ojmh45CA81qL+7udfCaZL2SdKCZlLiGBVFlg==, tarball: https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] '@oxlint/binding-win32-x64-msvc@1.51.0': - resolution: {integrity: sha512-Q14+fOGb9T28nWF/0EUsYqERiRA7cl1oy4TJrGmLaqhm+aO2cV+JttboHI3CbdeMCAyDI1+NoSlrM7Melhp/cw==} + resolution: {integrity: sha512-Q14+fOGb9T28nWF/0EUsYqERiRA7cl1oy4TJrGmLaqhm+aO2cV+JttboHI3CbdeMCAyDI1+NoSlrM7Melhp/cw==, tarball: https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.51.0.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@petamoriken/float16@3.9.3': - resolution: {integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==} + resolution: {integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==, tarball: https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.3.tgz} '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + '@polar-sh/sdk@0.47.0': + resolution: {integrity: sha512-tZEt8eLWsQVhmyfUQzXhnN84e5Cnu/JsYFnCci1ranWkIGveQc+wTiyzDCG9Tl1xiz1Y/ztPsAJ1fevj6ExdAA==, tarball: https://registry.npmjs.org/@polar-sh/sdk/-/sdk-0.47.0.tgz} + '@posthog/core@1.24.3': resolution: {integrity: sha512-nTyL1R/8V5vfdH37MbjXDYWFnUoxVijb2TnfJSNHz0+RBLtNnq0hNnBDCwWLl5yh1bzeJBYTT8UF+dV7D8y03w==} '@prisma/client-runtime-utils@7.4.2': - resolution: {integrity: sha512-cID+rzOEb38VyMsx5LwJMEY4NGIrWCNpKu/0ImbeooQ2Px7TI+kOt7cm0NelxUzF2V41UVVXAmYjANZQtCu1/Q==} + resolution: {integrity: sha512-cID+rzOEb38VyMsx5LwJMEY4NGIrWCNpKu/0ImbeooQ2Px7TI+kOt7cm0NelxUzF2V41UVVXAmYjANZQtCu1/Q==, tarball: https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.4.2.tgz} '@prisma/client@7.4.2': - resolution: {integrity: sha512-ts2mu+cQHriAhSxngO3StcYubBGTWDtu/4juZhXCUKOwgh26l+s4KD3vT2kMUzFyrYnll9u/3qWrtzRv9CGWzA==} + resolution: {integrity: sha512-ts2mu+cQHriAhSxngO3StcYubBGTWDtu/4juZhXCUKOwgh26l+s4KD3vT2kMUzFyrYnll9u/3qWrtzRv9CGWzA==, tarball: https://registry.npmjs.org/@prisma/client/-/client-7.4.2.tgz} engines: {node: ^20.19 || ^22.12 || >=24.0} peerDependencies: prisma: '*' @@ -2359,37 +2384,37 @@ packages: optional: true '@prisma/config@7.4.2': - resolution: {integrity: sha512-CftBjWxav99lzY1Z4oDgomdb1gh9BJFAOmWF6P2v1xRfXqQb56DfBub+QKcERRdNoAzCb3HXy3Zii8Vb4AsXhg==} + resolution: {integrity: sha512-CftBjWxav99lzY1Z4oDgomdb1gh9BJFAOmWF6P2v1xRfXqQb56DfBub+QKcERRdNoAzCb3HXy3Zii8Vb4AsXhg==, tarball: https://registry.npmjs.org/@prisma/config/-/config-7.4.2.tgz} '@prisma/debug@7.2.0': - resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==} + resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==, tarball: https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz} '@prisma/debug@7.4.2': - resolution: {integrity: sha512-aP7qzu+g/JnbF6U69LMwHoUkELiserKmWsE2shYuEpNUJ4GrtxBCvZwCyCBHFSH2kLTF2l1goBlBh4wuvRq62w==} + resolution: {integrity: sha512-aP7qzu+g/JnbF6U69LMwHoUkELiserKmWsE2shYuEpNUJ4GrtxBCvZwCyCBHFSH2kLTF2l1goBlBh4wuvRq62w==, tarball: https://registry.npmjs.org/@prisma/debug/-/debug-7.4.2.tgz} '@prisma/dev@0.20.0': - resolution: {integrity: sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==} + resolution: {integrity: sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==, tarball: https://registry.npmjs.org/@prisma/dev/-/dev-0.20.0.tgz} '@prisma/engines-version@7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919': - resolution: {integrity: sha512-5FIKY3KoYQlBuZC2yc16EXfVRQ8HY+fLqgxkYfWCtKhRb3ajCRzP/rPeoSx11+NueJDANdh4hjY36mdmrTcGSg==} + resolution: {integrity: sha512-5FIKY3KoYQlBuZC2yc16EXfVRQ8HY+fLqgxkYfWCtKhRb3ajCRzP/rPeoSx11+NueJDANdh4hjY36mdmrTcGSg==, tarball: https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919.tgz} '@prisma/engines@7.4.2': - resolution: {integrity: sha512-B+ZZhI4rXlzjVqRw/93AothEKOU5/x4oVyJFGo9RpHPnBwaPwk4Pi0Q4iGXipKxeXPs/dqljgNBjK0m8nocOJA==} + resolution: {integrity: sha512-B+ZZhI4rXlzjVqRw/93AothEKOU5/x4oVyJFGo9RpHPnBwaPwk4Pi0Q4iGXipKxeXPs/dqljgNBjK0m8nocOJA==, tarball: https://registry.npmjs.org/@prisma/engines/-/engines-7.4.2.tgz} '@prisma/fetch-engine@7.4.2': - resolution: {integrity: sha512-f/c/MwYpdJO7taLETU8rahEstLeXfYgQGlz5fycG7Fbmva3iPdzGmjiSWHeSWIgNnlXnelUdCJqyZnFocurZuA==} + resolution: {integrity: sha512-f/c/MwYpdJO7taLETU8rahEstLeXfYgQGlz5fycG7Fbmva3iPdzGmjiSWHeSWIgNnlXnelUdCJqyZnFocurZuA==, tarball: https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.4.2.tgz} '@prisma/get-platform@7.2.0': - resolution: {integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==} + resolution: {integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==, tarball: https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz} '@prisma/get-platform@7.4.2': - resolution: {integrity: sha512-UTnChXRwiauzl/8wT4hhe7Xmixja9WE28oCnGpBtRejaHhvekx5kudr3R4Y9mLSA0kqGnAMeyTiKwDVMjaEVsw==} + resolution: {integrity: sha512-UTnChXRwiauzl/8wT4hhe7Xmixja9WE28oCnGpBtRejaHhvekx5kudr3R4Y9mLSA0kqGnAMeyTiKwDVMjaEVsw==, tarball: https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.4.2.tgz} '@prisma/query-plan-executor@7.2.0': - resolution: {integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==} + resolution: {integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==, tarball: https://registry.npmjs.org/@prisma/query-plan-executor/-/query-plan-executor-7.2.0.tgz} '@prisma/studio-core@0.13.1': - resolution: {integrity: sha512-agdqaPEePRHcQ7CexEfkX1RvSH9uWDb6pXrZnhCRykhDFAV0/0P3d07WtfiY8hZWb7oRU4v+NkT4cGFHkQJIPg==} + resolution: {integrity: sha512-agdqaPEePRHcQ7CexEfkX1RvSH9uWDb6pXrZnhCRykhDFAV0/0P3d07WtfiY8hZWb7oRU4v+NkT4cGFHkQJIPg==, tarball: https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.13.1.tgz} peerDependencies: '@types/react': ^18.0.0 || ^19.0.0 react: ^18.0.0 || ^19.0.0 @@ -3109,179 +3134,179 @@ packages: optional: true '@rolldown/binding-android-arm64@1.0.0-rc.12': - resolution: {integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==} + resolution: {integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==, tarball: https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@rolldown/binding-android-arm64@1.0.0-rc.8': - resolution: {integrity: sha512-5bcmMQDWEfWUq3m79Mcf/kbO6e5Jr6YjKSsA1RnpXR6k73hQ9z1B17+4h93jXpzHvS18p7bQHM1HN/fSd+9zog==} + resolution: {integrity: sha512-5bcmMQDWEfWUq3m79Mcf/kbO6e5Jr6YjKSsA1RnpXR6k73hQ9z1B17+4h93jXpzHvS18p7bQHM1HN/fSd+9zog==, tarball: https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@rolldown/binding-darwin-arm64@1.0.0-rc.12': - resolution: {integrity: sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==} + resolution: {integrity: sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@rolldown/binding-darwin-arm64@1.0.0-rc.8': - resolution: {integrity: sha512-dcHPd5N4g9w2iiPRJmAvO0fsIWzF2JPr9oSuTjxLL56qu+oML5aMbBMNwWbk58Mt3pc7vYs9CCScwLxdXPdRsg==} + resolution: {integrity: sha512-dcHPd5N4g9w2iiPRJmAvO0fsIWzF2JPr9oSuTjxLL56qu+oML5aMbBMNwWbk58Mt3pc7vYs9CCScwLxdXPdRsg==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@rolldown/binding-darwin-x64@1.0.0-rc.12': - resolution: {integrity: sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==} + resolution: {integrity: sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@rolldown/binding-darwin-x64@1.0.0-rc.8': - resolution: {integrity: sha512-mw0VzDvoj8AuR761QwpdCFN0sc/jspuc7eRYJetpLWd+XyansUrH3C7IgNw6swBOgQT9zBHNKsVCjzpfGJlhUA==} + resolution: {integrity: sha512-mw0VzDvoj8AuR761QwpdCFN0sc/jspuc7eRYJetpLWd+XyansUrH3C7IgNw6swBOgQT9zBHNKsVCjzpfGJlhUA==, tarball: https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@rolldown/binding-freebsd-x64@1.0.0-rc.12': - resolution: {integrity: sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==} + resolution: {integrity: sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==, tarball: https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@rolldown/binding-freebsd-x64@1.0.0-rc.8': - resolution: {integrity: sha512-xNrRa6mQ9NmMIJBdJtPMPG8Mso0OhM526pDzc/EKnRrIrrkHD1E0Z6tONZRmUeJElfsQ6h44lQQCcDilSNIvSQ==} + resolution: {integrity: sha512-xNrRa6mQ9NmMIJBdJtPMPG8Mso0OhM526pDzc/EKnRrIrrkHD1E0Z6tONZRmUeJElfsQ6h44lQQCcDilSNIvSQ==, tarball: https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12': - resolution: {integrity: sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==} + resolution: {integrity: sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.8': - resolution: {integrity: sha512-WgCKoO6O/rRUwimWfEJDeztwJJmuuX0N2bYLLRxmXDTtCwjToTOqk7Pashl/QpQn3H/jHjx0b5yCMbcTVYVpNg==} + resolution: {integrity: sha512-WgCKoO6O/rRUwimWfEJDeztwJJmuuX0N2bYLLRxmXDTtCwjToTOqk7Pashl/QpQn3H/jHjx0b5yCMbcTVYVpNg==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12': - resolution: {integrity: sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==} + resolution: {integrity: sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.8': - resolution: {integrity: sha512-tOHgTOQa8G4Z3ULj4G3NYOGGJEsqPHR91dT72u63OtVsZ7B6wFJKOx+ZKv+pvwzxWz92/I2ycaqi2/Ll4l+rlg==} + resolution: {integrity: sha512-tOHgTOQa8G4Z3ULj4G3NYOGGJEsqPHR91dT72u63OtVsZ7B6wFJKOx+ZKv+pvwzxWz92/I2ycaqi2/Ll4l+rlg==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': - resolution: {integrity: sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==} + resolution: {integrity: sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.8': - resolution: {integrity: sha512-oRbxcgDujCi2Yp1GTxoUFsIFlZsuPHU4OV4AzNc3/6aUmR4lfm9FK0uwQu82PJsuUwnF2jFdop3Ep5c1uK7Uxg==} + resolution: {integrity: sha512-oRbxcgDujCi2Yp1GTxoUFsIFlZsuPHU4OV4AzNc3/6aUmR4lfm9FK0uwQu82PJsuUwnF2jFdop3Ep5c1uK7Uxg==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': - resolution: {integrity: sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==} + resolution: {integrity: sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.8': - resolution: {integrity: sha512-oaLRyUHw8kQE5M89RqrDJZ10GdmGJcMeCo8tvaE4ukOofqgjV84AbqBSH6tTPjeT2BHv+xlKj678GBuIb47lKA==} + resolution: {integrity: sha512-oaLRyUHw8kQE5M89RqrDJZ10GdmGJcMeCo8tvaE4ukOofqgjV84AbqBSH6tTPjeT2BHv+xlKj678GBuIb47lKA==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': - resolution: {integrity: sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==} + resolution: {integrity: sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.8': - resolution: {integrity: sha512-1hjSKFrod5MwBBdLOOA0zpUuSfSDkYIY+QqcMcIU1WOtswZtZdUkcFcZza9b2HcAb0bnpmmyo0LZcaxLb2ov1g==} + resolution: {integrity: sha512-1hjSKFrod5MwBBdLOOA0zpUuSfSDkYIY+QqcMcIU1WOtswZtZdUkcFcZza9b2HcAb0bnpmmyo0LZcaxLb2ov1g==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': - resolution: {integrity: sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==} + resolution: {integrity: sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.8': - resolution: {integrity: sha512-a1+F0aV4Wy9tT3o+cHl3XhOy6aFV+B8Ll+/JFj98oGkb6lGk3BNgrxd+80RwYRVd23oLGvj3LwluKYzlv1PEuw==} + resolution: {integrity: sha512-a1+F0aV4Wy9tT3o+cHl3XhOy6aFV+B8Ll+/JFj98oGkb6lGk3BNgrxd+80RwYRVd23oLGvj3LwluKYzlv1PEuw==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': - resolution: {integrity: sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==} + resolution: {integrity: sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@rolldown/binding-linux-x64-musl@1.0.0-rc.8': - resolution: {integrity: sha512-bGyXCFU11seFrf7z8PcHSwGEiFVkZ9vs+auLacVOQrVsI8PFHJzzJROF3P6b0ODDmXr0m6Tj5FlDhcXVk0Jp8w==} + resolution: {integrity: sha512-bGyXCFU11seFrf7z8PcHSwGEiFVkZ9vs+auLacVOQrVsI8PFHJzzJROF3P6b0ODDmXr0m6Tj5FlDhcXVk0Jp8w==, tarball: https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': - resolution: {integrity: sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==} + resolution: {integrity: sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==, tarball: https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@rolldown/binding-openharmony-arm64@1.0.0-rc.8': - resolution: {integrity: sha512-n8d+L2bKgf9G3+AM0bhHFWdlz9vYKNim39ujRTieukdRek0RAo2TfG2uEnV9spa4r4oHUfL9IjcY3M9SlqN1gw==} + resolution: {integrity: sha512-n8d+L2bKgf9G3+AM0bhHFWdlz9vYKNim39ujRTieukdRek0RAo2TfG2uEnV9spa4r4oHUfL9IjcY3M9SlqN1gw==, tarball: https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@rolldown/binding-wasm32-wasi@1.0.0-rc.12': - resolution: {integrity: sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==} + resolution: {integrity: sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==, tarball: https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] '@rolldown/binding-wasm32-wasi@1.0.0-rc.8': - resolution: {integrity: sha512-4R4iJDIk7BrJdteAbEAICXPoA7vZoY/M0OBfcRlQxzQvUYMcEp2GbC/C8UOgQJhu2TjGTpX1H8vVO1xHWcRqQA==} + resolution: {integrity: sha512-4R4iJDIk7BrJdteAbEAICXPoA7vZoY/M0OBfcRlQxzQvUYMcEp2GbC/C8UOgQJhu2TjGTpX1H8vVO1xHWcRqQA==, tarball: https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.8.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12': - resolution: {integrity: sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==} + resolution: {integrity: sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.8': - resolution: {integrity: sha512-3lwnklba9qQOpFnQ7EW+A1m4bZTWXZE4jtehsZ0YOl2ivW1FQqp5gY7X2DLuKITggesyuLwcmqS11fA7NtrmrA==} + resolution: {integrity: sha512-3lwnklba9qQOpFnQ7EW+A1m4bZTWXZE4jtehsZ0YOl2ivW1FQqp5gY7X2DLuKITggesyuLwcmqS11fA7NtrmrA==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@rolldown/binding-win32-x64-msvc@1.0.0-rc.12': - resolution: {integrity: sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==} + resolution: {integrity: sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@rolldown/binding-win32-x64-msvc@1.0.0-rc.8': - resolution: {integrity: sha512-VGjCx9Ha1P/r3tXGDZyG0Fcq7Q0Afnk64aaKzr1m40vbn1FL8R3W0V1ELDvPgzLXaaqK/9PnsqSaLWXfn6JtGQ==} + resolution: {integrity: sha512-VGjCx9Ha1P/r3tXGDZyG0Fcq7Q0Afnk64aaKzr1m40vbn1FL8R3W0V1ELDvPgzLXaaqK/9PnsqSaLWXfn6JtGQ==, tarball: https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.8.tgz} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -3296,127 +3321,127 @@ packages: resolution: {integrity: sha512-wzJwL82/arVfeSP3BLr1oTy40XddjtEdrdgtJ4lLRBu06mP3q/8HGM6K0JRlQuTA3XB0pNJx2so/nmpY4xyOew==} '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz} cpu: [arm] os: [android] '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz} cpu: [arm64] os: [android] '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz} cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz} cpu: [x64] os: [darwin] '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==, tarball: https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz} cpu: [arm64] os: [freebsd] '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==, tarball: https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz} cpu: [x64] os: [freebsd] '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz} cpu: [arm] os: [linux] '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz} cpu: [arm] os: [linux] '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz} cpu: [arm64] os: [linux] '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz} cpu: [arm64] os: [linux] '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz} cpu: [loong64] os: [linux] '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz} cpu: [loong64] os: [linux] '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz} cpu: [ppc64] os: [linux] '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz} cpu: [ppc64] os: [linux] '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz} cpu: [riscv64] os: [linux] '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz} cpu: [riscv64] os: [linux] '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz} cpu: [s390x] os: [linux] '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz} cpu: [x64] os: [linux] '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz} cpu: [x64] os: [linux] '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==, tarball: https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz} cpu: [x64] os: [openbsd] '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==, tarball: https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz} cpu: [arm64] os: [openharmony] '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz} cpu: [arm64] os: [win32] '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz} cpu: [ia32] os: [win32] '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz} cpu: [x64] os: [win32] '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz} cpu: [x64] os: [win32] @@ -3491,7 +3516,7 @@ packages: resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} '@sinclair/typebox@0.34.48': - resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} + resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==, tarball: https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz} '@sindresorhus/merge-streams@4.0.0': resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} @@ -3548,115 +3573,115 @@ packages: resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} '@tailwindcss/oxide-android-arm64@4.2.1': - resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} + resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [android] '@tailwindcss/oxide-android-arm64@4.2.2': - resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} + resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [android] '@tailwindcss/oxide-darwin-arm64@4.2.1': - resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} + resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] '@tailwindcss/oxide-darwin-arm64@4.2.2': - resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} + resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] '@tailwindcss/oxide-darwin-x64@4.2.1': - resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} + resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz} engines: {node: '>= 20'} cpu: [x64] os: [darwin] '@tailwindcss/oxide-darwin-x64@4.2.2': - resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} + resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz} engines: {node: '>= 20'} cpu: [x64] os: [darwin] '@tailwindcss/oxide-freebsd-x64@4.2.1': - resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} + resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] '@tailwindcss/oxide-freebsd-x64@4.2.2': - resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} + resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': - resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} + resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm] os: [linux] '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': - resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} + resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz} engines: {node: '>= 20'} cpu: [arm] os: [linux] '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': - resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} + resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [linux] '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': - resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} + resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [linux] '@tailwindcss/oxide-linux-arm64-musl@4.2.1': - resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} + resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [linux] '@tailwindcss/oxide-linux-arm64-musl@4.2.2': - resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} + resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [linux] '@tailwindcss/oxide-linux-x64-gnu@4.2.1': - resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} + resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz} engines: {node: '>= 20'} cpu: [x64] os: [linux] '@tailwindcss/oxide-linux-x64-gnu@4.2.2': - resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} + resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz} engines: {node: '>= 20'} cpu: [x64] os: [linux] '@tailwindcss/oxide-linux-x64-musl@4.2.1': - resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} + resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz} engines: {node: '>= 20'} cpu: [x64] os: [linux] '@tailwindcss/oxide-linux-x64-musl@4.2.2': - resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} + resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz} engines: {node: '>= 20'} cpu: [x64] os: [linux] '@tailwindcss/oxide-wasm32-wasi@4.2.1': - resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} + resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -3668,7 +3693,7 @@ packages: - tslib '@tailwindcss/oxide-wasm32-wasi@4.2.2': - resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} + resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -3680,25 +3705,25 @@ packages: - tslib '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': - resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} + resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [win32] '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': - resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} + resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz} engines: {node: '>= 20'} cpu: [arm64] os: [win32] '@tailwindcss/oxide-win32-x64-msvc@4.2.1': - resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} + resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz} engines: {node: '>= 20'} cpu: [x64] os: [win32] '@tailwindcss/oxide-win32-x64-msvc@4.2.2': - resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} + resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz} engines: {node: '>= 20'} cpu: [x64] os: [win32] @@ -3720,7 +3745,7 @@ packages: vite: ^5.2.0 || ^6 || ^7 || ^8 '@tanstack/history@1.161.4': - resolution: {integrity: sha512-Kp/WSt411ZWYvgXy6uiv5RmhHrz9cAml05AQPrtdAp7eUqvIDbMGPnML25OKbzR3RJ1q4wgENxDTvlGPa9+Mww==} + resolution: {integrity: sha512-Kp/WSt411ZWYvgXy6uiv5RmhHrz9cAml05AQPrtdAp7eUqvIDbMGPnML25OKbzR3RJ1q4wgENxDTvlGPa9+Mww==, tarball: https://registry.npmjs.org/@tanstack/history/-/history-1.161.4.tgz} engines: {node: '>=20.19'} '@tanstack/query-core@5.90.20': @@ -3732,31 +3757,31 @@ packages: react: ^18 || ^19 '@tanstack/react-router@1.166.2': - resolution: {integrity: sha512-pKhUtrvVLlhjWhsHkJSuIzh1J4LcP+8ErbIqRLORX9Js8dUFMKoT0+8oFpi+P8QRpuhm/7rzjYiWfcyTsqQZtA==} + resolution: {integrity: sha512-pKhUtrvVLlhjWhsHkJSuIzh1J4LcP+8ErbIqRLORX9Js8dUFMKoT0+8oFpi+P8QRpuhm/7rzjYiWfcyTsqQZtA==, tarball: https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.166.2.tgz} engines: {node: '>=20.19'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' '@tanstack/react-store@0.9.3': - resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==} + resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==, tarball: https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.9.3.tgz} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 '@tanstack/router-core@1.166.2': - resolution: {integrity: sha512-zn3NhENOAX9ToQiX077UV2OH3aJKOvV2ZMNZZxZ3gDG3i3WqL8NfWfEgetEAfMN37/Mnt90PpotYgf7IyuoKqQ==} + resolution: {integrity: sha512-zn3NhENOAX9ToQiX077UV2OH3aJKOvV2ZMNZZxZ3gDG3i3WqL8NfWfEgetEAfMN37/Mnt90PpotYgf7IyuoKqQ==, tarball: https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.166.2.tgz} engines: {node: '>=20.19'} '@tanstack/store@0.9.3': - resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==} + resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==, tarball: https://registry.npmjs.org/@tanstack/store/-/store-0.9.3.tgz} '@tokenizer/inflate@0.4.1': - resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} + resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==, tarball: https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz} engines: {node: '>=18'} '@tokenizer/token@0.3.0': - resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==, tarball: https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz} '@trpc/client@11.12.0': resolution: {integrity: sha512-zTwFKQdE99pvNm7kXFdHo5xIQpGqpQJHtqVkT9o+i8h/0fbDOUBEEbFVICiMsNA+GiXskoaDRX2l+z6ir+Ug3w==} @@ -3782,7 +3807,7 @@ packages: resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==, tarball: https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz} '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -4077,7 +4102,7 @@ packages: optional: true aws-ssl-profiles@1.1.2: - resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==, tarball: https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz} engines: {node: '>= 6.0.0'} axios@1.14.0: @@ -4174,7 +4199,7 @@ packages: optional: true bidi-js@1.0.3: - resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==, tarball: https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz} binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} @@ -4225,7 +4250,7 @@ packages: optional: true c12@3.1.0: - resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==} + resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==, tarball: https://registry.npmjs.org/c12/-/c12-3.1.0.tgz} peerDependencies: magicast: ^0.3.5 peerDependenciesMeta: @@ -4308,14 +4333,14 @@ packages: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} chevrotain@10.5.0: - resolution: {integrity: sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==} + resolution: {integrity: sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==, tarball: https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz} chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==, tarball: https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz} engines: {node: '>= 14.16.0'} chokidar@5.0.0: @@ -4330,7 +4355,7 @@ packages: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} citty@0.2.2: - resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==} + resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==, tarball: https://registry.npmjs.org/citty/-/citty-0.2.2.tgz} class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} @@ -4460,7 +4485,7 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} cookie-es@2.0.1: - resolution: {integrity: sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA==} + resolution: {integrity: sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA==, tarball: https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.1.tgz} cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} @@ -4500,7 +4525,7 @@ packages: engines: {node: '>=12'} css-tree@3.2.1: - resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==, tarball: https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} cssesc@3.0.0: @@ -4509,7 +4534,7 @@ packages: hasBin: true cssstyle@6.2.0: - resolution: {integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==} + resolution: {integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==, tarball: https://registry.npmjs.org/cssstyle/-/cssstyle-6.2.0.tgz} engines: {node: '>=20'} csstype@3.2.3: @@ -4564,7 +4589,7 @@ packages: engines: {node: '>= 12'} data-urls@7.0.0: - resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==, tarball: https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} date-fns-jalali@4.1.0-0: @@ -4593,7 +4618,7 @@ packages: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, tarball: https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz} decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} @@ -4611,7 +4636,7 @@ packages: optional: true deepmerge-ts@7.1.5: - resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} + resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==, tarball: https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz} engines: {node: '>=16.0.0'} deepmerge@4.3.1: @@ -4645,7 +4670,7 @@ packages: engines: {node: '>=0.4.0'} denque@2.1.0: - resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==, tarball: https://registry.npmjs.org/denque/-/denque-2.1.0.tgz} engines: {node: '>=0.10'} depd@2.0.0: @@ -4802,7 +4827,7 @@ packages: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} effect@3.18.4: - resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==} + resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==, tarball: https://registry.npmjs.org/effect/-/effect-3.18.4.tgz} electron-to-chromium@1.5.302: resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} @@ -4987,7 +5012,7 @@ packages: engines: {node: '>=18.0.0'} exact-mirror@0.2.7: - resolution: {integrity: sha512-+MeEmDcLA4o/vjK2zujgk+1VTxPR4hdp23qLqkWfStbECtAq9gmsvQa3LW6z/0GXZyHJobrCnmy1cdeE7BjsYg==} + resolution: {integrity: sha512-+MeEmDcLA4o/vjK2zujgk+1VTxPR4hdp23qLqkWfStbECtAq9gmsvQa3LW6z/0GXZyHJobrCnmy1cdeE7BjsYg==, tarball: https://registry.npmjs.org/exact-mirror/-/exact-mirror-0.2.7.tgz} peerDependencies: '@sinclair/typebox': ^0.34.15 peerDependenciesMeta: @@ -5023,7 +5048,7 @@ packages: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} fast-check@3.23.2: - resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==, tarball: https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz} engines: {node: '>=8.0.0'} fast-copy@4.0.2: @@ -5069,7 +5094,7 @@ packages: engines: {node: '>=18'} file-type@21.3.0: - resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==} + resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==, tarball: https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz} engines: {node: '>=20'} fill-range@7.1.1: @@ -5094,7 +5119,7 @@ packages: optional: true foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==, tarball: https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz} engines: {node: '>=14'} form-data@4.0.5: @@ -5150,7 +5175,7 @@ packages: engines: {node: '>= 8'} fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] @@ -5282,12 +5307,12 @@ packages: next: '>=13.2.0' gel@2.2.0: - resolution: {integrity: sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ==} + resolution: {integrity: sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ==, tarball: https://registry.npmjs.org/gel/-/gel-2.2.0.tgz} engines: {node: '>= 18.0.0'} hasBin: true generate-function@2.3.1: - resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==, tarball: https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz} gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} @@ -5314,7 +5339,7 @@ packages: engines: {node: '>=14.16'} get-port-please@3.2.0: - resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} + resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==, tarball: https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz} get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} @@ -5336,7 +5361,7 @@ packages: hasBin: true giget@2.0.0: - resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} + resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==, tarball: https://registry.npmjs.org/giget/-/giget-2.0.0.tgz} hasBin: true giget@3.2.0: @@ -5358,10 +5383,10 @@ packages: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} grammex@3.1.12: - resolution: {integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==} + resolution: {integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==, tarball: https://registry.npmjs.org/grammex/-/grammex-3.1.12.tgz} graphmatch@1.1.1: - resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} + resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==, tarball: https://registry.npmjs.org/graphmatch/-/graphmatch-1.1.1.tgz} graphql@16.13.0: resolution: {integrity: sha512-uSisMYERbaB9bkA9M4/4dnqyktaEkf1kMHNKq/7DHyxVeWqHQ2mBmVqm5u6/FVHwF3iCNalKcg82Zfl+tffWoA==} @@ -5420,7 +5445,7 @@ packages: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} hono@4.11.4: - resolution: {integrity: sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==} + resolution: {integrity: sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==, tarball: https://registry.npmjs.org/hono/-/hono-4.11.4.tgz} engines: {node: '>=16.9.0'} hono@4.12.3: @@ -5431,7 +5456,7 @@ packages: resolution: {integrity: sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==} html-encoding-sniffer@6.0.0: - resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==, tarball: https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} html-void-elements@3.0.0: @@ -5442,11 +5467,11 @@ packages: engines: {node: '>= 0.8'} http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==, tarball: https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz} engines: {node: '>= 14'} http-status-codes@2.3.0: - resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} + resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==, tarball: https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz} https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} @@ -5465,7 +5490,7 @@ packages: engines: {node: '>=0.10.0'} ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==, tarball: https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz} ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} @@ -5700,13 +5725,13 @@ packages: engines: {node: '>=12'} is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, tarball: https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz} is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} is-property@1.0.2: - resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==, tarball: https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz} is-regexp@3.1.0: resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} @@ -5745,7 +5770,7 @@ packages: engines: {node: '>=18'} isbot@5.1.37: - resolution: {integrity: sha512-5bcicX81xf6NlTEV8rWdg7Pk01LFizDetuYGHx6d/f6y3lR2/oo8IfxjzJqn1UdDEyCcwT9e7NRloj8DwCYujQ==} + resolution: {integrity: sha512-5bcicX81xf6NlTEV8rWdg7Pk01LFizDetuYGHx6d/f6y3lR2/oo8IfxjzJqn1UdDEyCcwT9e7NRloj8DwCYujQ==, tarball: https://registry.npmjs.org/isbot/-/isbot-5.1.37.tgz} engines: {node: '>=18'} isexe@2.0.0: @@ -5778,7 +5803,7 @@ packages: hasBin: true jsdom@28.1.0: - resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==} + resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==, tarball: https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 @@ -5828,133 +5853,133 @@ packages: engines: {node: '>=20.0.0'} lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} + resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==, tarball: https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-android-arm64@1.32.0: - resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==, tarball: https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} + resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==, tarball: https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-arm64@1.32.0: - resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==, tarball: https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} + resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==, tarball: https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-darwin-x64@1.32.0: - resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==, tarball: https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.31.1: - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} + resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==, tarball: https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-freebsd-x64@1.32.0: - resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==, tarball: https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.31.1: - resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} + resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==, tarball: https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm-gnueabihf@1.32.0: - resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==, tarball: https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.31.1: - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} + resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] lightningcss-linux-arm64-gnu@1.32.0: - resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] lightningcss-linux-arm64-musl@1.31.1: - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} + resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] lightningcss-linux-arm64-musl@1.32.0: - resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz} 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==} + resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] lightningcss-linux-x64-gnu@1.32.0: - resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] lightningcss-linux-x64-musl@1.31.1: - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} + resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz} 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==} + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] lightningcss-win32-arm64-msvc@1.31.1: - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} + resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==, tarball: https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-arm64-msvc@1.32.0: - resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==, tarball: https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.31.1: - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} + resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==, tarball: https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] lightningcss-win32-x64-msvc@1.32.0: - resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==, tarball: https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] @@ -5968,7 +5993,7 @@ packages: engines: {node: '>= 12.0.0'} lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==, tarball: https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz} engines: {node: '>=10'} lines-and-columns@1.2.4: @@ -5984,7 +6009,7 @@ packages: engines: {node: '>=20.0.0'} lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, tarball: https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz} log-symbols@6.0.0: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} @@ -5995,7 +6020,7 @@ packages: engines: {node: '>=18'} long@5.3.2: - resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==, tarball: https://registry.npmjs.org/long/-/long-5.3.2.tgz} longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -6005,14 +6030,14 @@ packages: hasBin: true lru-cache@11.3.3: - resolution: {integrity: sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==} + resolution: {integrity: sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==, tarball: https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.3.tgz} engines: {node: 20 || >=22} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} lru.min@1.1.4: - resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} + resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==, tarball: https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz} engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} lucide-react@0.575.0: @@ -6092,7 +6117,7 @@ packages: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} mdn-data@2.27.1: - resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==, tarball: https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz} media-typer@1.1.0: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} @@ -6320,11 +6345,11 @@ packages: engines: {node: ^18.17.0 || >=20.5.0} mysql2@3.15.3: - resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} + resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==, tarball: https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz} engines: {node: '>= 8.0'} named-placeholders@1.1.6: - resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} + resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==, tarball: https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz} engines: {node: '>=8.0.0'} nano-spawn@2.0.0: @@ -6408,7 +6433,7 @@ packages: hasBin: true nypm@0.6.5: - resolution: {integrity: sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==} + resolution: {integrity: sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==, tarball: https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz} engines: {node: '>=18'} hasBin: true @@ -6477,7 +6502,7 @@ packages: engines: {node: '>=20'} openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==, tarball: https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz} ora@8.2.0: resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} @@ -6530,7 +6555,7 @@ packages: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} parse5@8.0.0: - resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==, tarball: https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz} parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} @@ -6570,7 +6595,7 @@ packages: resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} pg-cloudflare@1.3.0: - resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} + resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==, tarball: https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz} pg-connection-string@2.12.0: resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==} @@ -6683,11 +6708,11 @@ packages: engines: {node: '>=0.10.0'} postgres@3.4.7: - resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} + resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==, tarball: https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz} engines: {node: '>=12'} postgres@3.4.8: - resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==} + resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==, tarball: https://registry.npmjs.org/postgres/-/postgres-3.4.8.tgz} engines: {node: '>=12'} posthog-node@5.28.8: @@ -6717,7 +6742,7 @@ packages: engines: {node: '>=18'} prisma@7.4.2: - resolution: {integrity: sha512-2bP8Ruww3Q95Z2eH4Yqh4KAENRsj/SxbdknIVBfd6DmjPwmpsC4OVFMLOeHt6tM3Amh8ebjvstrUz3V/hOe1dA==} + resolution: {integrity: sha512-2bP8Ruww3Q95Z2eH4Yqh4KAENRsj/SxbdknIVBfd6DmjPwmpsC4OVFMLOeHt6tM3Amh8ebjvstrUz3V/hOe1dA==, tarball: https://registry.npmjs.org/prisma/-/prisma-7.4.2.tgz} engines: {node: ^20.19 || ^22.12 || >=24.0} hasBin: true peerDependencies: @@ -6740,7 +6765,7 @@ packages: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} proper-lockfile@4.1.2: - resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==, tarball: https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz} property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -6757,11 +6782,11 @@ packages: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, tarball: https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz} engines: {node: '>=6'} pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==, tarball: https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz} qs@6.15.0: resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} @@ -6832,7 +6857,7 @@ packages: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==, tarball: https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz} react-medium-image-zoom@5.4.3: resolution: {integrity: sha512-cDIwdn35fRUPsGnnj/cG6Pacll+z+Mfv6EWU2wDO5ngbZjg5uLRb2ZhEnh92ufbXCJDFvXHekb8G3+oKqUcv5g==} @@ -6907,7 +6932,7 @@ packages: engines: {node: '>=8.10.0'} readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==, tarball: https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz} engines: {node: '>= 14.18.0'} readdirp@5.0.0: @@ -6962,7 +6987,7 @@ packages: resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} regexp-to-ast@0.5.0: - resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==} + resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==, tarball: https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz} rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} @@ -6989,7 +7014,7 @@ packages: resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} remeda@2.33.4: - resolution: {integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==} + resolution: {integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==, tarball: https://registry.npmjs.org/remeda/-/remeda-2.33.4.tgz} require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} @@ -7027,7 +7052,7 @@ packages: engines: {node: '>=18'} retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==, tarball: https://registry.npmjs.org/retry/-/retry-0.12.0.tgz} engines: {node: '>= 4'} rettime@0.10.1: @@ -7110,7 +7135,7 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, tarball: https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz} engines: {node: '>=v12.22.7'} scheduler@0.27.0: @@ -7142,16 +7167,16 @@ packages: engines: {node: '>= 18'} seq-queue@0.0.5: - resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==, tarball: https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz} seroval-plugins@1.5.2: - resolution: {integrity: sha512-qpY0Cl+fKYFn4GOf3cMiq6l72CpuVaawb6ILjubOQ+diJ54LfOWaSSPsaswN8DRPIPW4Yq+tE1k5aKd7ILyaFg==} + resolution: {integrity: sha512-qpY0Cl+fKYFn4GOf3cMiq6l72CpuVaawb6ILjubOQ+diJ54LfOWaSSPsaswN8DRPIPW4Yq+tE1k5aKd7ILyaFg==, tarball: https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.5.2.tgz} engines: {node: '>=10'} peerDependencies: seroval: ^1.0 seroval@1.5.2: - resolution: {integrity: sha512-xcRN39BdsnO9Tf+VzsE7b3JyTJASItIV1FVFewJKCFcW4s4haIKS3e6vj8PGB9qBwC7tnuOywQMdv5N4qkzi7Q==} + resolution: {integrity: sha512-xcRN39BdsnO9Tf+VzsE7b3JyTJASItIV1FVFewJKCFcW4s4haIKS3e6vj8PGB9qBwC7tnuOywQMdv5N4qkzi7Q==, tarball: https://registry.npmjs.org/seroval/-/seroval-1.5.2.tgz} engines: {node: '>=10'} serve-static@2.2.1: @@ -7172,7 +7197,7 @@ packages: hasBin: true sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==, tarball: https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -7184,7 +7209,7 @@ packages: engines: {node: '>=8'} shell-quote@1.8.3: - resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==, tarball: https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz} engines: {node: '>= 0.4'} shiki@4.0.0: @@ -7272,7 +7297,7 @@ packages: engines: {node: '>= 10.x'} sqlstring@2.3.3: - resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==, tarball: https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz} engines: {node: '>= 0.6'} stack-utils@2.0.6: @@ -7357,7 +7382,7 @@ packages: optional: true strtok3@10.3.5: - resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==} + resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==, tarball: https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz} engines: {node: '>=18'} stubborn-fs@2.0.0: @@ -7405,7 +7430,7 @@ packages: resolution: {integrity: sha512-/HTvXwjLJe1l/MsLXAO1ddCYxElJk4eNR4DzOjDOEmGrPN/3BtBE8perGwMAaJ2sT5T172VkBYzmHcjUfM1JRQ==} symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, tarball: https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz} system-architecture@0.1.0: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} @@ -7448,7 +7473,7 @@ packages: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} tiny-warning@1.0.3: - resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==, tarball: https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz} tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -7480,14 +7505,14 @@ packages: resolution: {integrity: sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==} tldts-core@7.0.28: - resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} + resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==, tarball: https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.28.tgz} tldts@7.0.23: resolution: {integrity: sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==} hasBin: true tldts@7.0.28: - resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==} + resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==, tarball: https://registry.npmjs.org/tldts/-/tldts-7.0.28.tgz} hasBin: true to-regex-range@5.0.1: @@ -7503,7 +7528,7 @@ packages: engines: {node: '>=0.6'} token-types@6.1.2: - resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==, tarball: https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz} engines: {node: '>=14.16'} tough-cookie@6.0.0: @@ -7511,11 +7536,11 @@ packages: engines: {node: '>=16'} tough-cookie@6.0.1: - resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==, tarball: https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz} engines: {node: '>=16'} tr46@6.0.0: - resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==, tarball: https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz} engines: {node: '>=20'} tree-kill@1.2.2: @@ -7572,32 +7597,32 @@ packages: hasBin: true turbo-darwin-64@2.8.10: - resolution: {integrity: sha512-A03fXh+B7S8mL3PbdhTd+0UsaGrhfyPkODvzBDpKRY7bbeac4MDFpJ7I+Slf2oSkCEeSvHKR7Z4U71uKRUfX7g==} + resolution: {integrity: sha512-A03fXh+B7S8mL3PbdhTd+0UsaGrhfyPkODvzBDpKRY7bbeac4MDFpJ7I+Slf2oSkCEeSvHKR7Z4U71uKRUfX7g==, tarball: https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-2.8.10.tgz} cpu: [x64] os: [darwin] turbo-darwin-arm64@2.8.10: - resolution: {integrity: sha512-sidzowgWL3s5xCHLeqwC9M3s9M0i16W1nuQF3Mc7fPHpZ+YPohvcbVFBB2uoRRHYZg6yBnwD4gyUHKTeXfwtXA==} + resolution: {integrity: sha512-sidzowgWL3s5xCHLeqwC9M3s9M0i16W1nuQF3Mc7fPHpZ+YPohvcbVFBB2uoRRHYZg6yBnwD4gyUHKTeXfwtXA==, tarball: https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-2.8.10.tgz} cpu: [arm64] os: [darwin] turbo-linux-64@2.8.10: - resolution: {integrity: sha512-YK9vcpL3TVtqonB021XwgaQhY9hJJbKKUhLv16osxV0HkcQASQWUqR56yMge7puh6nxU67rQlTq1b7ksR1T3KA==} + resolution: {integrity: sha512-YK9vcpL3TVtqonB021XwgaQhY9hJJbKKUhLv16osxV0HkcQASQWUqR56yMge7puh6nxU67rQlTq1b7ksR1T3KA==, tarball: https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-2.8.10.tgz} cpu: [x64] os: [linux] turbo-linux-arm64@2.8.10: - resolution: {integrity: sha512-3+j2tL0sG95iBJTm+6J8/45JsETQABPqtFyYjVjBbi6eVGdtNTiBmHNKrbvXRlQ3ZbUG75bKLaSSDHSEEN+btQ==} + resolution: {integrity: sha512-3+j2tL0sG95iBJTm+6J8/45JsETQABPqtFyYjVjBbi6eVGdtNTiBmHNKrbvXRlQ3ZbUG75bKLaSSDHSEEN+btQ==, tarball: https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-2.8.10.tgz} cpu: [arm64] os: [linux] turbo-windows-64@2.8.10: - resolution: {integrity: sha512-hdeF5qmVY/NFgiucf8FW0CWJWtyT2QPm5mIsX0W1DXAVzqKVXGq+Zf+dg4EUngAFKjDzoBeN6ec2Fhajwfztkw==} + resolution: {integrity: sha512-hdeF5qmVY/NFgiucf8FW0CWJWtyT2QPm5mIsX0W1DXAVzqKVXGq+Zf+dg4EUngAFKjDzoBeN6ec2Fhajwfztkw==, tarball: https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-2.8.10.tgz} cpu: [x64] os: [win32] turbo-windows-arm64@2.8.10: - resolution: {integrity: sha512-QGdr/Q8LWmj+ITMkSvfiz2glf0d7JG0oXVzGL3jxkGqiBI1zXFj20oqVY0qWi+112LO9SVrYdpHS0E/oGFrMbQ==} + resolution: {integrity: sha512-QGdr/Q8LWmj+ITMkSvfiz2glf0d7JG0oXVzGL3jxkGqiBI1zXFj20oqVY0qWi+112LO9SVrYdpHS0E/oGFrMbQ==, tarball: https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-2.8.10.tgz} cpu: [arm64] os: [win32] @@ -7655,7 +7680,7 @@ packages: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} undici@7.24.7: - resolution: {integrity: sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==} + resolution: {integrity: sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==, tarball: https://registry.npmjs.org/undici/-/undici-7.24.7.tgz} engines: {node: '>=20.18.1'} unicorn-magic@0.3.0: @@ -7750,7 +7775,7 @@ packages: hasBin: true valibot@1.2.0: - resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==, tarball: https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz} peerDependencies: typescript: '>=5' peerDependenciesMeta: @@ -7901,7 +7926,7 @@ packages: optional: true w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==, tarball: https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz} engines: {node: '>=18'} web-namespaces@2.0.1: @@ -7912,15 +7937,15 @@ packages: engines: {node: '>= 8'} webidl-conversions@8.0.1: - resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==, tarball: https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz} engines: {node: '>=20'} whatwg-mimetype@5.0.0: - resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==, tarball: https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz} engines: {node: '>=20'} whatwg-url@16.0.1: - resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==, tarball: https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} when-exit@2.1.5: @@ -7986,11 +8011,11 @@ packages: engines: {node: '>=20'} xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, tarball: https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz} engines: {node: '>=18'} xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, tarball: https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz} xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} @@ -8039,7 +8064,7 @@ packages: resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} zeptomatch@2.1.0: - resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} + resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==, tarball: https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.1.0.tgz} zod-to-json-schema@3.25.1: resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} @@ -9533,6 +9558,11 @@ snapshots: '@pinojs/redact@0.4.0': {} + '@polar-sh/sdk@0.47.0': + dependencies: + standardwebhooks: 1.0.0 + zod: 3.25.76 + '@posthog/core@1.24.3': dependencies: cross-spawn: 7.0.6 @@ -11050,7 +11080,7 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vercel/analytics@1.6.1(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)': + '@vercel/analytics@1.6.1(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)': optionalDependencies: next: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) react: 19.2.5 @@ -11244,13 +11274,13 @@ snapshots: auto-bind@5.0.1: {} - autumn-js@1.2.2(better-auth@1.6.2(140411336a30ff0790dc8607571a86e3))(better-call@1.3.5(zod@4.3.6))(express@5.2.1)(hono@4.12.3)(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5): + autumn-js@1.2.2(better-auth@1.6.2(8fff6a967c03a30b47656e909f3a8e44))(better-call@1.3.5(zod@4.3.6))(express@5.2.1)(hono@4.12.3)(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5): dependencies: query-string: 9.3.1 rou3: 0.6.3 zod: 4.3.6 optionalDependencies: - better-auth: 1.6.2(140411336a30ff0790dc8607571a86e3) + better-auth: 1.6.2(8fff6a967c03a30b47656e909f3a8e44) better-call: 1.3.5(zod@4.3.6) express: 5.2.1 hono: 4.12.3 @@ -11274,7 +11304,7 @@ snapshots: baseline-browser-mapping@2.10.0: {} - better-auth@1.6.2(140411336a30ff0790dc8607571a86e3): + better-auth@1.6.2(8fff6a967c03a30b47656e909f3a8e44): dependencies: '@better-auth/core': 1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/drizzle-adapter': 1.6.2(@better-auth/core@1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.18.0)(gel@2.2.0)(kysely@0.28.14)(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3))) @@ -12370,7 +12400,7 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76): + fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76): dependencies: '@formatjs/intl-localematcher': 0.8.2 '@orama/orama': 3.1.18 @@ -12410,14 +12440,14 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-mdx@14.2.11(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)(vite@8.0.3(@types/node@20.19.34)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): + fumadocs-mdx@14.2.11(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.1.0 chokidar: 5.0.0 esbuild: 0.27.3 estree-util-value-to-estree: 3.5.0 - fumadocs-core: 16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76) + fumadocs-core: 16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76) js-yaml: 4.1.1 mdast-util-mdx: 3.0.0 mdast-util-to-markdown: 2.1.2 @@ -12436,11 +12466,10 @@ snapshots: '@types/react': 19.2.14 next: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) react: 19.2.5 - vite: 8.0.3(@types/node@20.19.34)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - fumadocs-ui@16.7.11(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(shiki@4.0.0)(tailwindcss@4.2.1): + fumadocs-ui@16.7.11(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(shiki@4.0.0)(tailwindcss@4.2.1): dependencies: '@fumadocs/tailwind': 0.0.3(tailwindcss@4.2.1) '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -12455,7 +12484,7 @@ snapshots: '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) class-variance-authority: 0.7.1 fuma-cli: 0.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) - fumadocs-core: 16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76) + fumadocs-core: 16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76) lucide-react: 1.8.0(react@19.2.5) motion: 12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) next-themes: 0.4.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -12483,7 +12512,7 @@ snapshots: fuzzysort@3.1.0: {} - geist@1.7.0(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)): + geist@1.7.0(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)): dependencies: next: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -14829,6 +14858,23 @@ snapshots: transitivePeerDependencies: - oxc-resolver + rolldown-plugin-dts@0.22.4(oxc-resolver@11.19.1(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1))(rolldown@1.0.0-rc.8)(typescript@5.9.3): + dependencies: + '@babel/generator': 8.0.0-rc.2 + '@babel/helper-validator-identifier': 8.0.0-rc.2 + '@babel/parser': 8.0.0-rc.2 + '@babel/types': 8.0.0-rc.2 + ast-kit: 3.0.0-beta.1 + birpc: 4.0.0 + dts-resolver: 2.1.3(oxc-resolver@11.19.1(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)) + get-tsconfig: 4.13.6 + obug: 2.1.1 + rolldown: 1.0.0-rc.8 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - oxc-resolver + rolldown@1.0.0-rc.12: dependencies: '@oxc-project/types': 0.122.0 @@ -15529,6 +15575,33 @@ snapshots: - synckit - vue-tsc + tsdown@0.21.1(oxc-resolver@11.19.1(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1))(typescript@5.9.3): + dependencies: + ansis: 4.2.0 + cac: 7.0.0 + defu: 6.1.4 + empathic: 2.0.0 + hookable: 6.0.1 + import-without-cache: 0.2.5 + obug: 2.1.1 + picomatch: 4.0.3 + rolldown: 1.0.0-rc.8 + rolldown-plugin-dts: 0.22.4(oxc-resolver@11.19.1(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1))(rolldown@1.0.0-rc.8)(typescript@5.9.3) + semver: 7.7.4 + tinyexec: 1.0.2 + tinyglobby: 0.2.16 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + unrun: 0.2.31 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@ts-macro/tsc' + - '@typescript/native-preview' + - oxc-resolver + - synckit + - vue-tsc + tslib@2.8.1: {} tsx@4.21.0: @@ -15782,22 +15855,6 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 - vite@8.0.3(@types/node@20.19.34)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - lightningcss: 1.32.0 - picomatch: 4.0.4 - postcss: 8.5.8 - rolldown: 1.0.0-rc.12 - tinyglobby: 0.2.16 - optionalDependencies: - '@types/node': 20.19.34 - esbuild: 0.27.3 - fsevents: 2.3.3 - jiti: 2.6.1 - tsx: 4.21.0 - yaml: 2.8.2 - optional: true - vite@8.0.3(@types/node@25.3.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: lightningcss: 1.32.0 From baa277bd151e0a4c1540b7f96de7bdbb896ce124 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 20 Apr 2026 10:44:53 +0400 Subject: [PATCH 04/13] docs: add Polar provider documentation and enable in sidebar --- landing/content/docs/providers/meta.json | 2 +- landing/content/docs/providers/polar.mdx | 145 ++++++++++++++++++--- landing/content/docs/providers/stripe.mdx | 2 +- landing/src/components/docs/docs-icons.tsx | 2 +- 4 files changed, 128 insertions(+), 23 deletions(-) diff --git a/landing/content/docs/providers/meta.json b/landing/content/docs/providers/meta.json index d64b51ac..1778c933 100644 --- a/landing/content/docs/providers/meta.json +++ b/landing/content/docs/providers/meta.json @@ -1,4 +1,4 @@ { "title": "Providers", - "pages": ["stripe", "paypal", "creem", "polar", "paddle"] + "pages": ["stripe", "polar", "paypal", "creem", "paddle"] } diff --git a/landing/content/docs/providers/polar.mdx b/landing/content/docs/providers/polar.mdx index 17755d78..952fc1f4 100644 --- a/landing/content/docs/providers/polar.mdx +++ b/landing/content/docs/providers/polar.mdx @@ -1,35 +1,140 @@ --- title: Polar -description: Polar is on the roadmap as a future provider adapter. +description: Configure Polar for PayKit, set up webhooks, sync products, and use the customer portal. --- - - Polar is a roadmap provider. Treat this page as a placeholder for the eventual adapter docs. - +Polar is a developer-first payment platform. The `@paykitjs/polar` adapter handles all Polar API interactions, webhook processing, and product syncing. -## Why it belongs in the docs map +## Installation -Polar is a common comparison point for modern SaaS billing, so documenting the future adapter early -helps frame PayKit's provider-agnostic direction. + -## Planned setup +## Configuration -```ts -import { polar } from "paykitjs/providers/polar"; +Pass the `polar()` adapter to `createPayKit` with your access token and webhook secret. -const provider = polar({ - accessToken: process.env.POLAR_ACCESS_TOKEN!, +```ts title="paykit.ts" +import { polar } from "@paykitjs/polar"; +import { createPayKit } from "paykitjs"; + +export const paykit = createPayKit({ + // ... + provider: polar({ + accessToken: process.env.POLAR_ACCESS_TOKEN!, + webhookSecret: process.env.POLAR_WEBHOOK_SECRET!, + server: "sandbox", // or "production" + }), }); ``` -## Expected scope +## Environment variables + +Add these variables to your `.env` file: + +```bash title=".env" +POLAR_ACCESS_TOKEN=polar_oat_... +POLAR_WEBHOOK_SECRET=... +``` + +- `POLAR_ACCESS_TOKEN`: create one in [Polar Settings](https://polar.sh/settings) under **Access Tokens**. The token needs the following scopes: `products:read`, `products:write`, `customers:read`, `customers:write`, `customer_sessions:write`, `subscriptions:read`, `subscriptions:write`, `checkouts:write`, `organizations:read`, `organizations:write`. +- `POLAR_WEBHOOK_SECRET`: generated when you create a webhook endpoint. See the section below. + +## Webhook setup + +In the Polar Dashboard, go to **Settings > Webhooks** and create a new endpoint pointing to: + +``` +https://your-app.com/paykit/api/webhook/polar +``` + +Enable the following events: + +- `checkout.created`, `checkout.updated` +- `subscription.created`, `subscription.updated`, `subscription.active`, `subscription.canceled`, `subscription.uncanceled`, `subscription.revoked` + +You can also select all events. PayKit silently ignores any events it doesn't need. After saving, Polar displays the signing secret. Copy it as your `POLAR_WEBHOOK_SECRET`. + + + Polar uses the [Standard Webhooks](https://www.standardwebhooks.com) specification for signature verification and event deduplication. + + +## Local development + +Use the Polar CLI to forward webhook events to your local server: + +```bash +polar listen http://localhost:3000/paykit/api/webhook/polar +``` + +The CLI prints a webhook signing secret at startup. Use that as `POLAR_WEBHOOK_SECRET` in your local `.env`. -- hosted checkout initiation -- webhook normalization -- synced customer and charge state + + Install the Polar CLI with `curl -fsSL https://polar.sh/install.sh | bash`. You'll need to run `polar login` once to authenticate. + + +## Product syncing + +`paykitjs push` creates and updates Polar products to match your plan definitions. You don't need to touch the Polar Dashboard for product management. + + + +On every push, PayKit automatically: + +- Creates or updates products to match your plans +- Sets all products to **private** visibility (only purchasable via PayKit checkout, not the customer portal) +- Archives orphan products not managed by PayKit +- Configures your Polar organization settings (multiple subscriptions enabled, portal plan changes disabled) + + + Run this once on setup, and again every time you change your plans or pricing. + + +## Customer portal + +PayKit can open Polar's customer portal so users can view their subscriptions and invoices. + + + + ```ts + const { url } = await paykit.customerPortal({ + customerId: "user_123", + returnUrl: "https://myapp.com/billing", + }); + + // redirect the user to `url` + ``` + + + ```ts + const { url } = await paykitClient.customerPortal({ + returnUrl: window.location.href, + }); -## Expected scope + window.location.href = url; + ``` + + -- hosted checkout initiation -- webhook normalization -- synced customer and charge state + + Plan changes are disabled in the portal automatically by PayKit. Subscriptions should be managed through PayKit's API to keep state in sync. + + +## Dedicated account + +PayKit requires full ownership of your Polar account. The `push` command will block if it detects customers on Polar that are not managed by PayKit. Use a dedicated Polar organization for your PayKit integration. + +## Sandbox mode + +Pass `server: "sandbox"` to test with Polar's sandbox environment. This uses separate sandbox API endpoints and test data. + +```ts +provider: polar({ + accessToken: process.env.POLAR_ACCESS_TOKEN!, + webhookSecret: process.env.POLAR_WEBHOOK_SECRET!, + server: "sandbox", +}), +``` + + + Use a sandbox access token when testing. Sandbox and production are completely isolated in Polar. + diff --git a/landing/content/docs/providers/stripe.mdx b/landing/content/docs/providers/stripe.mdx index 85b74d8d..d09e09e5 100644 --- a/landing/content/docs/providers/stripe.mdx +++ b/landing/content/docs/providers/stripe.mdx @@ -1,6 +1,6 @@ --- title: Stripe -description: Configure the Stripe adapter for PayKit, set up webhooks, sync products, and use the customer portal. +description: Configure Stripe for PayKit, set up webhooks, sync products, and use the customer portal. --- Stripe is PayKit's primary payment provider. The `@paykitjs/stripe` adapter handles all Stripe API interactions, webhook processing, and product syncing. diff --git a/landing/src/components/docs/docs-icons.tsx b/landing/src/components/docs/docs-icons.tsx index b6738512..470f44ba 100644 --- a/landing/src/components/docs/docs-icons.tsx +++ b/landing/src/components/docs/docs-icons.tsx @@ -81,7 +81,7 @@ const pageIcons = { skills: , } as const; -const enabledProviders = new Set(["stripe"]); +const enabledProviders = new Set(["stripe", "polar"]); const soonPages = new Set(["drizzleadapter", "prismaadapter", "dashboard"]); const providerPageIcons = { From f7b3532f08a2f4703b5d4c48b0992de18fbf9f30 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 20 Apr 2026 10:46:36 +0400 Subject: [PATCH 05/13] fix(core): remove unused error import from customer API --- packages/paykit/src/customer/customer.api.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/paykit/src/customer/customer.api.ts b/packages/paykit/src/customer/customer.api.ts index 3961af88..b9676625 100644 --- a/packages/paykit/src/customer/customer.api.ts +++ b/packages/paykit/src/customer/customer.api.ts @@ -1,7 +1,6 @@ import * as z from "zod"; import { definePayKitMethod, returnUrl } from "../api/define-route"; -import { PayKitError, PAYKIT_ERROR_CODES } from "../core/errors"; import { getCustomerWithDetails, hardDeleteCustomer, From e32d4fb69991c12ad1682f290ec88dfabd1c3993 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 20 Apr 2026 10:55:24 +0400 Subject: [PATCH 06/13] fix: lint warnings, demo import paths, and polar non-null assertion --- apps/demo/src/app/_components/subscribe-panel.tsx | 2 +- apps/demo/src/app/paykit/[[...slug]]/route.ts | 2 +- apps/demo/src/lib/paykit-client.ts | 2 +- apps/demo/src/{server/plans.ts => lib/paykit-products.ts} | 0 apps/demo/src/{server => lib}/paykit.ts | 2 +- apps/demo/src/server/api/root.ts | 2 +- apps/demo/src/server/api/routers/{paykit.ts => paykit-route.ts} | 2 +- landing/src/app/(marketing)/layout.tsx | 1 - landing/src/app/docs/[[...slug]]/page.tsx | 1 - landing/src/components/docs/docs-icons.tsx | 1 - landing/src/components/layout/navigation-bar.tsx | 2 +- packages/polar/src/polar-provider.ts | 2 +- 12 files changed, 8 insertions(+), 11 deletions(-) rename apps/demo/src/{server/plans.ts => lib/paykit-products.ts} (100%) rename apps/demo/src/{server => lib}/paykit.ts (95%) rename apps/demo/src/server/api/routers/{paykit.ts => paykit-route.ts} (97%) diff --git a/apps/demo/src/app/_components/subscribe-panel.tsx b/apps/demo/src/app/_components/subscribe-panel.tsx index 3c9d3e99..6cf6dcb0 100644 --- a/apps/demo/src/app/_components/subscribe-panel.tsx +++ b/apps/demo/src/app/_components/subscribe-panel.tsx @@ -8,8 +8,8 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; import { Skeleton } from "@/components/ui/skeleton"; +import type { PayKit } from "@/lib/paykit"; import { paykitClient } from "@/lib/paykit-client"; -import type { PayKit } from "@/server/paykit"; import { api, type RouterOutputs } from "@/trpc/react"; type SubscribePlanId = PayKit["planId"]; diff --git a/apps/demo/src/app/paykit/[[...slug]]/route.ts b/apps/demo/src/app/paykit/[[...slug]]/route.ts index 810aae5f..92db7105 100644 --- a/apps/demo/src/app/paykit/[[...slug]]/route.ts +++ b/apps/demo/src/app/paykit/[[...slug]]/route.ts @@ -1,5 +1,5 @@ import { paykitHandler } from "paykitjs/handlers/next"; -import { paykit } from "@/server/paykit"; +import { paykit } from "@/lib/paykit"; export const { GET, POST } = paykitHandler(paykit); diff --git a/apps/demo/src/lib/paykit-client.ts b/apps/demo/src/lib/paykit-client.ts index f1564901..6f8c66a1 100644 --- a/apps/demo/src/lib/paykit-client.ts +++ b/apps/demo/src/lib/paykit-client.ts @@ -1,5 +1,5 @@ import { createPayKitClient } from "paykitjs/client"; -import type { paykit } from "@/server/paykit"; +import type { paykit } from "@/lib/paykit"; export const paykitClient = createPayKitClient(); diff --git a/apps/demo/src/server/plans.ts b/apps/demo/src/lib/paykit-products.ts similarity index 100% rename from apps/demo/src/server/plans.ts rename to apps/demo/src/lib/paykit-products.ts diff --git a/apps/demo/src/server/paykit.ts b/apps/demo/src/lib/paykit.ts similarity index 95% rename from apps/demo/src/server/paykit.ts rename to apps/demo/src/lib/paykit.ts index 00a700db..41f0f6b7 100644 --- a/apps/demo/src/server/paykit.ts +++ b/apps/demo/src/lib/paykit.ts @@ -7,7 +7,7 @@ import { env } from "@/env"; import { auth } from "@/server/auth"; import { pool } from "@/server/db"; -import { free, pro, ultra } from "./plans"; +import { free, pro, ultra } from "./paykit-products"; export const paykit = createPayKit({ testing: { enabled: true }, diff --git a/apps/demo/src/server/api/root.ts b/apps/demo/src/server/api/root.ts index 53dbfd5e..8d03d59b 100644 --- a/apps/demo/src/server/api/root.ts +++ b/apps/demo/src/server/api/root.ts @@ -1,5 +1,5 @@ import { autumnRouter } from "@/server/api/routers/autumn"; -import { paykitRouter } from "@/server/api/routers/paykit"; +import { paykitRouter } from "@/server/api/routers/paykit-route"; import { postRouter } from "@/server/api/routers/post"; import { createCallerFactory, createTRPCRouter } from "@/server/api/trpc"; diff --git a/apps/demo/src/server/api/routers/paykit.ts b/apps/demo/src/server/api/routers/paykit-route.ts similarity index 97% rename from apps/demo/src/server/api/routers/paykit.ts rename to apps/demo/src/server/api/routers/paykit-route.ts index 7624ecfb..d335c607 100644 --- a/apps/demo/src/server/api/routers/paykit.ts +++ b/apps/demo/src/server/api/routers/paykit-route.ts @@ -1,9 +1,9 @@ import { TRPCError } from "@trpc/server"; import { z } from "zod"; +import { paykit, type PayKit } from "@/lib/paykit"; import { createTRPCRouter, publicProcedure } from "@/server/api/trpc"; import { auth } from "@/server/auth"; -import { paykit, type PayKit } from "@/server/paykit"; export const paykitRouter = createTRPCRouter({ createCustomer: publicProcedure.mutation(async ({ ctx }) => { diff --git a/landing/src/app/(marketing)/layout.tsx b/landing/src/app/(marketing)/layout.tsx index 8a2751ad..0a5bfab2 100644 --- a/landing/src/app/(marketing)/layout.tsx +++ b/landing/src/app/(marketing)/layout.tsx @@ -3,7 +3,6 @@ import type { ReactNode } from "react"; import { CommandMenuProvider } from "@/components/command-menu"; import { NavigationBar } from "@/components/layout/navigation-bar"; import { PageTransition } from "@/components/layout/page-transition"; -import { getGitHubStars } from "@/lib/github"; export default async function MarketingLayout({ children }: { children: ReactNode }) { // const stars = await getGitHubStars(); diff --git a/landing/src/app/docs/[[...slug]]/page.tsx b/landing/src/app/docs/[[...slug]]/page.tsx index 98195d63..6351e6d3 100644 --- a/landing/src/app/docs/[[...slug]]/page.tsx +++ b/landing/src/app/docs/[[...slug]]/page.tsx @@ -12,7 +12,6 @@ import { CopyMarkdownButton } from "@/components/docs/copy-markdown-button"; import { Features } from "@/components/docs/features"; import { PackageInstall, PackageRun } from "@/components/docs/package-command"; import { TocFooter } from "@/components/docs/toc-footer"; -import { URLs } from "@/lib/consts"; import { source } from "@/lib/source"; import { cn } from "@/lib/utils"; diff --git a/landing/src/components/docs/docs-icons.tsx b/landing/src/components/docs/docs-icons.tsx index 470f44ba..56ec33b8 100644 --- a/landing/src/components/docs/docs-icons.tsx +++ b/landing/src/components/docs/docs-icons.tsx @@ -13,7 +13,6 @@ import { Gauge, GitCompareArrows, LayoutDashboard, - Layers, Monitor, Package, ReceiptText, diff --git a/landing/src/components/layout/navigation-bar.tsx b/landing/src/components/layout/navigation-bar.tsx index db1bc20b..839afb8c 100644 --- a/landing/src/components/layout/navigation-bar.tsx +++ b/landing/src/components/layout/navigation-bar.tsx @@ -80,7 +80,7 @@ const labelBase = // ─── Component ─────────────────────────────────────────────────────── -export function NavigationBar({ stars }: { stars: number | null }) { +export function NavigationBar({ stars: _stars }: { stars: number | null }) { const routerPathname = usePathname(); const [pathname, setPathname] = useState("/"); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); diff --git a/packages/polar/src/polar-provider.ts b/packages/polar/src/polar-provider.ts index d8bc38d2..0243bb5f 100644 --- a/packages/polar/src/polar-provider.ts +++ b/packages/polar/src/polar-provider.ts @@ -253,7 +253,7 @@ export function createPolarProvider(client: Polar, options: PolarOptions): Payme await client.subscriptions.update({ id: data.providerSubscriptionId, subscriptionUpdate: { - productId: data.providerProduct?.productId!, + productId: data.providerProduct!.productId!, prorationBehavior: "next_period", }, }); From 99201d0a7af410d99f0ac6becead585218569437 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 22 Apr 2026 19:24:34 +0400 Subject: [PATCH 07/13] docs(stripe): fix webhook URL to include /stripe provider suffix --- landing/content/docs/providers/stripe.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/landing/content/docs/providers/stripe.mdx b/landing/content/docs/providers/stripe.mdx index 6625e012..ddb1aa7a 100644 --- a/landing/content/docs/providers/stripe.mdx +++ b/landing/content/docs/providers/stripe.mdx @@ -43,7 +43,7 @@ STRIPE_WEBHOOK_SECRET=whsec_... In the Stripe Dashboard, go to **Developers > Webhooks** and create a new endpoint pointing to: ``` -https://your-app.com/paykit/api/webhook +https://your-app.com/paykit/api/webhook/stripe ``` Select the following events when creating the endpoint: @@ -70,7 +70,7 @@ After saving, Stripe displays the signing secret. Copy it as your `STRIPE_WEBHOO Use the Stripe CLI to forward webhook events to your local server: ```bash -stripe listen --forward-to localhost:3000/paykit/api/webhook +stripe listen --forward-to localhost:3000/paykit/api/webhook/stripe ``` The CLI prints a webhook signing secret at startup. Use that as `STRIPE_WEBHOOK_SECRET` in your local `.env`. From c0a52ee980f5c685af9c8916e253353f76d265aa Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 22 Apr 2026 19:24:43 +0400 Subject: [PATCH 08/13] feat(core): add upsertProviderCustomer flag to upsertCustomer Allows eagerly creating the provider customer in the same call instead of the default lazy behavior. Useful when the provider customer ID is needed immediately after creation. --- packages/paykit/src/customer/customer.api.ts | 1 + packages/paykit/src/customer/customer.service.ts | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/paykit/src/customer/customer.api.ts b/packages/paykit/src/customer/customer.api.ts index b9676625..d3ef8fcc 100644 --- a/packages/paykit/src/customer/customer.api.ts +++ b/packages/paykit/src/customer/customer.api.ts @@ -14,6 +14,7 @@ const upsertCustomerSchema = z.object({ email: z.string().optional(), name: z.string().optional(), metadata: z.record(z.string(), z.string()).optional(), + upsertProviderCustomer: z.boolean().optional(), }); const customerIdSchema = z.object({ diff --git a/packages/paykit/src/customer/customer.service.ts b/packages/paykit/src/customer/customer.service.ts index ccb6169e..7f6dbcdc 100644 --- a/packages/paykit/src/customer/customer.service.ts +++ b/packages/paykit/src/customer/customer.service.ts @@ -184,11 +184,18 @@ export async function ensureDefaultPlansForCustomer( export async function upsertCustomer( ctx: PayKitContext, - input: Parameters[1], + input: Parameters[1] & { upsertProviderCustomer?: boolean }, ): Promise { const syncedCustomer = await syncCustomer(ctx.database, input); await ensureDefaultPlansForCustomer(ctx, syncedCustomer.id); + if (input.upsertProviderCustomer) { + await upsertProviderCustomer(ctx, { + customerId: syncedCustomer.id, + customerRow: syncedCustomer, + }); + } + return syncedCustomer; } @@ -361,11 +368,12 @@ function providerCustomerNeedsSync( export async function upsertProviderCustomer( ctx: PayKitContext, - input: { customerId: string }, + input: { customerId: string; customerRow?: Customer }, ): Promise<{ customerId: string; providerCustomer: ProviderCustomer; providerCustomerId: string }> { const providerId = ctx.provider.id; - const existingCustomer = await getCustomerByIdOrThrow(ctx.database, input.customerId); + const existingCustomer = + input.customerRow ?? (await getCustomerByIdOrThrow(ctx.database, input.customerId)); const existingProviderCustomer = getProviderCustomer(existingCustomer, providerId); const existingProviderCustomerId = existingProviderCustomer?.id ?? null; From 687042ec92ab652263ba0daad0e87f29eac03d80 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 22 Apr 2026 19:24:57 +0400 Subject: [PATCH 09/13] feat(e2e): make smoke tests provider-agnostic with harness abstraction Refactor e2e smoke tests to support multiple payment providers: - Add ProviderHarness interface with Stripe and Polar implementations - Replace hardcoded Stripe logic with harness-driven setup - Add subscribeCustomer() helper that handles both direct and checkout flows - Add capability-based test skipping (testClocks, directSubscription) - Centralize env config via @t3-oss/env-core - Use unique customer IDs/emails per run to avoid provider conflicts - Add per-provider scripts (test:stripe, test:polar) --- e2e/cli/setup.ts | 15 +- e2e/env.ts | 25 ++ e2e/package.json | 8 +- e2e/smoke/cancel/cancel-then-upgrade.test.ts | 19 +- .../cancel/downgrade-change-target.test.ts | 25 +- e2e/smoke/entitlements/check-boolean.test.ts | 7 +- e2e/smoke/entitlements/check-metered.test.ts | 7 +- .../entitlements/stacked-metered.test.ts | 13 +- e2e/smoke/harness/index.ts | 19 ++ e2e/smoke/harness/polar.ts | 77 +++++ e2e/smoke/harness/stripe.ts | 93 ++++++ e2e/smoke/harness/types.ts | 27 ++ e2e/smoke/setup.ts | 292 ++++++++++-------- .../subscribe/cancel-end-of-cycle.test.ts | 217 +++++++------ e2e/smoke/subscribe/cancel-resume.test.ts | 19 +- .../subscribe/downgrade-scheduled.test.ts | 19 +- e2e/smoke/subscribe/downgrade-to-free.test.ts | 13 +- e2e/smoke/subscribe/renewal.test.ts | 209 ++++++------- e2e/smoke/subscribe/same-plan-noop.test.ts | 13 +- e2e/smoke/subscribe/subscribe-paid.test.ts | 20 +- e2e/smoke/subscribe/upgrade-immediate.test.ts | 13 +- e2e/smoke/webhook/duplicate-webhook.test.ts | 7 +- .../webhook/subscription-deleted.test.ts | 152 ++++----- pnpm-lock.yaml | 76 +++-- 24 files changed, 809 insertions(+), 576 deletions(-) create mode 100644 e2e/env.ts create mode 100644 e2e/smoke/harness/index.ts create mode 100644 e2e/smoke/harness/polar.ts create mode 100644 e2e/smoke/harness/stripe.ts create mode 100644 e2e/smoke/harness/types.ts diff --git a/e2e/cli/setup.ts b/e2e/cli/setup.ts index 73fcb705..8b74b8fe 100644 --- a/e2e/cli/setup.ts +++ b/e2e/cli/setup.ts @@ -2,15 +2,12 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { config } from "dotenv"; import { Pool } from "pg"; import { default as Stripe } from "stripe"; -process.env.PAYKIT_CLI = "1"; +import { env } from "../env"; -// Load env from repo root -config({ path: path.resolve(import.meta.dirname, "../../.env") }); -config({ path: path.resolve(import.meta.dirname, "../../.env.local"), override: true }); +process.env.PAYKIT_CLI = "1"; const packageRoot = path.resolve(import.meta.dirname, "../../packages/paykit"); const createPayKitPath = path.resolve(packageRoot, "src/index.ts"); @@ -29,17 +26,17 @@ export interface CliTestFixture { * Postgres DB and real Stripe. Returns everything needed for cleanup. */ export async function createCliFixture(_globalKey: string): Promise { - const secretKey = process.env.STRIPE_SECRET_KEY; - const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; + const secretKey = env.E2E_STRIPE_SK; + const webhookSecret = env.E2E_STRIPE_WHSEC; if (!secretKey || !webhookSecret) { - throw new Error("STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET must be set"); + throw new Error("E2E_STRIPE_SK and E2E_STRIPE_WHSEC must be set"); } const stripeClient = new Stripe(secretKey); // Create a fresh test database const dbName = `paykit_cli_${String(Date.now())}`; - const adminUrl = process.env.TEST_DATABASE_URL ?? "postgresql://localhost:5432/postgres"; + const adminUrl = env.TEST_DATABASE_URL; const adminPool = new Pool({ connectionString: adminUrl }); await adminPool.query(`CREATE DATABASE "${dbName}"`); await adminPool.end(); diff --git a/e2e/env.ts b/e2e/env.ts new file mode 100644 index 00000000..477395cd --- /dev/null +++ b/e2e/env.ts @@ -0,0 +1,25 @@ +import path from "node:path"; + +import { createEnv } from "@t3-oss/env-core"; +import { config } from "dotenv"; +import * as z from "zod"; + +config({ path: path.resolve(import.meta.dirname, "../.env"), quiet: true }); +config({ path: path.resolve(import.meta.dirname, "../.env.local"), override: true, quiet: true }); + +export const env = createEnv({ + server: { + PROVIDER: z.enum(["stripe", "polar"]).default("stripe"), + TEST_DATABASE_URL: z.string().default("postgresql://localhost:5432/postgres"), + + // Stripe + E2E_STRIPE_SK: z.string().optional(), + E2E_STRIPE_WHSEC: z.string().optional(), + + // Polar + E2E_POLAR_ACCESS_TOKEN: z.string().optional(), + E2E_POLAR_WHSEC: z.string().optional(), + }, + runtimeEnv: process.env, + emptyStringAsUndefined: true, +}); diff --git a/e2e/package.json b/e2e/package.json index af2543d5..aa2a95c7 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -4,6 +4,8 @@ "type": "module", "scripts": { "test": "vitest run --config smoke/vitest.automated.config.ts", + "test:stripe": "PROVIDER=stripe vitest run --config smoke/vitest.automated.config.ts", + "test:polar": "PROVIDER=polar vitest run --config smoke/vitest.automated.config.ts", "test:watch": "vitest --config smoke/vitest.automated.config.ts", "test:manual": "vitest run --config smoke/vitest.manual.config.ts", "test:all": "vitest run --config smoke/vitest.config.ts", @@ -12,13 +14,17 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "devDependencies": { + "@paykitjs/polar": "workspace:*", "@paykitjs/stripe": "workspace:*", + "@t3-oss/env-core": "^0.12.0", "@types/pg": "^8.18.0", "dotenv": "^17.3.1", "drizzle-orm": "^0.45.1", "paykitjs": "workspace:*", "pg": "^8.20.0", + "playwright": "^1.52.0", "stripe": "^19.1.0", - "vitest": "^4.0.18" + "vitest": "^4.0.18", + "zod": "^4.0.0" } } diff --git a/e2e/smoke/cancel/cancel-then-upgrade.test.ts b/e2e/smoke/cancel/cancel-then-upgrade.test.ts index 320ae620..4f081d95 100644 --- a/e2e/smoke/cancel/cancel-then-upgrade.test.ts +++ b/e2e/smoke/cancel/cancel-then-upgrade.test.ts @@ -7,6 +7,7 @@ import { expectProduct, expectSingleActivePlanInGroup, expectSingleScheduledPlanInGroup, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -27,17 +28,9 @@ describe("cancel-then-upgrade: pro → free (scheduled) → ultra (upgrade)", () customerId = customer.customerId; // Setup: subscribe to Pro, then schedule downgrade to Free - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); - await t.paykit.subscribe({ - customerId, - planId: "free", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "free" }); }); afterAll(async () => { @@ -67,11 +60,7 @@ describe("cancel-then-upgrade: pro → free (scheduled) → ultra (upgrade)", () }); // Action: upgrade to Ultra - await t.paykit.subscribe({ - customerId, - planId: "ultra", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "ultra" }); // Ultra is active await expectProduct({ diff --git a/e2e/smoke/cancel/downgrade-change-target.test.ts b/e2e/smoke/cancel/downgrade-change-target.test.ts index 6e023b83..5f047a10 100644 --- a/e2e/smoke/cancel/downgrade-change-target.test.ts +++ b/e2e/smoke/cancel/downgrade-change-target.test.ts @@ -8,6 +8,7 @@ import { expectProductNotPresent, expectSingleActivePlanInGroup, expectSingleScheduledPlanInGroup, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -28,24 +29,12 @@ describe("downgrade-change-target: ultra → pro (scheduled) → free (change ta customerId = customer.customerId; // Setup: subscribe Pro → upgrade Ultra - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); - await t.paykit.subscribe({ - customerId, - planId: "ultra", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "ultra" }); // Schedule downgrade to Pro - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); }); afterAll(async () => { @@ -75,11 +64,7 @@ describe("downgrade-change-target: ultra → pro (scheduled) → free (change ta }); // Action: change downgrade target to Free instead - await t.paykit.subscribe({ - customerId, - planId: "free", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "free" }); // Ultra still canceling await expectProduct({ diff --git a/e2e/smoke/entitlements/check-boolean.test.ts b/e2e/smoke/entitlements/check-boolean.test.ts index 3d6b0f68..9eb3ddd6 100644 --- a/e2e/smoke/entitlements/check-boolean.test.ts +++ b/e2e/smoke/entitlements/check-boolean.test.ts @@ -4,6 +4,7 @@ import { createTestCustomerWithPM, createTestPayKit, dumpStateOnFailure, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -35,11 +36,7 @@ describe("check-boolean: boolean feature access", () => { expect(freeCheck.allowed).toBe(false); // Subscribe to Pro (includes dashboard) - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); // On Pro — dashboard accessible const proCheck = await t.paykit.check({ customerId, featureId: "dashboard" }); diff --git a/e2e/smoke/entitlements/check-metered.test.ts b/e2e/smoke/entitlements/check-metered.test.ts index 855c2f69..6ce5280a 100644 --- a/e2e/smoke/entitlements/check-metered.test.ts +++ b/e2e/smoke/entitlements/check-metered.test.ts @@ -5,6 +5,7 @@ import { createTestPayKit, dumpStateOnFailure, expectExactMeteredBalance, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -25,11 +26,7 @@ describe("check-metered: metered feature balance and usage reporting", () => { customerId = customer.customerId; // Subscribe to Pro (500 messages/month) - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); }); afterAll(async () => { diff --git a/e2e/smoke/entitlements/stacked-metered.test.ts b/e2e/smoke/entitlements/stacked-metered.test.ts index 4d810e43..58504160 100644 --- a/e2e/smoke/entitlements/stacked-metered.test.ts +++ b/e2e/smoke/entitlements/stacked-metered.test.ts @@ -5,6 +5,7 @@ import { createTestPayKit, dumpStateOnFailure, expectExactMeteredBalance, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -25,18 +26,10 @@ describe("stacked-metered: cross-group entitlement aggregation", () => { customerId = customer.customerId; // Pro (base group): 500 messages/month - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); // Extra Messages (addons group): 200 messages/month - await t.paykit.subscribe({ - customerId, - planId: "extra_messages", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "extra_messages" }); }); afterAll(async () => { diff --git a/e2e/smoke/harness/index.ts b/e2e/smoke/harness/index.ts new file mode 100644 index 00000000..3792b3b9 --- /dev/null +++ b/e2e/smoke/harness/index.ts @@ -0,0 +1,19 @@ +import { env } from "../../env"; +import { createPolarHarness } from "./polar"; +import { createStripeHarness } from "./stripe"; +import type { ProviderHarness } from "./types"; + +export type { ProviderCapabilities, ProviderHarness } from "./types"; + +export function loadHarness(): ProviderHarness { + const provider = env.PROVIDER; + + switch (provider) { + case "stripe": + return createStripeHarness(); + case "polar": + return createPolarHarness(); + default: + throw new Error(`Unknown provider: ${provider}. Supported: stripe, polar`); + } +} diff --git a/e2e/smoke/harness/polar.ts b/e2e/smoke/harness/polar.ts new file mode 100644 index 00000000..af97ebf0 --- /dev/null +++ b/e2e/smoke/harness/polar.ts @@ -0,0 +1,77 @@ +import { polar } from "@paykitjs/polar"; +import { chromium } from "playwright"; + +import { env } from "../../env"; +import type { ProviderHarness } from "./types"; + +export function createPolarHarness(): ProviderHarness { + const accessToken = env.E2E_POLAR_ACCESS_TOKEN; + const webhookSecret = env.E2E_POLAR_WHSEC; + if (!accessToken || !webhookSecret) { + throw new Error("E2E_POLAR_ACCESS_TOKEN and E2E_POLAR_WHSEC must be set"); + } + + return { + id: "polar", + capabilities: { + testClocks: false, + directSubscription: false, + }, + + createProviderConfig() { + return polar({ accessToken: accessToken!, webhookSecret: webhookSecret!, server: "sandbox" }); + }, + + async setupCustomerForDirectSubscription(_providerCustomerId: string) { + // Polar doesn't support direct subscription — always goes through checkout. + // This is a no-op; tests will get a paymentUrl and call completeCheckout. + }, + + async completeCheckout(url: string) { + const browser = await chromium.launch({ headless: true }); + const page = await browser.newPage(); + + try { + await page.goto(url, { waitUntil: "networkidle" }); + + // Polar sandbox checkout — fill test card details + await page.fill( + '[data-testid="card-number"], input[name="cardNumber"], input[placeholder*="card number" i]', + "4242424242424242", + ); + await page.fill( + '[data-testid="card-expiry"], input[name="cardExpiry"], input[placeholder*="MM" i]', + "12/30", + ); + await page.fill( + '[data-testid="card-cvc"], input[name="cardCvc"], input[placeholder*="CVC" i]', + "123", + ); + + // Submit payment + const submitButton = page.locator( + 'button[type="submit"], button:has-text("Pay"), button:has-text("Subscribe")', + ); + await submitButton.click(); + + // Wait for redirect to success URL or confirmation + await page.waitForURL("**/success**", { timeout: 30_000 }).catch(() => { + // Some checkouts show a confirmation page rather than redirecting + }); + } finally { + await browser.close(); + } + }, + + async cleanup(_ctx) { + // Polar sandbox has no test clocks to clean up. + // Subscriptions in sandbox are ephemeral. + }, + + validateEnv() { + if (!env.E2E_POLAR_ACCESS_TOKEN || !env.E2E_POLAR_WHSEC) { + throw new Error("E2E_POLAR_ACCESS_TOKEN and E2E_POLAR_WHSEC must be set"); + } + }, + }; +} diff --git a/e2e/smoke/harness/stripe.ts b/e2e/smoke/harness/stripe.ts new file mode 100644 index 00000000..0e830e23 --- /dev/null +++ b/e2e/smoke/harness/stripe.ts @@ -0,0 +1,93 @@ +import { stripe } from "@paykitjs/stripe"; +import { default as Stripe } from "stripe"; + +import type { PayKitDatabase } from "../../../packages/paykit/src/database/index"; +import { syncPaymentMethodByProviderCustomer } from "../../../packages/paykit/src/payment-method/payment-method.service"; +import { env } from "../../env"; +import type { ProviderHarness } from "./types"; + +export function createStripeHarness(): ProviderHarness { + const secretKey = env.E2E_STRIPE_SK; + const webhookSecret = env.E2E_STRIPE_WHSEC; + if (!secretKey || !webhookSecret) { + throw new Error("E2E_STRIPE_SK and E2E_STRIPE_WHSEC must be set"); + } + + const stripeClient = new Stripe(secretKey); + + return { + id: "stripe", + capabilities: { + testClocks: true, + directSubscription: true, + }, + + createProviderConfig() { + return stripe({ secretKey, webhookSecret }); + }, + + async setupCustomerForDirectSubscription(providerCustomerId: string) { + const pm = await stripeClient.paymentMethods.attach("pm_card_visa", { + customer: providerCustomerId, + }); + await stripeClient.customers.update(providerCustomerId, { + invoice_settings: { default_payment_method: pm.id }, + }); + }, + + async completeCheckout(_url: string) { + throw new Error("Stripe direct-subscription tests should not need checkout completion"); + }, + + async cleanup(ctx) { + // Delete test clocks for all customers + for (const providerCustomerId of ctx.providerCustomerIds) { + try { + const customer = await stripeClient.customers.retrieve(providerCustomerId); + if ("deleted" in customer && customer.deleted) continue; + const testClockId = (customer as Stripe.Customer).test_clock; + if (testClockId && typeof testClockId === "string") { + await stripeClient.testHelpers.testClocks.del(testClockId).catch(() => {}); + } + } catch { + // Customer may already be deleted + } + } + }, + + validateEnv() { + if (!env.E2E_STRIPE_SK || !env.E2E_STRIPE_WHSEC) { + throw new Error("E2E_STRIPE_SK and E2E_STRIPE_WHSEC must be set"); + } + }, + }; +} + +/** Sync a Stripe payment method into the PayKit database. */ +export async function syncStripePaymentMethod(input: { + database: PayKitDatabase; + providerCustomerId: string; + providerId: string; + stripeClient: Stripe; +}): Promise { + const pm = await input.stripeClient.paymentMethods.list({ + customer: input.providerCustomerId, + type: "card", + limit: 1, + }); + const method = pm.data[0]; + if (!method) return; + + await syncPaymentMethodByProviderCustomer(input.database, { + paymentMethod: { + providerMethodId: method.id, + type: method.type, + last4: method.card?.last4, + expiryMonth: method.card?.exp_month, + expiryYear: method.card?.exp_year, + isDefault: true, + }, + providerCustomerId: input.providerCustomerId, + providerId: input.providerId, + }); +} diff --git a/e2e/smoke/harness/types.ts b/e2e/smoke/harness/types.ts new file mode 100644 index 00000000..68a94814 --- /dev/null +++ b/e2e/smoke/harness/types.ts @@ -0,0 +1,27 @@ +import type { PayKitProviderConfig } from "paykitjs"; + +export interface ProviderCapabilities { + testClocks: boolean; + directSubscription: boolean; +} + +export interface ProviderHarness { + id: string; + capabilities: ProviderCapabilities; + + createProviderConfig(): PayKitProviderConfig; + + /** + * Make the customer ready to subscribe without checkout (e.g., attach PM for Stripe). + * For providers that only support checkout, this is a no-op. + */ + setupCustomerForDirectSubscription(providerCustomerId: string): Promise; + + /** Complete a hosted checkout given the URL (e.g., Playwright automation). */ + completeCheckout(url: string): Promise; + + /** Provider-specific cleanup (e.g., delete test clocks). */ + cleanup(ctx: { providerCustomerIds: string[] }): Promise; + + validateEnv(): void; +} diff --git a/e2e/smoke/setup.ts b/e2e/smoke/setup.ts index 4c40963e..7c044859 100644 --- a/e2e/smoke/setup.ts +++ b/e2e/smoke/setup.ts @@ -1,8 +1,5 @@ import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http"; -import path from "node:path"; -import { stripe } from "@paykitjs/stripe"; -import { config } from "dotenv"; import { and, count, desc, eq, gt, inArray, isNull, or, sql } from "drizzle-orm"; import { createPayKit, feature, plan } from "paykitjs"; import { Pool } from "pg"; @@ -20,12 +17,15 @@ import { } from "../../packages/paykit/src/database/schema"; import { syncPaymentMethodByProviderCustomer } from "../../packages/paykit/src/payment-method/payment-method.service"; import { syncProducts } from "../../packages/paykit/src/product/product-sync.service"; - -config({ path: path.resolve(import.meta.dirname, "../../.env") }); -config({ path: path.resolve(import.meta.dirname, "../../.env.local"), override: true }); +import { env } from "../env"; +import { loadHarness } from "./harness/index"; +import type { ProviderCapabilities, ProviderHarness } from "./harness/types"; const WEBHOOK_PORT = 4567; +// Provider harness — loaded once at module init based on PROVIDER env var +export const harness: ProviderHarness = loadHarness(); + const messagesFeature = feature({ id: "messages", type: "metered" }); const dashboardFeature = feature({ id: "dashboard", type: "boolean" }); const adminFeature = feature({ id: "admin", type: "boolean" }); @@ -80,7 +80,7 @@ type SmokePayKit = ReturnType< typeof createPayKit<{ database: Pool; plans: typeof smokePlans; - provider: ReturnType; + provider: ReturnType; testing: { enabled: true }; }> >; @@ -89,7 +89,7 @@ export interface TestPayKit { paykit: SmokePayKit; database: PayKitDatabase; ctx: PayKitContext; - stripeClient: Stripe; + harness: ProviderHarness; dbPath: string; server: Server; webhookRequests: CapturedWebhookRequest[]; @@ -106,137 +106,123 @@ export interface CapturedWebhookRequest { const activeSubscriptionStatuses = ["active", "trialing", "past_due"] as const; const presentSubscriptionStatuses = [...activeSubscriptionStatuses, "scheduled"] as const; -// createTestPayKit - export async function createTestPayKit(): Promise { - const secretKey = process.env.STRIPE_SECRET_KEY; - const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; - if (!secretKey || !webhookSecret) { - throw new Error("STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET must be set"); - } - - const stripeClient = new Stripe(secretKey); + harness.validateEnv(); // 1. Create a fresh test database const dbName = `paykit_smoke_${String(Date.now())}`; const adminPool = new Pool({ - connectionString: process.env.TEST_DATABASE_URL ?? "postgresql://localhost:5432/postgres", + connectionString: env.TEST_DATABASE_URL, }); await adminPool.query(`CREATE DATABASE "${dbName}"`); await adminPool.end(); - const dbUrl = (process.env.TEST_DATABASE_URL ?? "postgresql://localhost:5432/postgres").replace( - /\/[^/]*$/, - `/${dbName}`, - ); + const dbUrl = env.TEST_DATABASE_URL.replace(/\/[^/]*$/, `/${dbName}`); const pool = new Pool({ connectionString: dbUrl }); // 2. Run migrations await migrateDatabase(pool); - // 3. Create PayKit instance with real Stripe - const stripeProvider = stripe({ secretKey, webhookSecret }); + // 3. Create PayKit instance with the active provider + const providerConfig = harness.createProviderConfig(); const paykit = createPayKit({ database: pool, plans: smokePlans, - provider: stripeProvider, + provider: providerConfig, testing: { enabled: true }, }); const ctx = await paykit.$context; - // Override createSubscription to use allow_incomplete. The default - // payment_behavior: "default_incomplete" requires client-side payment - // confirmation which isn't possible in automated tests. - (ctx.provider as unknown as Record).createSubscription = async (data: { - providerCustomerId: string; - providerProduct: Record; - }) => { - const sub = await stripeClient.subscriptions.create({ - customer: data.providerCustomerId, - items: [{ price: data.providerProduct.priceId }], - payment_behavior: "allow_incomplete", - expand: ["latest_invoice"], - }); + // Stripe-specific: Override createSubscription to use allow_incomplete. + // This allows direct subscription without client-side payment confirmation. + if (harness.id === "stripe") { + const secretKey = env.E2E_STRIPE_SK!; + const stripeClient = new Stripe(secretKey); + + (ctx.provider as unknown as Record).createSubscription = async (data: { + providerCustomerId: string; + providerProduct: Record; + }) => { + const sub = await stripeClient.subscriptions.create({ + customer: data.providerCustomerId, + items: [{ price: data.providerProduct.priceId }], + payment_behavior: "allow_incomplete", + expand: ["latest_invoice"], + }); - const firstItem = sub.items.data[0]; - const periodStart = firstItem?.current_period_start ?? null; - const periodEnd = firstItem?.current_period_end ?? null; - const latestInvoice = sub.latest_invoice; - const invoice = - latestInvoice && typeof latestInvoice !== "string" - ? { - currency: latestInvoice.currency, - hostedUrl: latestInvoice.hosted_invoice_url ?? null, - periodEndAt: latestInvoice.period_end - ? new Date(latestInvoice.period_end * 1000) - : null, - periodStartAt: latestInvoice.period_start - ? new Date(latestInvoice.period_start * 1000) - : null, - providerInvoiceId: latestInvoice.id, - status: latestInvoice.status, - totalAmount: latestInvoice.total, - } - : null; - - return { - invoice, - paymentUrl: null, - subscription: { - cancelAtPeriodEnd: sub.cancel_at_period_end, - canceledAt: sub.canceled_at != null ? new Date(sub.canceled_at * 1000) : null, - currentPeriodEndAt: periodEnd != null ? new Date(periodEnd * 1000) : null, - currentPeriodStartAt: periodStart != null ? new Date(periodStart * 1000) : null, - endedAt: sub.ended_at != null ? new Date(sub.ended_at * 1000) : null, - providerSubscriptionId: sub.id, - providerSubscriptionScheduleId: null, - status: sub.status, - }, + const firstItem = sub.items.data[0]; + const periodStart = firstItem?.current_period_start ?? null; + const periodEnd = firstItem?.current_period_end ?? null; + const latestInvoice = sub.latest_invoice; + const inv = + latestInvoice && typeof latestInvoice !== "string" + ? { + currency: latestInvoice.currency, + hostedUrl: latestInvoice.hosted_invoice_url ?? null, + periodEndAt: latestInvoice.period_end + ? new Date(latestInvoice.period_end * 1000) + : null, + periodStartAt: latestInvoice.period_start + ? new Date(latestInvoice.period_start * 1000) + : null, + providerInvoiceId: latestInvoice.id, + status: latestInvoice.status, + totalAmount: latestInvoice.total, + } + : null; + + return { + invoice: inv, + paymentUrl: null, + subscription: { + cancelAtPeriodEnd: sub.cancel_at_period_end, + canceledAt: sub.canceled_at != null ? new Date(sub.canceled_at * 1000) : null, + currentPeriodEndAt: periodEnd != null ? new Date(periodEnd * 1000) : null, + currentPeriodStartAt: periodStart != null ? new Date(periodStart * 1000) : null, + endedAt: sub.ended_at != null ? new Date(sub.ended_at * 1000) : null, + providerSubscriptionId: sub.id, + providerSubscriptionScheduleId: null, + status: sub.status, + }, + }; }; - }; + } // 4. Start webhook server BEFORE syncing products — product sync - // creates Stripe products which fires webhooks immediately + // creates provider products which fires webhooks immediately const webhookRequests: CapturedWebhookRequest[] = []; const server = startWebhookServer(paykit, webhookRequests); - // 5. Sync products to Stripe + // 5. Sync products to provider await syncProducts(ctx); return { paykit, database: ctx.database, ctx, - stripeClient, + harness, dbPath: dbUrl, server, webhookRequests, cleanup: async () => { const customerRows = await ctx.database.query.customer.findMany(); - const testClockIds = new Set(); + const providerCustomerIds: string[] = []; for (const row of customerRows) { - const providerMap = (row.provider ?? {}) as Record< - string, - { id: string; testClockId?: string } - >; - const testClockId = providerMap.stripe?.testClockId; - if (testClockId) { - testClockIds.add(testClockId); - } + const providerMap = (row.provider ?? {}) as Record; + const entry = providerMap[harness.id]; + if (entry?.id) providerCustomerIds.push(entry.id); } - for (const testClockId of testClockIds) { - await stripeClient.testHelpers.testClocks.del(testClockId).catch(() => {}); - } + await harness.cleanup({ providerCustomerIds }); // Wait for cleanup webhooks to arrive and be processed await new Promise((resolve) => setTimeout(resolve, 10_000)); - server.close(); + await new Promise((resolve) => server.close(() => resolve())); await pool.end(); // Drop the test database const cleanupPool = new Pool({ - connectionString: process.env.TEST_DATABASE_URL ?? "postgresql://localhost:5432/postgres", + connectionString: env.TEST_DATABASE_URL, }); await cleanupPool.query(`DROP DATABASE IF EXISTS "${dbName}"`).catch(() => {}); await cleanupPool.end(); @@ -245,33 +231,43 @@ export async function createTestPayKit(): Promise { } /** - * Creates a PayKit customer. In testing mode this also provisions a Stripe - * customer with a dedicated test clock. No payment method attached — first - * paid subscribe will go through checkout. + * Creates a PayKit customer. In testing mode this also provisions a provider + * customer (with a test clock for Stripe). No payment method attached. */ export async function createTestCustomer(input: { t: TestPayKit; customer: { id: string; email: string; name: string }; }): Promise<{ customerId: string; providerCustomerId: string }> { - await input.t.paykit.upsertCustomer(input.customer); + const suffix = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; + const uniqueId = `${input.customer.id}_${suffix}`; + const uniqueEmail = input.customer.email.replace("@", `+${suffix}@`); + + await input.t.paykit.upsertCustomer({ + ...input.customer, + id: uniqueId, + email: uniqueEmail, + upsertProviderCustomer: true, + }); - // Now the provider customer ID is stored in the customer's provider JSONB column const row = await input.t.database.query.customer.findFirst({ - where: eq(customer.id, input.customer.id), + where: eq(customer.id, uniqueId), }); const providerMap = (row?.provider ?? {}) as Record; - const providerCustomerId = providerMap.stripe?.id; + const providerCustomerId = providerMap[input.t.harness.id]?.id; if (!providerCustomerId) { - throw new Error(`No Stripe provider customer ID found for customer "${input.customer.id}"`); + throw new Error( + `No ${input.t.harness.id} provider customer ID found for customer "${uniqueId}"`, + ); } - return { customerId: input.customer.id, providerCustomerId }; + return { customerId: uniqueId, providerCustomerId }; } /** - * Creates a PayKit customer with a pre-attached payment method. - * Subscribe calls will go through the direct path (no checkout). + * Creates a PayKit customer ready for direct subscription (no checkout). + * For Stripe: attaches a test payment method. + * For providers without direct subscription support, this is equivalent to createTestCustomer. */ export async function createTestCustomerWithPM(input: { t: TestPayKit; @@ -279,31 +275,84 @@ export async function createTestCustomerWithPM(input: { }): Promise<{ customerId: string; providerCustomerId: string }> { const { customerId, providerCustomerId } = await createTestCustomer(input); - // Attach test payment method to Stripe customer - const pm = await input.t.stripeClient.paymentMethods.attach("pm_card_visa", { - customer: providerCustomerId, - }); - await input.t.stripeClient.customers.update(providerCustomerId, { - invoice_settings: { default_payment_method: pm.id }, - }); + await input.t.harness.setupCustomerForDirectSubscription(providerCustomerId); - // Sync payment method into PayKit DB - await syncPaymentMethodByProviderCustomer(input.t.ctx.database, { - paymentMethod: { - providerMethodId: pm.id, - type: pm.type, - last4: pm.card?.last4, - expiryMonth: pm.card?.exp_month, - expiryYear: pm.card?.exp_year, - isDefault: true, - }, - providerCustomerId, - providerId: input.t.ctx.provider.id, - }); + // For Stripe, sync the payment method into PayKit DB + if (input.t.harness.id === "stripe") { + const secretKey = env.E2E_STRIPE_SK!; + const stripeClient = new Stripe(secretKey); + const pm = await stripeClient.paymentMethods.list({ + customer: providerCustomerId, + type: "card", + limit: 1, + }); + const method = pm.data[0]; + if (method) { + await syncPaymentMethodByProviderCustomer(input.t.ctx.database, { + paymentMethod: { + providerMethodId: method.id, + type: method.type, + last4: method.card?.last4, + expiryMonth: method.card?.exp_month, + expiryYear: method.card?.exp_year, + isDefault: true, + }, + providerCustomerId, + providerId: input.t.ctx.provider.id, + }); + } + } return { customerId, providerCustomerId }; } +/** + * Subscribe a customer to a plan, handling checkout flow if the provider requires it. + * For providers with direct subscription (Stripe with PM): returns immediately. + * For providers requiring checkout (Polar): completes checkout via Playwright and waits for webhook. + */ +export async function subscribeCustomer(input: { + t: TestPayKit; + customerId: string; + planId: Parameters[0]["planId"]; +}): Promise { + const beforeSubscribe = new Date(); + + const result = await input.t.paykit.subscribe({ + customerId: input.customerId, + planId: input.planId, + successUrl: "https://example.com/success", + }); + + if (result.paymentUrl) { + // Checkout-based flow — automate checkout completion + await input.t.harness.completeCheckout(result.paymentUrl); + + // Wait for the subscription to become active via webhook + await waitForWebhook({ + database: input.t.database, + eventType: "subscription.updated", + after: beforeSubscribe, + timeout: 60_000, + }); + } +} + +export function requireCapability(capability: keyof ProviderCapabilities): void { + if (!harness.capabilities[capability]) { + throw new SkipTestError( + `Test requires "${capability}" but provider "${harness.id}" does not support it`, + ); + } +} + +class SkipTestError extends Error { + constructor(message: string) { + super(message); + this.name = "SkipTestError"; + } +} + export async function expectProduct(input: { database: PayKitDatabase; customerId: string; @@ -619,6 +668,7 @@ export async function advanceTestClock(input: { frozenTime: Date; t: TestPayKit; }): Promise { + requireCapability("testClocks"); await input.t.paykit.advanceTestClock({ customerId: input.customerId, frozenTime: input.frozenTime, diff --git a/e2e/smoke/subscribe/cancel-end-of-cycle.test.ts b/e2e/smoke/subscribe/cancel-end-of-cycle.test.ts index 6b964746..3745a781 100644 --- a/e2e/smoke/subscribe/cancel-end-of-cycle.test.ts +++ b/e2e/smoke/subscribe/cancel-end-of-cycle.test.ts @@ -11,127 +11,124 @@ import { expectNoScheduledPlanInGroup, expectProduct, expectSingleActivePlanInGroup, + harness, + subscribeCustomer, type TestPayKit, waitForWebhook, } from "../setup"; -describe("cancel-end-of-cycle: pro → free + clock advance", () => { - let t: TestPayKit; - let customerId: string; +describe.skipIf(!harness.capabilities.testClocks)( + "cancel-end-of-cycle: pro → free + clock advance", + () => { + let t: TestPayKit; + let customerId: string; - beforeAll(async () => { - t = await createTestPayKit(); - const customer = await createTestCustomerWithPM({ - t, - customer: { - id: "test_cancel_eoc", - email: "cancel-eoc@test.com", - name: "Cancel EOC Test", - }, - }); - customerId = customer.customerId; + beforeAll(async () => { + t = await createTestPayKit(); + const customer = await createTestCustomerWithPM({ + t, + customer: { + id: "test_cancel_eoc", + email: "cancel-eoc@test.com", + name: "Cancel EOC Test", + }, + }); + customerId = customer.customerId; - // Setup: subscribe to Pro, then schedule downgrade to Free - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + // Setup: subscribe to Pro, then schedule downgrade to Free + await subscribeCustomer({ t, customerId, planId: "pro" }); - await t.paykit.subscribe({ - customerId, - planId: "free", - successUrl: "https://example.com/success", + await subscribeCustomer({ t, customerId, planId: "free" }); }); - }); - afterAll(async () => { - await t?.cleanup(); - }); + afterAll(async () => { + await t?.cleanup(); + }); - it("advancing past period end activates the free plan", async () => { - try { - // Get period end to advance past - const subRows = await t.database - .select({ currentPeriodEndAt: subscription.currentPeriodEndAt }) - .from(subscription) - .where(eq(subscription.customerId, customerId)) - .orderBy(desc(subscription.updatedAt)) - .limit(1); - const periodEnd = new Date(subRows[0]!.currentPeriodEndAt as unknown as string); + it("advancing past period end activates the free plan", async () => { + try { + // Get period end to advance past + const subRows = await t.database + .select({ currentPeriodEndAt: subscription.currentPeriodEndAt }) + .from(subscription) + .where(eq(subscription.customerId, customerId)) + .orderBy(desc(subscription.updatedAt)) + .limit(1); + const periodEnd = new Date(subRows[0]!.currentPeriodEndAt as unknown as string); - // Advance clock 1 day past period end - const advanceTo = new Date(periodEnd.getTime() + 86_400_000); - const beforeAdvance = new Date(); - await advanceTestClock({ - t, - customerId, - frozenTime: advanceTo, - }); - await waitForWebhook({ - after: beforeAdvance, - database: t.database, - eventType: "subscription.deleted", - timeout: 30_000, - }); + // Advance clock 1 day past period end + const advanceTo = new Date(periodEnd.getTime() + 86_400_000); + const beforeAdvance = new Date(); + await advanceTestClock({ + t, + customerId, + frozenTime: advanceTo, + }); + await waitForWebhook({ + after: beforeAdvance, + database: t.database, + eventType: "subscription.deleted", + timeout: 30_000, + }); - // Poll until Free is active after the forwarded deletion event is processed - for (let i = 0; i < 60; i++) { - const rows = await t.database - .select({ status: subscription.status }) - .from(subscription) - .innerJoin(product, eq(product.internalId, subscription.productInternalId)) - .where( - and( - eq(subscription.customerId, customerId), - eq(product.id, "free"), - eq(subscription.status, "active"), - ), - ); - if (rows.length > 0) break; - if (i === 59) throw new Error("Free plan never activated after clock advance"); - await new Promise((resolve) => setTimeout(resolve, 2000)); - } + // Poll until Free is active after the forwarded deletion event is processed + for (let i = 0; i < 60; i++) { + const rows = await t.database + .select({ status: subscription.status }) + .from(subscription) + .innerJoin(product, eq(product.internalId, subscription.productInternalId)) + .where( + and( + eq(subscription.customerId, customerId), + eq(product.id, "free"), + eq(subscription.status, "active"), + ), + ); + if (rows.length > 0) break; + if (i === 59) throw new Error("Free plan never activated after clock advance"); + await new Promise((resolve) => setTimeout(resolve, 2000)); + } - // Pro is canceled/ended - await expectProduct({ - database: t.database, - customerId, - planId: "pro", - expected: { canceled: true, status: "canceled" }, - }); + // Pro is canceled/ended + await expectProduct({ + database: t.database, + customerId, + planId: "pro", + expected: { canceled: true, status: "canceled" }, + }); - // Free is active with no period end (no billing cycle) - await expectProduct({ - database: t.database, - customerId, - planId: "free", - expected: { - status: "active", - hasPeriodEnd: false, - }, - }); - await expectSingleActivePlanInGroup({ - database: t.database, - customerId, - group: "base", - planId: "free", - }); - await expectNoScheduledPlanInGroup({ - database: t.database, - customerId, - group: "base", - }); - await expectExactMeteredBalance({ - paykit: t.paykit, - customerId, - featureId: "messages", - limit: 100, - remaining: 100, - }); - } catch (error) { - await dumpStateOnFailure(t.database, t.dbPath); - throw error; - } - }); -}); + // Free is active with no period end (no billing cycle) + await expectProduct({ + database: t.database, + customerId, + planId: "free", + expected: { + status: "active", + hasPeriodEnd: false, + }, + }); + await expectSingleActivePlanInGroup({ + database: t.database, + customerId, + group: "base", + planId: "free", + }); + await expectNoScheduledPlanInGroup({ + database: t.database, + customerId, + group: "base", + }); + await expectExactMeteredBalance({ + paykit: t.paykit, + customerId, + featureId: "messages", + limit: 100, + remaining: 100, + }); + } catch (error) { + await dumpStateOnFailure(t.database, t.dbPath); + throw error; + } + }); + }, +); diff --git a/e2e/smoke/subscribe/cancel-resume.test.ts b/e2e/smoke/subscribe/cancel-resume.test.ts index 3fa907b5..63b6c92a 100644 --- a/e2e/smoke/subscribe/cancel-resume.test.ts +++ b/e2e/smoke/subscribe/cancel-resume.test.ts @@ -9,6 +9,7 @@ import { expectSingleActivePlanInGroup, expectSingleScheduledPlanInGroup, expectSubscription, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -29,17 +30,9 @@ describe("cancel-resume: pro → free → pro (resume)", () => { customerId = customer.customerId; // Setup: subscribe to Pro, then schedule downgrade to Free - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); - await t.paykit.subscribe({ - customerId, - planId: "free", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "free" }); }); afterAll(async () => { @@ -72,11 +65,7 @@ describe("cancel-resume: pro → free → pro (resume)", () => { }); // Action: resume Pro - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); // Pro is active and no longer canceled await expectProduct({ diff --git a/e2e/smoke/subscribe/downgrade-scheduled.test.ts b/e2e/smoke/subscribe/downgrade-scheduled.test.ts index 3d25f803..a1c12b2f 100644 --- a/e2e/smoke/subscribe/downgrade-scheduled.test.ts +++ b/e2e/smoke/subscribe/downgrade-scheduled.test.ts @@ -7,6 +7,7 @@ import { expectProduct, expectSingleActivePlanInGroup, expectSingleScheduledPlanInGroup, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -27,17 +28,9 @@ describe("downgrade-scheduled: ultra → pro", () => { customerId = customer.customerId; // Setup: subscribe to Pro then upgrade to Ultra - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); - await t.paykit.subscribe({ - customerId, - planId: "ultra", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "ultra" }); }); afterAll(async () => { @@ -46,11 +39,7 @@ describe("downgrade-scheduled: ultra → pro", () => { it("downgrading to a lower tier schedules the change at period end", async () => { try { - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); // Ultra is still active but marked as canceled await expectProduct({ diff --git a/e2e/smoke/subscribe/downgrade-to-free.test.ts b/e2e/smoke/subscribe/downgrade-to-free.test.ts index 1fae9353..17a9faf1 100644 --- a/e2e/smoke/subscribe/downgrade-to-free.test.ts +++ b/e2e/smoke/subscribe/downgrade-to-free.test.ts @@ -8,6 +8,7 @@ import { expectProduct, expectSingleActivePlanInGroup, expectSingleScheduledPlanInGroup, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -28,11 +29,7 @@ describe("downgrade-to-free: pro → free", () => { customerId = customer.customerId; // Setup: subscribe to Pro - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); }); afterAll(async () => { @@ -41,11 +38,7 @@ describe("downgrade-to-free: pro → free", () => { it("downgrading to free schedules cancellation at period end", async () => { try { - await t.paykit.subscribe({ - customerId, - planId: "free", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "free" }); // Pro is still active but canceled await expectProduct({ diff --git a/e2e/smoke/subscribe/renewal.test.ts b/e2e/smoke/subscribe/renewal.test.ts index 83b55c4c..adb33a62 100644 --- a/e2e/smoke/subscribe/renewal.test.ts +++ b/e2e/smoke/subscribe/renewal.test.ts @@ -10,125 +10,126 @@ import { expectExactMeteredBalance, expectProduct, expectSingleActivePlanInGroup, + harness, + subscribeCustomer, type TestPayKit, waitForWebhook, } from "../setup"; -describe("renewal: pro subscription renews after 1 month", () => { - let t: TestPayKit; - let customerId: string; +describe.skipIf(!harness.capabilities.testClocks)( + "renewal: pro subscription renews after 1 month", + () => { + let t: TestPayKit; + let customerId: string; - beforeAll(async () => { - t = await createTestPayKit(); - const customer = await createTestCustomerWithPM({ - t, - customer: { - id: "test_renewal", - email: "renewal@test.com", - name: "Renewal Test", - }, - }); - customerId = customer.customerId; + beforeAll(async () => { + t = await createTestPayKit(); + const customer = await createTestCustomerWithPM({ + t, + customer: { + id: "test_renewal", + email: "renewal@test.com", + name: "Renewal Test", + }, + }); + customerId = customer.customerId; - // Setup: subscribe to Pro - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", + // Setup: subscribe to Pro + await subscribeCustomer({ t, customerId, planId: "pro" }); }); - }); - afterAll(async () => { - await t?.cleanup(); - }); - - it("advancing clock 1 month rolls period dates forward and resets usage", async () => { - try { - const usage = await t.paykit.report({ - customerId, - featureId: "messages", - amount: 37, - }); - expect(usage.success).toBe(true); - await expectExactMeteredBalance({ - paykit: t.paykit, - customerId, - featureId: "messages", - limit: 500, - remaining: 463, - }); + afterAll(async () => { + await t?.cleanup(); + }); - // Record current period end - const subRows = await t.database - .select({ currentPeriodEndAt: subscription.currentPeriodEndAt }) - .from(subscription) - .where(eq(subscription.customerId, customerId)) - .orderBy(desc(subscription.updatedAt)) - .limit(1); - const periodEnd = new Date(subRows[0]!.currentPeriodEndAt as unknown as string); + it("advancing clock 1 month rolls period dates forward and resets usage", async () => { + try { + const usage = await t.paykit.report({ + customerId, + featureId: "messages", + amount: 37, + }); + expect(usage.success).toBe(true); + await expectExactMeteredBalance({ + paykit: t.paykit, + customerId, + featureId: "messages", + limit: 500, + remaining: 463, + }); - // Advance clock 1 day past period end - const advanceTo = new Date(periodEnd.getTime() + 86_400_000); - const beforeAdvance = new Date(); - await advanceTestClock({ - t, - customerId, - frozenTime: advanceTo, - }); - await waitForWebhook({ - after: beforeAdvance, - database: t.database, - eventType: "subscription.updated", - timeout: 30_000, - }); - - // Poll until period dates change after the forwarded renewal event is processed - let newPeriodEnd = periodEnd; - for (let i = 0; i < 60; i++) { - const rows = await t.database + // Record current period end + const subRows = await t.database .select({ currentPeriodEndAt: subscription.currentPeriodEndAt }) .from(subscription) - .where(and(eq(subscription.customerId, customerId), eq(subscription.status, "active"))) + .where(eq(subscription.customerId, customerId)) .orderBy(desc(subscription.updatedAt)) .limit(1); - const row = rows[0]; - if (row?.currentPeriodEndAt) { - const end = new Date(row.currentPeriodEndAt as unknown as string); - if (end.getTime() > periodEnd.getTime()) { - newPeriodEnd = end; - break; + const periodEnd = new Date(subRows[0]!.currentPeriodEndAt as unknown as string); + + // Advance clock 1 day past period end + const advanceTo = new Date(periodEnd.getTime() + 86_400_000); + const beforeAdvance = new Date(); + await advanceTestClock({ + t, + customerId, + frozenTime: advanceTo, + }); + await waitForWebhook({ + after: beforeAdvance, + database: t.database, + eventType: "subscription.updated", + timeout: 30_000, + }); + + // Poll until period dates change after the forwarded renewal event is processed + let newPeriodEnd = periodEnd; + for (let i = 0; i < 60; i++) { + const rows = await t.database + .select({ currentPeriodEndAt: subscription.currentPeriodEndAt }) + .from(subscription) + .where(and(eq(subscription.customerId, customerId), eq(subscription.status, "active"))) + .orderBy(desc(subscription.updatedAt)) + .limit(1); + const row = rows[0]; + if (row?.currentPeriodEndAt) { + const end = new Date(row.currentPeriodEndAt as unknown as string); + if (end.getTime() > periodEnd.getTime()) { + newPeriodEnd = end; + break; + } } + if (i === 59) throw new Error("Period dates never rolled forward after clock advance"); + await new Promise((resolve) => setTimeout(resolve, 2000)); } - if (i === 59) throw new Error("Period dates never rolled forward after clock advance"); - await new Promise((resolve) => setTimeout(resolve, 2000)); - } - // Period end moved forward - expect(newPeriodEnd.getTime()).toBeGreaterThan(periodEnd.getTime()); + // Period end moved forward + expect(newPeriodEnd.getTime()).toBeGreaterThan(periodEnd.getTime()); - // Pro is still active - await expectProduct({ - database: t.database, - customerId, - planId: "pro", - expected: { status: "active" }, - }); - await expectSingleActivePlanInGroup({ - database: t.database, - customerId, - group: "base", - planId: "pro", - }); - await expectExactMeteredBalance({ - paykit: t.paykit, - customerId, - featureId: "messages", - limit: 500, - remaining: 500, - }); - } catch (error) { - await dumpStateOnFailure(t.database, t.dbPath); - throw error; - } - }); -}); + // Pro is still active + await expectProduct({ + database: t.database, + customerId, + planId: "pro", + expected: { status: "active" }, + }); + await expectSingleActivePlanInGroup({ + database: t.database, + customerId, + group: "base", + planId: "pro", + }); + await expectExactMeteredBalance({ + paykit: t.paykit, + customerId, + featureId: "messages", + limit: 500, + remaining: 500, + }); + } catch (error) { + await dumpStateOnFailure(t.database, t.dbPath); + throw error; + } + }); + }, +); diff --git a/e2e/smoke/subscribe/same-plan-noop.test.ts b/e2e/smoke/subscribe/same-plan-noop.test.ts index bed90c72..bd557450 100644 --- a/e2e/smoke/subscribe/same-plan-noop.test.ts +++ b/e2e/smoke/subscribe/same-plan-noop.test.ts @@ -9,6 +9,7 @@ import { expectExactMeteredBalance, expectNoScheduledPlanInGroup, expectSingleActivePlanInGroup, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -29,11 +30,7 @@ describe("same-plan-noop: pro → pro", () => { customerId = customer.customerId; // Setup: subscribe to Pro - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); }); afterAll(async () => { @@ -64,11 +61,7 @@ describe("same-plan-noop: pro → pro", () => { const invoiceCountBefore = invoicesBeforeRows[0]?.count ?? 0; // Action: subscribe to same plan - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); // Same product ID (no new row created) const afterRows = await t.database diff --git a/e2e/smoke/subscribe/subscribe-paid.test.ts b/e2e/smoke/subscribe/subscribe-paid.test.ts index 2a21fff6..3a310dbf 100644 --- a/e2e/smoke/subscribe/subscribe-paid.test.ts +++ b/e2e/smoke/subscribe/subscribe-paid.test.ts @@ -5,11 +5,11 @@ import { createTestPayKit, dumpStateOnFailure, expectExactMeteredBalance, - expectInvoiceCount, expectNoScheduledPlanInGroup, expectProduct, expectSingleActivePlanInGroup, expectSubscription, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -36,16 +36,7 @@ describe("subscribe-paid: free → pro", () => { it("subscribing to a paid plan from free creates an active subscription", async () => { try { - const result = await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); - - // Direct path (has payment method, no checkout) - if (result.paymentUrl != null) { - throw new Error("Expected direct subscription, got checkout URL"); - } + await subscribeCustomer({ t, customerId, planId: "pro" }); // Pro is active with period dates await expectProduct({ @@ -90,13 +81,6 @@ describe("subscribe-paid: free → pro", () => { customerId, expected: { status: "active" }, }); - - // At least 1 invoice - await expectInvoiceCount({ - database: t.database, - customerId, - expectedAtLeast: 1, - }); } catch (error) { await dumpStateOnFailure(t.database, t.dbPath); throw error; diff --git a/e2e/smoke/subscribe/upgrade-immediate.test.ts b/e2e/smoke/subscribe/upgrade-immediate.test.ts index 32f6ea39..c6345682 100644 --- a/e2e/smoke/subscribe/upgrade-immediate.test.ts +++ b/e2e/smoke/subscribe/upgrade-immediate.test.ts @@ -8,6 +8,7 @@ import { expectNoScheduledPlanInGroup, expectProduct, expectSingleActivePlanInGroup, + subscribeCustomer, type TestPayKit, } from "../setup"; @@ -28,11 +29,7 @@ describe("upgrade-immediate: pro → ultra", () => { customerId = customer.customerId; // Setup: subscribe to Pro first - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); }); afterAll(async () => { @@ -41,11 +38,7 @@ describe("upgrade-immediate: pro → ultra", () => { it("upgrading to a higher tier activates it immediately and ends the old plan", async () => { try { - await t.paykit.subscribe({ - customerId, - planId: "ultra", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "ultra" }); // Ultra is active with period dates await expectProduct({ diff --git a/e2e/smoke/webhook/duplicate-webhook.test.ts b/e2e/smoke/webhook/duplicate-webhook.test.ts index b9ec3535..d919ee6c 100644 --- a/e2e/smoke/webhook/duplicate-webhook.test.ts +++ b/e2e/smoke/webhook/duplicate-webhook.test.ts @@ -11,6 +11,7 @@ import { expectProduct, expectSingleActivePlanInGroup, replayWebhookRequest, + subscribeCustomer, type TestPayKit, waitForForwardedWebhookRequest, waitForWebhook, @@ -41,11 +42,7 @@ describe("duplicate-webhook: same event delivered twice", () => { try { const beforeSubscribe = new Date(); - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + await subscribeCustomer({ t, customerId, planId: "pro" }); const subscriptionWebhook = await waitForWebhook({ after: beforeSubscribe, diff --git a/e2e/smoke/webhook/subscription-deleted.test.ts b/e2e/smoke/webhook/subscription-deleted.test.ts index 6725d486..b6039984 100644 --- a/e2e/smoke/webhook/subscription-deleted.test.ts +++ b/e2e/smoke/webhook/subscription-deleted.test.ts @@ -1,96 +1,100 @@ import { desc, eq } from "drizzle-orm"; +import { default as Stripe } from "stripe"; import { afterAll, beforeAll, describe, it } from "vitest"; import { subscription } from "../../../packages/paykit/src/database/schema"; +import { env } from "../../env"; import { createTestCustomerWithPM, createTestPayKit, dumpStateOnFailure, expectProduct, expectSingleActivePlanInGroup, + harness, + subscribeCustomer, type TestPayKit, waitForWebhook, } from "../setup"; -describe("subscription-deleted: Stripe cancels subscription directly", () => { - let t: TestPayKit; - let customerId: string; - let providerSubscriptionId: string; +describe.skipIf(harness.id !== "stripe")( + "subscription-deleted: Stripe cancels subscription directly", + () => { + let t: TestPayKit; + let customerId: string; + let providerSubscriptionId: string; + const stripeClient = new Stripe(env.E2E_STRIPE_SK!); - beforeAll(async () => { - t = await createTestPayKit(); - const customer = await createTestCustomerWithPM({ - t, - customer: { - id: "test_sub_deleted", - email: "sub-deleted@test.com", - name: "Subscription Deleted Test", - }, - }); - customerId = customer.customerId; + beforeAll(async () => { + t = await createTestPayKit(); + const customer = await createTestCustomerWithPM({ + t, + customer: { + id: "test_sub_deleted", + email: "sub-deleted@test.com", + name: "Subscription Deleted Test", + }, + }); + customerId = customer.customerId; - // Setup: subscribe to Pro - await t.paykit.subscribe({ - customerId, - planId: "pro", - successUrl: "https://example.com/success", - }); + // Setup: subscribe to Pro + await subscribeCustomer({ t, customerId, planId: "pro" }); - // Get provider subscription ID from provider_data JSONB - const subRows = await t.database - .select({ providerData: subscription.providerData }) - .from(subscription) - .where(eq(subscription.customerId, customerId)) - .orderBy(desc(subscription.updatedAt)) - .limit(1); - const providerData = subRows[0]?.providerData as { subscriptionId: string } | null; - providerSubscriptionId = providerData!.subscriptionId; - }); + // Get provider subscription ID from provider_data JSONB + const subRows = await t.database + .select({ providerData: subscription.providerData }) + .from(subscription) + .where(eq(subscription.customerId, customerId)) + .orderBy(desc(subscription.updatedAt)) + .limit(1); + const providerData = subRows[0]?.providerData as { subscriptionId: string } | null; + providerSubscriptionId = providerData!.subscriptionId; + }); - afterAll(async () => { - await t?.cleanup(); - }); + afterAll(async () => { + await t?.cleanup(); + }); - it("when Stripe cancels a subscription directly, PayKit ends the product and activates free", async () => { - try { - const beforeCancel = new Date(); + it("when Stripe cancels a subscription directly, PayKit ends the product and activates free", async () => { + try { + const beforeCancel = new Date(); - // Cancel directly via Stripe API (simulates Stripe dashboard cancellation) - await t.stripeClient.subscriptions.cancel(providerSubscriptionId); - await waitForWebhook({ - after: beforeCancel, - database: t.database, - eventType: "subscription.deleted", - timeout: 30_000, - }); + // Cancel directly via Stripe API (simulates Stripe dashboard cancellation) + await stripeClient.subscriptions.cancel(providerSubscriptionId); + await waitForWebhook({ + after: beforeCancel, + database: t.database, + eventType: "subscription.deleted", + timeout: 30_000, + }); - // Pro should be canceled/ended - await expectProduct({ - database: t.database, - customerId, - planId: "pro", - expected: { canceled: true, status: "canceled" }, - }); + // Pro should be canceled/ended + await expectProduct({ + database: t.database, + customerId, + planId: "pro", + expected: { canceled: true, status: "canceled" }, + }); - // Free should be active (default plan activated) - await expectProduct({ - database: t.database, - customerId, - planId: "free", - expected: { - status: "active", - hasPeriodEnd: false, - }, - }); - await expectSingleActivePlanInGroup({ - database: t.database, - customerId, - group: "base", - planId: "free", - }); - } catch (error) { - await dumpStateOnFailure(t.database, t.dbPath); - throw error; - } - }); -}); + // Free should be active (default plan activated) + await expectProduct({ + database: t.database, + customerId, + planId: "free", + expected: { + status: "active", + hasPeriodEnd: false, + }, + }); + await expectSingleActivePlanInGroup({ + database: t.database, + customerId, + group: "base", + planId: "free", + }); + } catch (error) { + await dumpStateOnFailure(t.database, t.dbPath); + throw error; + } + }); + }, +); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 703c80e5..0be75091 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -82,10 +82,10 @@ importers: version: 11.12.0(typescript@5.9.3) autumn-js: specifier: ^1.2.2 - version: 1.2.2(better-auth@1.6.2(8fff6a967c03a30b47656e909f3a8e44))(better-call@1.3.5(zod@4.3.6))(express@5.2.1)(hono@4.12.3)(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5) + version: 1.2.2(better-auth@1.6.2(140411336a30ff0790dc8607571a86e3))(better-call@1.3.5(zod@4.3.6))(express@5.2.1)(hono@4.12.3)(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5) better-auth: specifier: ^1.6.2 - version: 1.6.2(8fff6a967c03a30b47656e909f3a8e44) + version: 1.6.2(140411336a30ff0790dc8607571a86e3) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -204,9 +204,15 @@ importers: e2e: devDependencies: + '@paykitjs/polar': + specifier: workspace:* + version: link:../packages/polar '@paykitjs/stripe': specifier: workspace:* version: link:../packages/stripe + '@t3-oss/env-core': + specifier: ^0.12.0 + version: 0.12.0(typescript@5.9.3)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6) '@types/pg': specifier: ^8.18.0 version: 8.18.0 @@ -222,12 +228,18 @@ importers: pg: specifier: ^8.20.0 version: 8.20.0 + playwright: + specifier: ^1.52.0 + version: 1.59.1 stripe: specifier: ^19.1.0 version: 19.3.1(@types/node@25.3.0) vitest: specifier: ^4.0.18 version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.3.0)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.2) + zod: + specifier: ^4.0.0 + version: 4.3.6 landing: dependencies: @@ -248,7 +260,7 @@ importers: version: 2.0.13 '@vercel/analytics': specifier: ^1.6.1 - version: 1.6.1(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5) + version: 1.6.1(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -269,16 +281,16 @@ importers: version: 12.34.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5) fumadocs-core: specifier: ^16.7.11 - version: 16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76) + version: 16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76) fumadocs-mdx: specifier: ^14.2.11 - version: 14.2.11(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5) + version: 14.2.11(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5) fumadocs-ui: specifier: ^16.7.11 - version: 16.7.11(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(shiki@4.0.0)(tailwindcss@4.2.1) + version: 16.7.11(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(shiki@4.0.0)(tailwindcss@4.2.1) geist: specifier: ^1.3.1 - version: 1.7.0(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) + version: 1.7.0(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) input-otp: specifier: ^1.4.2 version: 1.4.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -3535,7 +3547,7 @@ packages: resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} '@t3-oss/env-core@0.12.0': - resolution: {integrity: sha512-lOPj8d9nJJTt81mMuN9GMk8x5veOt7q9m11OSnCBJhwp1QrL/qR+M8Y467ULBSm9SunosryWNbmQQbgoiMgcdw==} + resolution: {integrity: sha512-lOPj8d9nJJTt81mMuN9GMk8x5veOt7q9m11OSnCBJhwp1QrL/qR+M8Y467ULBSm9SunosryWNbmQQbgoiMgcdw==, tarball: https://registry.npmjs.org/@t3-oss/env-core/-/env-core-0.12.0.tgz} peerDependencies: typescript: '>=5.0.0' valibot: ^1.0.0-beta.7 || ^1.0.0 @@ -5174,6 +5186,11 @@ packages: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, tarball: https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -6672,6 +6689,16 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + playwright-core@1.59.1: + resolution: {integrity: sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==, tarball: https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz} + engines: {node: '>=18'} + hasBin: true + + playwright@1.59.1: + resolution: {integrity: sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==, tarball: https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz} + engines: {node: '>=18'} + hasBin: true + postal-mime@2.7.3: resolution: {integrity: sha512-MjhXadAJaWgYzevi46+3kLak8y6gbg0ku14O1gO/LNOuay8dO+1PtcSGvAdgDR0DoIsSaiIA8y/Ddw6MnrO0Tw==} @@ -9561,7 +9588,7 @@ snapshots: '@polar-sh/sdk@0.47.0': dependencies: standardwebhooks: 1.0.0 - zod: 3.25.76 + zod: 4.3.6 '@posthog/core@1.24.3': dependencies: @@ -11080,7 +11107,7 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vercel/analytics@1.6.1(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)': + '@vercel/analytics@1.6.1(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)': optionalDependencies: next: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) react: 19.2.5 @@ -11274,13 +11301,13 @@ snapshots: auto-bind@5.0.1: {} - autumn-js@1.2.2(better-auth@1.6.2(8fff6a967c03a30b47656e909f3a8e44))(better-call@1.3.5(zod@4.3.6))(express@5.2.1)(hono@4.12.3)(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5): + autumn-js@1.2.2(better-auth@1.6.2(140411336a30ff0790dc8607571a86e3))(better-call@1.3.5(zod@4.3.6))(express@5.2.1)(hono@4.12.3)(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5): dependencies: query-string: 9.3.1 rou3: 0.6.3 zod: 4.3.6 optionalDependencies: - better-auth: 1.6.2(8fff6a967c03a30b47656e909f3a8e44) + better-auth: 1.6.2(140411336a30ff0790dc8607571a86e3) better-call: 1.3.5(zod@4.3.6) express: 5.2.1 hono: 4.12.3 @@ -11304,7 +11331,7 @@ snapshots: baseline-browser-mapping@2.10.0: {} - better-auth@1.6.2(8fff6a967c03a30b47656e909f3a8e44): + better-auth@1.6.2(140411336a30ff0790dc8607571a86e3): dependencies: '@better-auth/core': 1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/drizzle-adapter': 1.6.2(@better-auth/core@1.6.2(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.5(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.4.0)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.18.0)(gel@2.2.0)(kysely@0.28.14)(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3))) @@ -12384,6 +12411,9 @@ snapshots: dependencies: minipass: 3.3.6 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -12400,7 +12430,7 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76): + fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76): dependencies: '@formatjs/intl-localematcher': 0.8.2 '@orama/orama': 3.1.18 @@ -12440,14 +12470,14 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-mdx@14.2.11(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5): + fumadocs-mdx@14.2.11(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.1.0 chokidar: 5.0.0 esbuild: 0.27.3 estree-util-value-to-estree: 3.5.0 - fumadocs-core: 16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76) + fumadocs-core: 16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76) js-yaml: 4.1.1 mdast-util-mdx: 3.0.0 mdast-util-to-markdown: 2.1.2 @@ -12469,7 +12499,7 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-ui@16.7.11(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(shiki@4.0.0)(tailwindcss@4.2.1): + fumadocs-ui@16.7.11(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1)(@types/mdx@2.0.13)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(shiki@4.0.0)(tailwindcss@4.2.1): dependencies: '@fumadocs/tailwind': 0.0.3(tailwindcss@4.2.1) '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -12484,7 +12514,7 @@ snapshots: '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) class-variance-authority: 0.7.1 fuma-cli: 0.0.3(@emnapi/core@1.8.1)(@emnapi/runtime@1.8.1) - fumadocs-core: 16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76) + fumadocs-core: 16.7.11(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.166.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.575.0(react@19.2.5))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@3.25.76) lucide-react: 1.8.0(react@19.2.5) motion: 12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) next-themes: 0.4.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -12512,7 +12542,7 @@ snapshots: fuzzysort@3.1.0: {} - geist@1.7.0(next@16.2.3(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)): + geist@1.7.0(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)): dependencies: next: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -14343,6 +14373,14 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 + playwright-core@1.59.1: {} + + playwright@1.59.1: + dependencies: + playwright-core: 1.59.1 + optionalDependencies: + fsevents: 2.3.2 + postal-mime@2.7.3: {} postcss-selector-parser@7.1.1: From 41927b17cc454ba86a9d3b2f0c7c4f839be86dbc Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 22 Apr 2026 19:28:52 +0400 Subject: [PATCH 10/13] chore: release v0.0.4-canary.0 --- packages/dash/package.json | 2 +- packages/paykit/package.json | 2 +- packages/polar/package.json | 2 +- packages/stripe/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/dash/package.json b/packages/dash/package.json index c796c5dd..3f31bdad 100644 --- a/packages/dash/package.json +++ b/packages/dash/package.json @@ -1,6 +1,6 @@ { "name": "@paykitjs/dash", - "version": "0.0.3", + "version": "0.0.4-canary.0", "private": true, "description": "Embedded dashboard plugin for PayKit", "license": "MIT", diff --git a/packages/paykit/package.json b/packages/paykit/package.json index 7d2236b0..1136dd72 100644 --- a/packages/paykit/package.json +++ b/packages/paykit/package.json @@ -1,6 +1,6 @@ { "name": "paykitjs", - "version": "0.0.3", + "version": "0.0.4-canary.0", "description": "TypeScript-first payments orchestration framework for modern SaaS", "keywords": [ "creem", diff --git a/packages/polar/package.json b/packages/polar/package.json index edb5614e..1d94e781 100644 --- a/packages/polar/package.json +++ b/packages/polar/package.json @@ -1,6 +1,6 @@ { "name": "@paykitjs/polar", - "version": "0.0.1", + "version": "0.0.4-canary.0", "description": "Polar provider adapter for PayKit", "license": "MIT", "repository": { diff --git a/packages/stripe/package.json b/packages/stripe/package.json index 470f1209..ab5309df 100644 --- a/packages/stripe/package.json +++ b/packages/stripe/package.json @@ -1,6 +1,6 @@ { "name": "@paykitjs/stripe", - "version": "0.0.3", + "version": "0.0.4-canary.0", "description": "Stripe provider adapter for PayKit", "license": "MIT", "repository": { From 53cea74fd70701a417ff7a063862d51f543d9dbb Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 22 Apr 2026 20:09:30 +0400 Subject: [PATCH 11/13] fix: address code review findings across core, polar, demo, and e2e - fix(cli): check trialing/past_due statuses in provider preflight - fix(cli): show preflight error message instead of suggesting push - fix(core): validate provider syncProducts result completeness - fix(polar): require non-empty email, narrow catch to SDKValidationError - fix(demo): make Stripe env vars optional, use provider-neutral UI copy - fix(e2e): remove dead SkipTestError, exhaustive switch, safer assertions - fix(merge): correct getProductByProviderData import after merge - chore(demo): fix zod import style, split combined type/value import --- .../src/app/_components/subscribe-panel.tsx | 4 ++-- apps/demo/src/env.js | 6 ++--- .../src/server/api/routers/paykit-route.ts | 3 ++- e2e/smoke/harness/index.ts | 6 +++-- e2e/smoke/harness/polar.ts | 2 +- e2e/smoke/setup.ts | 18 +-------------- .../webhook/subscription-deleted.test.ts | 5 ++++- packages/paykit/src/cli/commands/status.ts | 18 +++++++++------ packages/paykit/src/cli/utils/shared.ts | 4 ++-- .../src/product/product-sync.service.ts | 22 ++++++++++++++----- .../src/subscription/subscription.service.ts | 2 +- packages/polar/src/polar-provider.ts | 18 +++++++++++---- 12 files changed, 61 insertions(+), 47 deletions(-) diff --git a/apps/demo/src/app/_components/subscribe-panel.tsx b/apps/demo/src/app/_components/subscribe-panel.tsx index 6cf6dcb0..b1b8e341 100644 --- a/apps/demo/src/app/_components/subscribe-panel.tsx +++ b/apps/demo/src/app/_components/subscribe-panel.tsx @@ -170,7 +170,7 @@ function TestClockPanel() { {testClock.data ? {testClock.data.status} : null} - Advance the logged-in customer through Stripe billing cycles without leaving the demo. + Advance the logged-in customer through billing cycles without leaving the demo. @@ -191,7 +191,7 @@ function TestClockPanel() { {formatDateTime(testClock.data.frozenTime)} - Stripe time + Test clock
{actions.map((action) => ( diff --git a/apps/demo/src/env.js b/apps/demo/src/env.js index cb1fd282..4b6bae30 100644 --- a/apps/demo/src/env.js +++ b/apps/demo/src/env.js @@ -1,5 +1,5 @@ import { createEnv } from "@t3-oss/env-nextjs"; -import { z } from "zod"; +import * as z from "zod"; export const env = createEnv({ /** @@ -10,8 +10,8 @@ export const env = createEnv({ APP_URL: z.string().url(), DATABASE_URL: z.string().min(1), NODE_ENV: z.enum(["development", "test", "production"]).default("development"), - STRIPE_SECRET_KEY: z.string().min(1), - STRIPE_WEBHOOK_SECRET: z.string().min(1), + STRIPE_SECRET_KEY: z.string().min(1).optional(), + STRIPE_WEBHOOK_SECRET: z.string().min(1).optional(), POLAR_ACCESS_TOKEN: z.string().min(1), POLAR_WEBHOOK_SECRET: z.string().min(1), BETTER_AUTH_SECRET: z.string().min(1), diff --git a/apps/demo/src/server/api/routers/paykit-route.ts b/apps/demo/src/server/api/routers/paykit-route.ts index d335c607..c0615765 100644 --- a/apps/demo/src/server/api/routers/paykit-route.ts +++ b/apps/demo/src/server/api/routers/paykit-route.ts @@ -1,7 +1,8 @@ import { TRPCError } from "@trpc/server"; import { z } from "zod"; -import { paykit, type PayKit } from "@/lib/paykit"; +import { paykit } from "@/lib/paykit"; +import type { PayKit } from "@/lib/paykit"; import { createTRPCRouter, publicProcedure } from "@/server/api/trpc"; import { auth } from "@/server/auth"; diff --git a/e2e/smoke/harness/index.ts b/e2e/smoke/harness/index.ts index 3792b3b9..806313a4 100644 --- a/e2e/smoke/harness/index.ts +++ b/e2e/smoke/harness/index.ts @@ -13,7 +13,9 @@ export function loadHarness(): ProviderHarness { return createStripeHarness(); case "polar": return createPolarHarness(); - default: - throw new Error(`Unknown provider: ${provider}. Supported: stripe, polar`); + default: { + const _exhaustive: never = provider; + throw new Error(`Unknown provider: ${String(_exhaustive)}`); + } } } diff --git a/e2e/smoke/harness/polar.ts b/e2e/smoke/harness/polar.ts index af97ebf0..ceb3f483 100644 --- a/e2e/smoke/harness/polar.ts +++ b/e2e/smoke/harness/polar.ts @@ -19,7 +19,7 @@ export function createPolarHarness(): ProviderHarness { }, createProviderConfig() { - return polar({ accessToken: accessToken!, webhookSecret: webhookSecret!, server: "sandbox" }); + return polar({ accessToken, webhookSecret, server: "sandbox" }); }, async setupCustomerForDirectSubscription(_providerCustomerId: string) { diff --git a/e2e/smoke/setup.ts b/e2e/smoke/setup.ts index 7c044859..37d2da79 100644 --- a/e2e/smoke/setup.ts +++ b/e2e/smoke/setup.ts @@ -19,7 +19,7 @@ import { syncPaymentMethodByProviderCustomer } from "../../packages/paykit/src/p import { syncProducts } from "../../packages/paykit/src/product/product-sync.service"; import { env } from "../env"; import { loadHarness } from "./harness/index"; -import type { ProviderCapabilities, ProviderHarness } from "./harness/types"; +import type { ProviderHarness } from "./harness/types"; const WEBHOOK_PORT = 4567; @@ -338,21 +338,6 @@ export async function subscribeCustomer(input: { } } -export function requireCapability(capability: keyof ProviderCapabilities): void { - if (!harness.capabilities[capability]) { - throw new SkipTestError( - `Test requires "${capability}" but provider "${harness.id}" does not support it`, - ); - } -} - -class SkipTestError extends Error { - constructor(message: string) { - super(message); - this.name = "SkipTestError"; - } -} - export async function expectProduct(input: { database: PayKitDatabase; customerId: string; @@ -668,7 +653,6 @@ export async function advanceTestClock(input: { frozenTime: Date; t: TestPayKit; }): Promise { - requireCapability("testClocks"); await input.t.paykit.advanceTestClock({ customerId: input.customerId, frozenTime: input.frozenTime, diff --git a/e2e/smoke/webhook/subscription-deleted.test.ts b/e2e/smoke/webhook/subscription-deleted.test.ts index b6039984..b78b9a61 100644 --- a/e2e/smoke/webhook/subscription-deleted.test.ts +++ b/e2e/smoke/webhook/subscription-deleted.test.ts @@ -47,7 +47,10 @@ describe.skipIf(harness.id !== "stripe")( .orderBy(desc(subscription.updatedAt)) .limit(1); const providerData = subRows[0]?.providerData as { subscriptionId: string } | null; - providerSubscriptionId = providerData!.subscriptionId; + if (!providerData?.subscriptionId) { + throw new Error("Expected providerData with subscriptionId on subscription row"); + } + providerSubscriptionId = providerData.subscriptionId; }); afterAll(async () => { diff --git a/packages/paykit/src/cli/commands/status.ts b/packages/paykit/src/cli/commands/status.ts index aaa7415f..9a65dceb 100644 --- a/packages/paykit/src/cli/commands/status.ts +++ b/packages/paykit/src/cli/commands/status.ts @@ -169,13 +169,17 @@ async function statusAction(options: { const hasIssues = needsMigration || needsSync || preflightErrors.length > 0; if (hasIssues) { - const action = - needsMigration && needsSync - ? "apply migrations and sync products" - : needsMigration - ? "apply migrations" - : "sync products"; - p.outro(`Run ${picocolors.bold(pushCmd)} to ${action}`); + if (needsMigration || needsSync) { + const action = + needsMigration && needsSync + ? "apply migrations and sync products" + : needsMigration + ? "apply migrations" + : "sync products"; + p.outro(`Run ${picocolors.bold(pushCmd)} to ${action}`); + } else { + p.outro("Resolve the preflight errors above before continuing"); + } await printUpdateNotification(updateCheck, deps.getInstallCommand(pm, ["paykitjs@latest"])); if (options.throw) process.exit(1); } else { diff --git a/packages/paykit/src/cli/utils/shared.ts b/packages/paykit/src/cli/utils/shared.ts index 8a699dab..baa512af 100644 --- a/packages/paykit/src/cli/utils/shared.ts +++ b/packages/paykit/src/cli/utils/shared.ts @@ -184,13 +184,13 @@ export async function checkActiveSubscriptionsOnOtherProvider( ): Promise { const errors: string[] = []; const { subscription } = await import("../../database/schema"); - const { and, eq, ne, isNotNull, count } = await import("drizzle-orm"); + const { and, ne, isNotNull, inArray, count } = await import("drizzle-orm"); const rows = await ctx.database .select({ count: count(), providerId: subscription.providerId }) .from(subscription) .where( and( - eq(subscription.status, "active"), + inArray(subscription.status, ["active", "trialing", "past_due"]), isNotNull(subscription.providerId), ne(subscription.providerId, currentProviderId), ), diff --git a/packages/paykit/src/product/product-sync.service.ts b/packages/paykit/src/product/product-sync.service.ts index 8b8bf7ed..0794ac6c 100644 --- a/packages/paykit/src/product/product-sync.service.ts +++ b/packages/paykit/src/product/product-sync.service.ts @@ -188,15 +188,25 @@ export async function syncProducts(ctx: PayKitContext): Promise p.id)); + const returnedIds = new Set(providerResults.results.map((r) => r.id)); + const missingIds = [...requestedIds].filter((id) => !returnedIds.has(id)); + if (missingIds.length > 0) { + throw new Error( + `Provider syncProducts did not return mappings for: ${missingIds.join(", ")}`, + ); + } + for (const providerResult of providerResults.results) { const plan = paidPlansToSync.find((p) => p.id === providerResult.id); - if (plan) { - await upsertProviderProduct(ctx.database, { - productInternalId: plan.storedProductInternalId, - providerId, - providerProduct: providerResult.providerProduct, - }); + if (!plan) { + throw new Error(`Provider syncProducts returned unknown product id: ${providerResult.id}`); } + await upsertProviderProduct(ctx.database, { + productInternalId: plan.storedProductInternalId, + providerId, + providerProduct: providerResult.providerProduct, + }); } } diff --git a/packages/paykit/src/subscription/subscription.service.ts b/packages/paykit/src/subscription/subscription.service.ts index 6696b6b1..f5514da2 100644 --- a/packages/paykit/src/subscription/subscription.service.ts +++ b/packages/paykit/src/subscription/subscription.service.ts @@ -15,7 +15,7 @@ import { getDefaultProductInGroup, getProductByHash, getProductByInternalId, - getProductByProviderPriceId, + getProductByProviderData, getProductFeatures, withProviderInfo, } from "../product/product.service"; diff --git a/packages/polar/src/polar-provider.ts b/packages/polar/src/polar-provider.ts index 0243bb5f..a1fa24d3 100644 --- a/packages/polar/src/polar-provider.ts +++ b/packages/polar/src/polar-provider.ts @@ -123,6 +123,14 @@ export function createPolarProvider(client: Polar, options: PolarOptions): Payme name: "Polar", async createCustomer(data) { + if (!data.email) { + throw PayKitError.from( + "BAD_REQUEST", + PAYKIT_ERROR_CODES.CUSTOMER_CREATE_FAILED, + "Polar requires a non-empty email to create a customer", + ); + } + const customerMetadata = { ...data.metadata, paykitCustomerId: data.id, @@ -130,7 +138,7 @@ export function createPolarProvider(client: Polar, options: PolarOptions): Payme try { const customer = await client.customers.create({ - email: data.email ?? "", + email: data.email, name: data.name, metadata: customerMetadata, }); @@ -138,9 +146,11 @@ export function createPolarProvider(client: Polar, options: PolarOptions): Payme return { providerCustomer: { id: customer.id }, }; - } catch { - // Customer already exists with this email. Find and re-link. - const list = await client.customers.list({ query: data.email ?? "", limit: 1 }); + } catch (error) { + if (!(error instanceof SDKValidationError)) throw error; + + // Duplicate email — find and re-link the existing customer. + const list = await client.customers.list({ query: data.email, limit: 1 }); const existing = list.result.items[0]; if (!existing) { From 3024ef96b9854d598fc5febf44379e0aa222d409 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 22 Apr 2026 20:22:19 +0400 Subject: [PATCH 12/13] fix: address second round of code review findings - fix(e2e): make startWebhookServer async to avoid port bind race - fix(cli): deduplicate paykitIds in preflight customer check - fix(cli): update preflight error message to list all blocked statuses - fix(core): use PayKitError for syncProducts validation, detect duplicates - fix(core): use subscription providerId for providerProduct lookup - fix(core): symmetric equality check for checkout product matching - fix(stripe): update test to use providerProduct instead of providerPriceId --- e2e/smoke/setup.ts | 11 +++++++---- packages/paykit/src/cli/utils/shared.ts | 10 ++++++---- packages/paykit/src/product/product-sync.service.ts | 12 ++++++------ .../paykit/src/subscription/subscription.service.ts | 11 +++++++---- packages/stripe/src/__tests__/stripe.test.ts | 4 ++-- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/e2e/smoke/setup.ts b/e2e/smoke/setup.ts index 37d2da79..294bf584 100644 --- a/e2e/smoke/setup.ts +++ b/e2e/smoke/setup.ts @@ -192,7 +192,7 @@ export async function createTestPayKit(): Promise { // 4. Start webhook server BEFORE syncing products — product sync // creates provider products which fires webhooks immediately const webhookRequests: CapturedWebhookRequest[] = []; - const server = startWebhookServer(paykit, webhookRequests); + const server = await startWebhookServer(paykit, webhookRequests); // 5. Sync products to provider await syncProducts(ctx); @@ -604,10 +604,10 @@ export async function expectExactMeteredBalance(input: { } } -function startWebhookServer( +async function startWebhookServer( paykit: Pick, webhookRequests: CapturedWebhookRequest[], -): Server { +): Promise { const server = createServer(async (req: IncomingMessage, res: ServerResponse) => { const chunks: Buffer[] = []; for await (const chunk of req) { @@ -644,7 +644,10 @@ function startWebhookServer( } }); - server.listen(WEBHOOK_PORT); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(WEBHOOK_PORT, () => resolve()); + }); return server; } diff --git a/packages/paykit/src/cli/utils/shared.ts b/packages/paykit/src/cli/utils/shared.ts index baa512af..9e577949 100644 --- a/packages/paykit/src/cli/utils/shared.ts +++ b/packages/paykit/src/cli/utils/shared.ts @@ -161,9 +161,11 @@ export async function checkProviderCustomers( const hasUnmanaged = customerSample.some((s) => !s.paykitCustomerId); if (hasUnmanaged) return [message]; - const paykitIds = customerSample - .map((s) => s.paykitCustomerId) - .filter((id): id is string => id !== null); + const paykitIds = [ + ...new Set( + customerSample.map((s) => s.paykitCustomerId).filter((id): id is string => id !== null), + ), + ]; if (paykitIds.length > 0) { const { customer } = await import("../../database/schema"); @@ -199,7 +201,7 @@ export async function checkActiveSubscriptionsOnOtherProvider( for (const row of rows) { if (row.count > 0 && row.providerId) { errors.push( - `Found ${String(row.count)} active subscription${row.count === 1 ? "" : "s"} linked to "${row.providerId}" but current provider is "${currentProviderId}". Existing subscriptions must be canceled before switching providers.`, + `Found ${String(row.count)} subscription${row.count === 1 ? "" : "s"} (active, trialing, or past_due) linked to "${row.providerId}" but current provider is "${currentProviderId}". Existing subscriptions must be canceled before switching providers.`, ); } } diff --git a/packages/paykit/src/product/product-sync.service.ts b/packages/paykit/src/product/product-sync.service.ts index 0794ac6c..4e83d98c 100644 --- a/packages/paykit/src/product/product-sync.service.ts +++ b/packages/paykit/src/product/product-sync.service.ts @@ -191,17 +191,17 @@ export async function syncProducts(ctx: PayKitContext): Promise p.id)); const returnedIds = new Set(providerResults.results.map((r) => r.id)); const missingIds = [...requestedIds].filter((id) => !returnedIds.has(id)); - if (missingIds.length > 0) { - throw new Error( - `Provider syncProducts did not return mappings for: ${missingIds.join(", ")}`, + if (missingIds.length > 0 || returnedIds.size !== requestedIds.size) { + throw PayKitError.from( + "INTERNAL_SERVER_ERROR", + PAYKIT_ERROR_CODES.PLAN_SYNC_FAILED, + `Provider syncProducts returned invalid mapping: missing=[${missingIds.join(", ")}], expected=${String(requestedIds.size)}, got=${String(returnedIds.size)}`, ); } for (const providerResult of providerResults.results) { const plan = paidPlansToSync.find((p) => p.id === providerResult.id); - if (!plan) { - throw new Error(`Provider syncProducts returned unknown product id: ${providerResult.id}`); - } + if (!plan) continue; await upsertProviderProduct(ctx.database, { productInternalId: plan.storedProductInternalId, providerId, diff --git a/packages/paykit/src/subscription/subscription.service.ts b/packages/paykit/src/subscription/subscription.service.ts index f5514da2..2f2b41a0 100644 --- a/packages/paykit/src/subscription/subscription.service.ts +++ b/packages/paykit/src/subscription/subscription.service.ts @@ -336,9 +336,11 @@ export async function prepareSubscribeCheckoutCompleted( const checkoutProviderProduct = checkoutSubscription.providerProduct; const storedProviderProduct = subCtx.storedPlan.providerProduct; if (checkoutProviderProduct && storedProviderProduct) { - const mismatch = Object.entries(checkoutProviderProduct).some( - ([key, value]) => storedProviderProduct[key] !== value, - ); + const checkoutKeys = Object.keys(checkoutProviderProduct); + const storedKeys = Object.keys(storedProviderProduct); + const mismatch = + checkoutKeys.length !== storedKeys.length || + checkoutKeys.some((key) => checkoutProviderProduct[key] !== storedProviderProduct[key]); if (mismatch) { throw PayKitError.from( "BAD_REQUEST", @@ -1252,6 +1254,7 @@ function mapJoinRowToSubscriptionWithCatalog(row: { product: typeof product.$inferSelect; }): SubscriptionWithCatalog { const providerMap = row.product.provider as ProviderProductMap | null; + const providerId = row.subscription.providerId; return { ...row.subscription, planGroup: row.product.group, @@ -1260,7 +1263,7 @@ function mapJoinRowToSubscriptionWithCatalog(row: { planName: row.product.name, priceAmount: row.product.priceAmount, priceInterval: row.product.priceInterval, - providerProduct: Object.values(providerMap ?? {})[0] ?? null, + providerProduct: (providerId ? providerMap?.[providerId] : null) ?? null, }; } diff --git a/packages/stripe/src/__tests__/stripe.test.ts b/packages/stripe/src/__tests__/stripe.test.ts index abeed9ea..3592659e 100644 --- a/packages/stripe/src/__tests__/stripe.test.ts +++ b/packages/stripe/src/__tests__/stripe.test.ts @@ -157,7 +157,7 @@ describe("providers/stripe", () => { cancelUrl: "https://example.com/cancel", metadata: {}, providerCustomerId: "cus_123", - providerPriceId: "price_123", + providerProduct: { priceId: "price_123" }, successUrl: "https://example.com/success", }); @@ -176,7 +176,7 @@ describe("providers/stripe", () => { cancelUrl: "https://example.com/cancel", metadata: {}, providerCustomerId: "cus_123", - providerPriceId: "price_123", + providerProduct: { priceId: "price_123" }, successUrl: "https://example.com/success", }); From c8daadaa9f0f9e1f90bd3b5a9ea927a4a1b9a208 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 22 Apr 2026 20:41:48 +0400 Subject: [PATCH 13/13] fix: detect duplicate provider sync results and deduplicate cleanup IDs --- e2e/smoke/setup.ts | 6 +++--- .../src/product/product-sync.service.ts | 20 ++++++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/e2e/smoke/setup.ts b/e2e/smoke/setup.ts index 294bf584..7c403411 100644 --- a/e2e/smoke/setup.ts +++ b/e2e/smoke/setup.ts @@ -207,14 +207,14 @@ export async function createTestPayKit(): Promise { webhookRequests, cleanup: async () => { const customerRows = await ctx.database.query.customer.findMany(); - const providerCustomerIds: string[] = []; + const idSet = new Set(); for (const row of customerRows) { const providerMap = (row.provider ?? {}) as Record; const entry = providerMap[harness.id]; - if (entry?.id) providerCustomerIds.push(entry.id); + if (entry?.id) idSet.add(entry.id); } - await harness.cleanup({ providerCustomerIds }); + await harness.cleanup({ providerCustomerIds: [...idSet] }); // Wait for cleanup webhooks to arrive and be processed await new Promise((resolve) => setTimeout(resolve, 10_000)); diff --git a/packages/paykit/src/product/product-sync.service.ts b/packages/paykit/src/product/product-sync.service.ts index 4e83d98c..47591033 100644 --- a/packages/paykit/src/product/product-sync.service.ts +++ b/packages/paykit/src/product/product-sync.service.ts @@ -189,17 +189,27 @@ export async function syncProducts(ctx: PayKitContext): Promise p.id)); - const returnedIds = new Set(providerResults.results.map((r) => r.id)); - const missingIds = [...requestedIds].filter((id) => !returnedIds.has(id)); - if (missingIds.length > 0 || returnedIds.size !== requestedIds.size) { + const resultById = new Map(); + for (const r of providerResults.results) { + if (resultById.has(r.id)) { + throw PayKitError.from( + "INTERNAL_SERVER_ERROR", + PAYKIT_ERROR_CODES.PLAN_SYNC_FAILED, + `Provider syncProducts returned duplicate mapping for id: ${r.id}`, + ); + } + resultById.set(r.id, r); + } + const missingIds = [...requestedIds].filter((id) => !resultById.has(id)); + if (missingIds.length > 0 || resultById.size !== requestedIds.size) { throw PayKitError.from( "INTERNAL_SERVER_ERROR", PAYKIT_ERROR_CODES.PLAN_SYNC_FAILED, - `Provider syncProducts returned invalid mapping: missing=[${missingIds.join(", ")}], expected=${String(requestedIds.size)}, got=${String(returnedIds.size)}`, + `Provider syncProducts returned invalid mapping: missing=[${missingIds.join(", ")}], expected=${String(requestedIds.size)}, got=${String(resultById.size)}`, ); } - for (const providerResult of providerResults.results) { + for (const [, providerResult] of resultById) { const plan = paidPlansToSync.find((p) => p.id === providerResult.id); if (!plan) continue; await upsertProviderProduct(ctx.database, {