From b323cc1a09b86dc4f3b324401f3eb504c11569e8 Mon Sep 17 00:00:00 2001 From: tianzx Date: Wed, 3 Sep 2025 11:59:29 +0800 Subject: [PATCH 1/2] chore: add initial Drizzle ORM snapshots and implement annual quota refresh logic in subscription limits on branch 0903 --- apps/cdn/CHANGELOG.md | 9 + apps/cdn/package.json | 2 +- apps/cdn/src/utils/quota-management.ts | 22 +- apps/deploy/CHANGELOG.md | 9 + apps/deploy/package.json | 2 +- apps/deploy/src/utils/deploy-quota.ts | 22 +- packages/auth/CHANGELOG.md | 8 + packages/auth/package.json | 2 +- .../auth/plugins/stripe/customer-handlers.ts | 4 +- .../plugins/stripe/subscription-lifecycle.ts | 10 +- .../plugins/stripe/subscription-updates.ts | 10 +- .../__tests__/annual-quota-refresh.test.ts | 91 +++ .../annual-quota-refresh.ts | 546 +++++++++++++++ .../auth/utils/subscription-limits/core.ts | 86 ++- packages/db/CHANGELOG.md | 6 + packages/db/drizzle/0001_sticky_blade.sql | 3 + packages/db/drizzle/meta/0001_snapshot.json | 654 ++++++++++++++++++ packages/db/drizzle/meta/_journal.json | 7 + packages/db/package.json | 2 +- packages/db/schema/project-schema.ts | 7 + 20 files changed, 1489 insertions(+), 13 deletions(-) create mode 100644 packages/auth/utils/subscription-limits/__tests__/annual-quota-refresh.test.ts create mode 100644 packages/auth/utils/subscription-limits/annual-quota-refresh.ts create mode 100644 packages/db/drizzle/0001_sticky_blade.sql create mode 100644 packages/db/drizzle/meta/0001_snapshot.json diff --git a/apps/cdn/CHANGELOG.md b/apps/cdn/CHANGELOG.md index b05a4a2..7c5d403 100644 --- a/apps/cdn/CHANGELOG.md +++ b/apps/cdn/CHANGELOG.md @@ -1,5 +1,14 @@ # @libra/cdn +## 1.0.5 + +### Patch Changes + +- stripe bug fix +- Updated dependencies + - @libra/auth@1.0.4 + - @libra/db@1.0.1 + ## 1.0.4 ### Patch Changes diff --git a/apps/cdn/package.json b/apps/cdn/package.json index a9d2336..ce684b9 100644 --- a/apps/cdn/package.json +++ b/apps/cdn/package.json @@ -1,6 +1,6 @@ { "name": "@libra/cdn", - "version": "1.0.4", + "version": "1.0.5", "scripts": { "dev": "wrangler dev --port 3004 --persist-to=../web/.wrangler/state", "deploy": "wrangler deploy --minify", diff --git a/apps/cdn/src/utils/quota-management.ts b/apps/cdn/src/utils/quota-management.ts index fa79613..c708ebe 100644 --- a/apps/cdn/src/utils/quota-management.ts +++ b/apps/cdn/src/utils/quota-management.ts @@ -25,6 +25,7 @@ import type { NodePgDatabase } from 'drizzle-orm/node-postgres' import {getActiveOrganization} from '@libra/auth/utils/organization-utils' import {getPlanLimitsForHono} from '@libra/auth/utils/subscription-limits/constants' import {PLAN_TYPES, type PlanType} from '@libra/auth/utils/subscription-limits/types' +import {checkRefreshAndDeductQuota} from '@libra/auth/utils/subscription-limits/annual-quota-refresh' import {log, tryCatch} from '@libra/common' import {subscriptionLimit} from '@libra/db/schema/project-schema' import type * as schema from '@libra/db/schema/project-schema' @@ -344,6 +345,25 @@ export async function checkAndUpdateUploadUsage(c: AppContext): Promise operation: 'upload_quota_deduction' }) + // PRIORITY: Try annual subscription quota refresh and deduction first + try { + const annualResult = await checkRefreshAndDeductQuota(organizationId, 'uploadLimit', 1) + if (annualResult.success) { + log.cdn('info', 'Upload quota deducted from annual subscription', { + organizationId, + remaining: annualResult.remaining, + operation: 'upload_quota_deduction' + }) + return true + } + } catch (error) { + log.cdn('warn', 'Annual quota refresh check failed, falling back to regular logic', { + organizationId, + error: error instanceof Error ? error.message : 'Unknown error', + operation: 'upload_quota_deduction' + }) + } + // Get current time from database to ensure UTC consistency across all operations const {rows} = await db.execute(sql`SELECT NOW() as "dbNow"`) const [{dbNow}] = rows as [{ dbNow: string | Date }] @@ -356,7 +376,7 @@ export async function checkAndUpdateUploadUsage(c: AppContext): Promise operation: 'upload_quota_deduction' }) - // PRIORITY PATH: Try FREE plan deduction first + // SECONDARY PATH: Try FREE plan deduction // Users should consume their free quota before paid quota const freeDeductionResult = await handleFreePlanUploadDeduction(db, organizationId, now, c) if (freeDeductionResult) { diff --git a/apps/deploy/CHANGELOG.md b/apps/deploy/CHANGELOG.md index 2d22414..8aac753 100644 --- a/apps/deploy/CHANGELOG.md +++ b/apps/deploy/CHANGELOG.md @@ -1,5 +1,14 @@ # @libra/deploy +## 1.0.8 + +### Patch Changes + +- stripe bug fix +- Updated dependencies + - @libra/auth@1.0.4 + - @libra/db@1.0.1 + ## 1.0.7 ### Patch Changes diff --git a/apps/deploy/package.json b/apps/deploy/package.json index 1be4786..abaaff4 100644 --- a/apps/deploy/package.json +++ b/apps/deploy/package.json @@ -1,6 +1,6 @@ { "name": "@libra/deploy", - "version": "1.0.7", + "version": "1.0.8", "description": "Queue-based deployment service for Libra platform", "scripts": { "dev": "wrangler dev --port 3008 --persist-to=../web/.wrangler/state", diff --git a/apps/deploy/src/utils/deploy-quota.ts b/apps/deploy/src/utils/deploy-quota.ts index 97dd5e7..428fd40 100644 --- a/apps/deploy/src/utils/deploy-quota.ts +++ b/apps/deploy/src/utils/deploy-quota.ts @@ -24,6 +24,7 @@ import { and, eq, sql } from 'drizzle-orm' import { tryCatch } from '@libra/common' import { createLogger } from './logger' import type { Bindings } from '../types' +import { checkRefreshAndDeductQuota } from '@libra/auth/utils/subscription-limits/annual-quota-refresh' // Plan types for quota management const PLAN_TYPES = { @@ -66,6 +67,25 @@ export async function checkAndUpdateDeployUsageForWorkflow( operation: 'deploy_quota_deduction' }) + // PRIORITY: Try annual subscription quota refresh and deduction first + try { + const annualResult = await checkRefreshAndDeductQuota(organizationId, 'deployLimit', 1) + if (annualResult.success) { + logger.info('Deploy quota deducted from annual subscription', { + organizationId, + remaining: annualResult.remaining, + operation: 'deploy_quota_deduction' + }) + return true + } + } catch (error) { + logger.warn('Annual quota refresh check failed, falling back to regular logic', { + organizationId, + error: error instanceof Error ? error.message : 'Unknown error', + operation: 'deploy_quota_deduction' + }) + } + db = await getDbForHono({ env }) // Get current time from database to ensure UTC consistency across all operations @@ -73,7 +93,7 @@ export async function checkAndUpdateDeployUsageForWorkflow( const [{ dbNow }] = rows as [{ dbNow: string | Date }] const now = typeof dbNow === 'string' ? new Date(dbNow) : dbNow - // PRIORITY PATH: Try FREE plan deduction first + // SECONDARY PATH: Try FREE plan deduction // Users should consume their free quota before paid quota const freeDeductionResult = await handleFreePlanDeployDeduction(db, organizationId, now) if (freeDeductionResult) { diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md index f535674..2e9a4f3 100644 --- a/packages/auth/CHANGELOG.md +++ b/packages/auth/CHANGELOG.md @@ -1,5 +1,13 @@ # @libra/auth +## 1.0.4 + +### Patch Changes + +- stripe bug fix +- Updated dependencies + - @libra/db@1.0.1 + ## 1.0.3 ### Patch Changes diff --git a/packages/auth/package.json b/packages/auth/package.json index 0ce28cf..4a730be 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@libra/auth", - "version": "1.0.3", + "version": "1.0.4", "private": true, "main": "auth-server.ts", "types": "./index.ts", diff --git a/packages/auth/plugins/stripe/customer-handlers.ts b/packages/auth/plugins/stripe/customer-handlers.ts index d476e52..d766040 100644 --- a/packages/auth/plugins/stripe/customer-handlers.ts +++ b/packages/auth/plugins/stripe/customer-handlers.ts @@ -43,7 +43,9 @@ export const onCustomerCreate = async ({ customer, stripeCustomer, user }: any, stripeCustomer.id, PLAN_TYPES.FREE, now, - periodEnd + periodEnd, + undefined, // no custom limits for FREE plan + 'month' // FREE plans are always monthly ) // @ts-ignore user.stripeCustomerId = stripeCustomer.id diff --git a/packages/auth/plugins/stripe/subscription-lifecycle.ts b/packages/auth/plugins/stripe/subscription-lifecycle.ts index 81ee5f1..202537d 100644 --- a/packages/auth/plugins/stripe/subscription-lifecycle.ts +++ b/packages/auth/plugins/stripe/subscription-lifecycle.ts @@ -472,13 +472,21 @@ export const onSubscriptionComplete = async ({ event, subscription, stripeSubscr // Create new subscription limit record if (subscription.periodStart && subscription.periodEnd) { try { + // Determine billing interval from period duration + const periodStart = new Date(subscription.periodStart) + const periodEnd = new Date(subscription.periodEnd) + const periodDurationMs = periodEnd.getTime() - periodStart.getTime() + const monthsInPeriod = Math.round(periodDurationMs / (1000 * 60 * 60 * 24 * 30.44)) // Average days per month + const billingInterval = monthsInPeriod >= 11 ? 'year' : 'month' // 11+ months considered annual + await createOrUpdateSubscriptionLimit( subscription.referenceId, subscription.stripeCustomerId || null, subscription.plan, subscription.periodStart, subscription.periodEnd, - customLimits + customLimits, + billingInterval ) log.subscription('info', 'Subscription limits created/updated', { diff --git a/packages/auth/plugins/stripe/subscription-updates.ts b/packages/auth/plugins/stripe/subscription-updates.ts index 47ae36c..7e201af 100644 --- a/packages/auth/plugins/stripe/subscription-updates.ts +++ b/packages/auth/plugins/stripe/subscription-updates.ts @@ -102,6 +102,13 @@ export const onSubscriptionUpdate = async ({ event, subscription }: any) => { } } + // Determine billing interval from period duration + const periodStart = new Date(subscription.periodStart) + const periodEnd = new Date(subscription.periodEnd) + const periodDurationMs = periodEnd.getTime() - periodStart.getTime() + const monthsInPeriod = Math.round(periodDurationMs / (1000 * 60 * 60 * 24 * 30.44)) // Average days per month + const billingInterval = monthsInPeriod >= 11 ? 'year' : 'month' // 11+ months considered annual + // Always update limits, whether renewal or not await createOrUpdateSubscriptionLimit( subscription.referenceId, @@ -109,7 +116,8 @@ export const onSubscriptionUpdate = async ({ event, subscription }: any) => { subscription.plan, subscription.periodStart, subscription.periodEnd, - customLimits + customLimits, + billingInterval ) } else if (subscription.status === 'past_due' || subscription.status === 'unpaid') { diff --git a/packages/auth/utils/subscription-limits/__tests__/annual-quota-refresh.test.ts b/packages/auth/utils/subscription-limits/__tests__/annual-quota-refresh.test.ts new file mode 100644 index 0000000..b0f5d45 --- /dev/null +++ b/packages/auth/utils/subscription-limits/__tests__/annual-quota-refresh.test.ts @@ -0,0 +1,91 @@ +/* + * SPDX-License-Identifier: AGPL-3.0-only + * annual-quota-refresh.test.ts + * Copyright (C) 2025 Nextify Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +import { describe, it, expect, beforeEach, vi } from 'vitest' +import { checkAndRefreshAnnualQuota, checkRefreshAndDeductQuota } from '../annual-quota-refresh' + +// Mock dependencies +vi.mock('@libra/db', () => ({ + getProjectDb: vi.fn(), +})) + +vi.mock('@libra/common', () => ({ + log: { + subscription: vi.fn(), + }, +})) + +describe('Annual Quota Refresh', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + describe('checkAndRefreshAnnualQuota', () => { + it('should return false for invalid organization ID', async () => { + const result = await checkAndRefreshAnnualQuota('') + expect(result).toBe(false) + }) + + it('should return false when no annual subscription found', async () => { + // Mock database to return no annual subscription + const mockDb = { + query: { + subscriptionLimit: { + findFirst: vi.fn().mockResolvedValue(null), + }, + }, + } + + vi.mocked(require('@libra/db').getProjectDb).mockResolvedValue(mockDb) + + const result = await checkAndRefreshAnnualQuota('org-123') + expect(result).toBe(false) + }) + }) + + describe('checkRefreshAndDeductQuota', () => { + it('should return false for invalid organization ID', async () => { + const result = await checkRefreshAndDeductQuota('', 'aiNums', 1) + expect(result).toEqual({ success: false }) + }) + + it('should handle all quota types', async () => { + const quotaTypes = ['aiNums', 'enhanceNums', 'uploadLimit', 'deployLimit', 'projectNums'] as const + + for (const quotaType of quotaTypes) { + const result = await checkRefreshAndDeductQuota('org-123', quotaType, 1) + expect(result).toHaveProperty('success') + } + }) + }) +}) + +// Integration test example (commented out - requires actual database) +/* +describe('Annual Quota Refresh Integration', () => { + it('should refresh quota for annual subscription after 1 month', async () => { + // This test would require: + // 1. Setting up test database + // 2. Creating annual subscription with lastQuotaRefresh > 1 month ago + // 3. Calling checkAndRefreshAnnualQuota + // 4. Verifying quota was refreshed and lastQuotaRefresh updated + }) +}) +*/ diff --git a/packages/auth/utils/subscription-limits/annual-quota-refresh.ts b/packages/auth/utils/subscription-limits/annual-quota-refresh.ts new file mode 100644 index 0000000..f99bf59 --- /dev/null +++ b/packages/auth/utils/subscription-limits/annual-quota-refresh.ts @@ -0,0 +1,546 @@ +/* + * SPDX-License-Identifier: AGPL-3.0-only + * annual-quota-refresh.ts + * Copyright (C) 2025 Nextify Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +import { addMonths } from 'date-fns' +import { and, eq, sql } from 'drizzle-orm' +import { log } from '@libra/common' +import { getDbAsync } from '@libra/db' +import { subscriptionLimit } from '@libra/db/schema/project-schema' +import { getPlanLimits } from './constants' +import { PLAN_TYPES, type PlanType } from './types' + +// Type-safe column mapping for quota types +const quotaColumnMap = { + aiNums: subscriptionLimit.aiNums, + enhanceNums: subscriptionLimit.enhanceNums, + uploadLimit: subscriptionLimit.uploadLimit, + deployLimit: subscriptionLimit.deployLimit, + projectNums: subscriptionLimit.projectNums, +} as const + +/** + * Resolve quota limit with fallback rules + * Centralizes business logic for quota limit calculation + */ +function resolveQuotaLimit( + limits: any, + quotaType: keyof typeof quotaColumnMap, + fallbackValue?: number +): number { + switch (quotaType) { + case 'aiNums': + return limits.aiNums || 0 + case 'enhanceNums': + return limits.enhanceNums ?? limits.aiNums ?? 0 + case 'uploadLimit': + return limits.uploadLimit ?? limits.aiNums ?? 0 + case 'deployLimit': + return limits.deployLimit ?? (limits.aiNums ? limits.aiNums * 2 : 0) + case 'projectNums': + return limits.projectNums ?? fallbackValue ?? 0 + default: + return 0 + } +} + +/** + * Check if annual subscription quota needs monthly refresh + * @param organizationId Organization ID + * @returns Promise Whether quota was refreshed + */ +export async function checkAndRefreshAnnualQuota(organizationId: string): Promise { + if (!organizationId?.trim()) { + return false + } + + try { + const db = await getDbAsync() + + // Get current time from database to ensure consistency across instances + const { rows } = await db.execute(sql`SELECT NOW() as "dbNow"`) + const [{ dbNow }] = rows as [{ dbNow: string | Date }] + const now = typeof dbNow === 'string' ? new Date(dbNow) : dbNow + + // Find active annual subscription that hasn't expired + const annualSubscription = await db.query.subscriptionLimit.findFirst({ + where: and( + eq(subscriptionLimit.organizationId, organizationId), + eq(subscriptionLimit.isActive, true), + eq(subscriptionLimit.billingInterval, 'year'), + sql`${subscriptionLimit.planName} != ${PLAN_TYPES.FREE}`, + sql`${subscriptionLimit.periodEnd} >= ${now}` // Not expired + ) + }) + + if (!annualSubscription) { + // No annual subscription found + return false + } + + // Check if quota refresh is needed + const needsRefresh = shouldRefreshQuota(annualSubscription, now) + + if (!needsRefresh) { + return false + } + + log.subscription('info', 'Annual subscription quota refresh needed', { + organizationId, + planName: annualSubscription.planName, + lastQuotaRefresh: annualSubscription.lastQuotaRefresh, + operation: 'annual_quota_refresh' + }) + + // Perform quota refresh + await refreshAnnualSubscriptionQuota(annualSubscription, now) + + return true + } catch (error) { + log.subscription('error', 'Failed to check/refresh annual quota', { + organizationId, + error: error instanceof Error ? error.message : 'Unknown error', + operation: 'annual_quota_refresh' + }) + return false + } +} + +/** + * Determine if quota refresh is needed for annual subscription + * Uses addMonths for more accurate "one month later" semantics + */ +function shouldRefreshQuota(subscription: any, now: Date): boolean { + // If no lastQuotaRefresh, this is a new subscription - refresh needed + if (!subscription.lastQuotaRefresh) { + return true + } + + const lastRefresh = new Date(subscription.lastQuotaRefresh) + const nextRefreshTime = addMonths(lastRefresh, 1) + + // Refresh if current time is at or after the next refresh time + return now >= nextRefreshTime +} + +/** + * Refresh quota for annual subscription + */ +async function refreshAnnualSubscriptionQuota(subscription: any, now: Date): Promise { + const db = await getDbAsync() + + // Get plan limits + const { limits } = await getPlanLimits(subscription.planName as any) + + await db.transaction(async (tx: any) => { + // Refresh quota while preserving projectNums (existing projects) + await tx + .update(subscriptionLimit) + .set({ + aiNums: limits.aiNums, + enhanceNums: limits.aiNums, + uploadLimit: limits.aiNums, + deployLimit: limits.aiNums * 2, + seats: limits.seats, + // Keep existing projectNums - don't reset user's project count + lastQuotaRefresh: now.toISOString(), + updatedAt: sql`CURRENT_TIMESTAMP`, + }) + .where(eq(subscriptionLimit.id, subscription.id)) + + log.subscription('info', 'Annual subscription quota refreshed', { + organizationId: subscription.organizationId, + planName: subscription.planName, + refreshedQuota: { + aiNums: limits.aiNums, + enhanceNums: limits.aiNums, + uploadLimit: limits.aiNums, + deployLimit: limits.aiNums * 2, + seats: limits.seats + }, + lastQuotaRefresh: now.toISOString(), + operation: 'annual_quota_refresh' + }) + }) +} + +/** + * Check and refresh quota with deduction for immediate use + * This is used when a quota operation needs to happen immediately after refresh + * Uses optimistic locking to prevent race conditions in concurrent refresh scenarios + */ +export async function checkRefreshAndDeductQuota( + organizationId: string, + quotaType: 'aiNums' | 'enhanceNums' | 'uploadLimit' | 'deployLimit' | 'projectNums', + deductAmount = 1, + maxRetries = 3 +): Promise<{ success: boolean; remaining?: number; reason?: 'insufficient' | 'concurrency' | 'not_found' | 'expired' | 'invalid' }> { + if (!organizationId?.trim()) { + return { success: false, reason: 'invalid' } + } + + if (deductAmount <= 0 || !Number.isSafeInteger(deductAmount)) { + log.subscription('warn', 'Invalid deduct amount for annual quota', { + organizationId, + quotaType, + deductAmount, + operation: 'annual_quota_refresh_deduct' + }) + return { success: false, reason: 'invalid' } + } + + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + const db = await getDbAsync() + + // Get current time from database to ensure consistency across instances + const { rows } = await db.execute(sql`SELECT NOW() as "dbNow"`) + const [{ dbNow }] = rows as [{ dbNow: string | Date }] + const now = typeof dbNow === 'string' ? new Date(dbNow) : dbNow + + // Find active annual subscription with current values that hasn't expired + const annualSubscription = await db.query.subscriptionLimit.findFirst({ + where: and( + eq(subscriptionLimit.organizationId, organizationId), + eq(subscriptionLimit.isActive, true), + eq(subscriptionLimit.billingInterval, 'year'), + sql`${subscriptionLimit.planName} != ${PLAN_TYPES.FREE}`, + sql`${subscriptionLimit.periodEnd} >= ${now}` // Not expired + ) + }) + + if (!annualSubscription) { + return { success: false, reason: 'not_found' } + } + + // Check if quota refresh is needed + const needsRefresh = shouldRefreshQuota(annualSubscription, now) + const lastRefreshSnapshot = annualSubscription.lastQuotaRefresh + + // Get plan limits outside transaction to reduce lock time + const planLimitsResult = needsRefresh ? await getPlanLimits(annualSubscription.planName as PlanType) : null + const limits = planLimitsResult?.limits + + // Fast fail if refresh is needed but limits are unavailable + if (needsRefresh && !limits) { + log.subscription('error', 'Plan limits unavailable for annual quota refresh', { + organizationId, + planName: annualSubscription.planName, + quotaType, + operation: 'annual_quota_refresh_deduct' + }) + return { success: false, reason: 'not_found' } + } + + // Define consistency guard for both refresh and non-refresh paths + const consistencyGuard = lastRefreshSnapshot + ? eq(subscriptionLimit.lastQuotaRefresh, lastRefreshSnapshot) + : sql`${subscriptionLimit.lastQuotaRefresh} IS NULL` + + return await db.transaction(async (tx) => { + if (needsRefresh && limits) { + // Use optimistic locking: only refresh if lastQuotaRefresh hasn't changed + // IMPORTANT: Refresh will reset all quota types to plan limits, potentially + // overwriting concurrent deductions that happened before this refresh. + // This is semantically acceptable as refresh represents a new billing period. + + // Simplified refresh: since we already determined refresh is needed, just do it + // No complex CASE logic - directly reset to plan limits and deduct + // Use centralized quota limit resolution + const refreshUpdate: any = { + aiNums: quotaType === 'aiNums' + ? Math.max(0, resolveQuotaLimit(limits, 'aiNums') - deductAmount) + : resolveQuotaLimit(limits, 'aiNums'), + enhanceNums: quotaType === 'enhanceNums' + ? Math.max(0, resolveQuotaLimit(limits, 'enhanceNums') - deductAmount) + : resolveQuotaLimit(limits, 'enhanceNums'), + uploadLimit: quotaType === 'uploadLimit' + ? Math.max(0, resolveQuotaLimit(limits, 'uploadLimit') - deductAmount) + : resolveQuotaLimit(limits, 'uploadLimit'), + deployLimit: quotaType === 'deployLimit' + ? Math.max(0, resolveQuotaLimit(limits, 'deployLimit') - deductAmount) + : resolveQuotaLimit(limits, 'deployLimit'), + seats: limits.seats, + lastQuotaRefresh: now, + updatedAt: now, + } + + // Handle projectNums specially - don't reset, only deduct if sufficient + if (quotaType === 'projectNums') { + refreshUpdate.projectNums = sql`GREATEST(0, COALESCE(${quotaColumnMap.projectNums}, 0) - ${deductAmount})` + } + // For other quota types, keep existing projectNums unchanged + + // Build WHERE conditions for refresh path + const refreshWhereConditions = [ + eq(subscriptionLimit.id, annualSubscription.id), + eq(subscriptionLimit.isActive, true), + eq(subscriptionLimit.planName, annualSubscription.planName), + eq(subscriptionLimit.billingInterval, 'year'), + sql`${subscriptionLimit.periodEnd} >= ${now}`, + consistencyGuard + ] + + // Add projectNums protection if needed + if (quotaType === 'projectNums') { + refreshWhereConditions.push(sql`COALESCE(${subscriptionLimit.projectNums}, 0) >= ${deductAmount}`) + } + + const result = await tx + .update(subscriptionLimit) + .set(refreshUpdate) + .where(and(...refreshWhereConditions)) + .returning() + + if (result.length === 0) { + // Check if it's optimistic lock failure or quota insufficient + const check = await tx.query.subscriptionLimit.findFirst({ + where: eq(subscriptionLimit.id, annualSubscription.id), + }) + + if (!check) { + return { success: false, reason: 'not_found' } + } + + const currentRefresh = check.lastQuotaRefresh?.toString() + const snapshotRefresh = lastRefreshSnapshot?.toString() + + if (currentRefresh !== snapshotRefresh) { + // Optimistic lock failed, retry + log.subscription('warn', 'Annual quota refresh optimistic lock failed, retrying', { + organizationId, + quotaType, + attempt: attempt + 1, + operation: 'annual_quota_refresh_deduct' + }) + throw new Error('Optimistic lock failed') + } + + // Check if it's projectNums quota insufficient + if (quotaType === 'projectNums') { + const currentProjectNums = check.projectNums || 0 + if (currentProjectNums < deductAmount) { + log.subscription('info', 'Annual quota refresh failed: insufficient projectNums', { + organizationId, + quotaType, + currentProjectNums, + deductAmount, + operation: 'annual_quota_refresh_deduct' + }) + return { success: false, reason: 'insufficient' } + } + } + + // Check specific reasons for 0 rows update + const currentSnapshot = { + planName: check.planName, + isActive: check.isActive, + billingInterval: check.billingInterval, + periodEnd: check.periodEnd + } + + log.subscription('warn', 'Annual quota refresh failed: subscription state changed', { + organizationId, + quotaType, + originalSnapshot: { + planName: annualSubscription.planName, + isActive: annualSubscription.isActive, + billingInterval: annualSubscription.billingInterval, + periodEnd: annualSubscription.periodEnd + }, + currentSnapshot, + operation: 'annual_quota_refresh_deduct' + }) + + // Determine specific reason + if (!currentSnapshot.isActive) { + return { success: false, reason: 'expired' } + } + if (currentSnapshot.planName !== annualSubscription.planName) { + return { success: false, reason: 'not_found' } + } + if (currentSnapshot.billingInterval !== 'year') { + return { success: false, reason: 'not_found' } + } + if (new Date(currentSnapshot.periodEnd) < now) { + return { success: false, reason: 'expired' } + } + + // Unknown reason + return { success: false, reason: 'expired' } + } + + // Since we simplified the refresh logic, if we reach here and needsRefresh was true, + // and the update succeeded, then refresh definitely happened + const wasRefreshed = true + + // Get the remaining quota from the updated record + const updatedRecord = result[0] + const remaining = updatedRecord ? updatedRecord[quotaType] : 0 + + log.subscription('info', 'Annual quota processed with race condition protection', { + organizationId, + planName: annualSubscription.planName, + quotaType, + deductAmount, + remaining, + wasRefreshed, + attempt: attempt + 1, + operation: 'annual_quota_refresh_deduct' + }) + + return { + success: true, + remaining + } + } + + // Just deduct from existing quota with atomic database operation + const result = await tx + .update(subscriptionLimit) + .set({ + [quotaType]: sql`GREATEST(0, COALESCE(${quotaColumnMap[quotaType]}, 0) - ${deductAmount})` as any, + updatedAt: sql`CURRENT_TIMESTAMP`, + }) + .where(and( + eq(subscriptionLimit.id, annualSubscription.id), + eq(subscriptionLimit.isActive, true), + eq(subscriptionLimit.planName, annualSubscription.planName), + eq(subscriptionLimit.billingInterval, 'year'), + sql`${subscriptionLimit.periodEnd} >= ${now}`, + consistencyGuard, + sql`COALESCE(${quotaColumnMap[quotaType]}, 0) >= ${deductAmount}` // Ensure quota still available + )) + .returning() + + if (result.length === 0) { + // Check if it's optimistic lock failure or quota insufficient + const check = await tx.query.subscriptionLimit.findFirst({ + where: eq(subscriptionLimit.id, annualSubscription.id), + }) + + if (!check) { + return { success: false, reason: 'not_found' } + } + + const currentRefresh = check.lastQuotaRefresh?.toString() + const snapshotRefresh = lastRefreshSnapshot?.toString() + + if (currentRefresh !== snapshotRefresh) { + // Optimistic lock failed, retry + log.subscription('warn', 'Annual quota deduction optimistic lock failed, retrying', { + organizationId, + quotaType, + attempt: attempt + 1, + operation: 'annual_quota_refresh_deduct' + }) + throw new Error('Optimistic lock failed') + } + + // Quota insufficient + log.subscription('info', 'Annual quota deduction failed: insufficient quota', { + organizationId, + quotaType, + currentQuota: check[quotaType] || 0, + deductAmount, + operation: 'annual_quota_refresh_deduct' + }) + return { success: false, reason: 'insufficient' } + } + + // Get the remaining quota from the updated record + const updatedRecord = result[0] + const remaining = updatedRecord ? updatedRecord[quotaType] : 0 + + return { + success: true, + remaining + } + }) + } catch (error) { + if (attempt === maxRetries - 1) { + log.subscription('error', 'Failed to refresh and deduct annual quota after retries', { + organizationId, + quotaType, + deductAmount, + attempts: maxRetries, + error: error instanceof Error ? error.message : 'Unknown error', + operation: 'annual_quota_refresh_deduct' + }) + return { success: false, reason: 'concurrency' } + } + + // Exponential backoff with jitter to reduce contention + const baseDelay = 50 + const backoffDelay = baseDelay * (2 ** attempt) + const jitter = Math.random() * baseDelay + await new Promise(resolve => setTimeout(resolve, backoffDelay + jitter)) + } + } + + return { success: false, reason: 'concurrency' } +} + +/** + * Unified quota deduction entry point for all services + * This function should be used by all services that need to deduct quota + * It handles annual subscription refresh automatically + */ +export async function deductQuotaUnified( + organizationId: string, + quotaType: 'aiNums' | 'enhanceNums' | 'uploadLimit' | 'deployLimit' | 'projectNums', + deductAmount = 1 +): Promise<{ success: boolean; remaining?: number; source?: 'annual' | 'fallback' }> { + if (!organizationId?.trim()) { + return { success: false } + } + + // First try annual subscription quota refresh and deduction + try { + const annualResult = await checkRefreshAndDeductQuota(organizationId, quotaType, deductAmount) + if (annualResult.success) { + log.subscription('info', 'Quota deducted from annual subscription via unified entry', { + organizationId, + quotaType, + deductAmount, + remaining: annualResult.remaining, + operation: 'unified_quota_deduction' + }) + return { + success: true, + remaining: annualResult.remaining, + source: 'annual' + } + } + } catch (error) { + log.subscription('warn', 'Annual quota deduction failed in unified entry, will use fallback', { + organizationId, + quotaType, + deductAmount, + error: error instanceof Error ? error.message : 'Unknown error', + operation: 'unified_quota_deduction' + }) + } + + // Return indication that fallback logic should be used + return { + success: false, + source: 'fallback' + } +} diff --git a/packages/auth/utils/subscription-limits/core.ts b/packages/auth/utils/subscription-limits/core.ts index 4b3061c..960596c 100644 --- a/packages/auth/utils/subscription-limits/core.ts +++ b/packages/auth/utils/subscription-limits/core.ts @@ -26,6 +26,7 @@ import { and, eq, sql } from 'drizzle-orm' import { getPlanLimits } from './constants' import { log, tryCatch, withDatabaseErrorHandling } from '@libra/common' import { getCloudflareContext } from '@opennextjs/cloudflare' +import { checkRefreshAndDeductQuota, checkAndRefreshAnnualQuota } from './annual-quota-refresh' import { PLAN_TYPES, @@ -68,7 +69,8 @@ export async function createOrUpdateSubscriptionLimit( plan: string, periodStart: Date, periodEnd: Date, - customLimits?: { aiNums?: number; seats?: number; projectNums?: number } + customLimits?: { aiNums?: number; seats?: number; projectNums?: number }, + billingInterval?: 'month' | 'year' ) { log.subscription('info', 'Creating/updating subscription limit', { organizationId, @@ -169,6 +171,8 @@ export async function createOrUpdateSubscriptionLimit( isActive: true, periodStart: utcPeriodStart.toISOString(), periodEnd: utcPeriodEnd.toISOString(), + billingInterval: 'month', // FREE plans are always monthly + lastQuotaRefresh: utcPeriodStart.toISOString(), }) log.subscription('info', 'Created new FREE plan', { @@ -207,6 +211,8 @@ export async function createOrUpdateSubscriptionLimit( isActive: true, periodStart: utcPeriodStart.toISOString(), periodEnd: utcPeriodEnd.toISOString(), + billingInterval: billingInterval || 'month', + lastQuotaRefresh: utcPeriodStart.toISOString(), }) // Note: No onConflict needed here because we deactivated existing plans first // Partial unique index will prevent duplicate active records @@ -259,7 +265,27 @@ export async function checkAndUpdateAIMessageUsage(organizationId: string): Prom return true } - // FALLBACK PATH: Try paid plan deduction only if FREE plan exhausted + // ANNUAL SUBSCRIPTION CHECK: Check and refresh annual subscription quota if needed + // This must happen before attempting paid plan deduction + try { + const annualRefreshResult = await checkRefreshAndDeductQuota(organizationId, 'aiNums', 1) + if (annualRefreshResult.success) { + log.subscription('info', 'AI message deducted from annual subscription (with refresh check)', { + organizationId, + remaining: annualRefreshResult.remaining, + operation: 'ai_message_deduction' + }); + return true + } + } catch (error) { + log.subscription('warn', 'Annual quota refresh check failed, falling back to regular paid plan', { + organizationId, + error: error instanceof Error ? error.message : 'Unknown error', + operation: 'ai_message_deduction' + }); + } + + // FALLBACK PATH: Try paid plan deduction only if FREE plan exhausted and annual check failed // This ensures paid users can continue using AI after free quota is used const paidDeductionResult = await attemptPaidPlanDeduction(db, organizationId, now) if (paidDeductionResult.success) { @@ -416,7 +442,27 @@ export async function checkAndUpdateEnhanceUsage(organizationId: string): Promis return true } - // FALLBACK PATH: Try paid plan deduction only if FREE plan exhausted + // ANNUAL SUBSCRIPTION CHECK: Check and refresh annual subscription quota if needed + // This must happen before attempting paid plan deduction + try { + const annualRefreshResult = await checkRefreshAndDeductQuota(organizationId, 'enhanceNums', 1) + if (annualRefreshResult.success) { + log.subscription('info', 'Enhance deducted from annual subscription (with refresh check)', { + organizationId, + remaining: annualRefreshResult.remaining, + operation: 'enhance_deduction' + }); + return true + } + } catch (error) { + log.subscription('warn', 'Annual quota refresh check failed, falling back to regular paid plan', { + organizationId, + error: error instanceof Error ? error.message : 'Unknown error', + operation: 'enhance_deduction' + }); + } + + // FALLBACK PATH: Try paid plan deduction only if FREE plan exhausted and annual check failed // This ensures paid users can continue using enhance features after free quota is used const paidDeductionResult = await attemptPaidPlanEnhanceDeduction(db, organizationId, now) if (paidDeductionResult.success) { @@ -643,6 +689,18 @@ export async function getSubscriptionUsage(organizationId: string): Promise statement-breakpoint +ALTER TABLE "subscription_limit" ADD COLUMN "last_quota_refresh" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "subscription_limit" ADD COLUMN "billing_interval" text DEFAULT 'month'; \ No newline at end of file diff --git a/packages/db/drizzle/meta/0001_snapshot.json b/packages/db/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000..6bb0f88 --- /dev/null +++ b/packages/db/drizzle/meta/0001_snapshot.json @@ -0,0 +1,654 @@ +{ + "id": "54750253-bbed-455a-9b0e-83f77e7b8ce2", + "prevId": "b57ae172-f96d-41d9-9ae7-b1cef4cc977d", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.components": { + "name": "components", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "component_slug": { + "name": "component_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "compiled_css": { + "name": "compiled_css", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "component_names": { + "name": "component_names", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "demo_code": { + "name": "demo_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "demo_dependencies": { + "name": "demo_dependencies", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "demo_direct_registry_dependencies": { + "name": "demo_direct_registry_dependencies", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "dependencies": { + "name": "dependencies", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "direct_registry_dependencies": { + "name": "direct_registry_dependencies", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "global_css_extension": { + "name": "global_css_extension", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tailwind_config_extension": { + "name": "tailwind_config_extension", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "downloads_count": { + "name": "downloads_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "likes_count": { + "name": "likes_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_paid": { + "name": "is_paid", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "payment_url": { + "name": "payment_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "preview_url": { + "name": "preview_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "components_component_slug_unique": { + "name": "components_component_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "component_slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_type": { + "name": "template_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "git_url": { + "name": "git_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "git_branch": { + "name": "git_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "preview_image_url": { + "name": "preview_image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "production_deploy_url": { + "name": "production_deploy_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deployment_status": { + "name": "deployment_status", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'idle'" + }, + "custom_domain": { + "name": "custom_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "custom_domain_status": { + "name": "custom_domain_status", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "custom_domain_verified_at": { + "name": "custom_domain_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "custom_hostname_id": { + "name": "custom_hostname_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ownership_verification": { + "name": "ownership_verification", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ssl_status": { + "name": "ssl_status", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "visibility": { + "name": "visibility", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "container_id": { + "name": "container_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "initial_message": { + "name": "initial_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "knowledge": { + "name": "knowledge", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "message_history": { + "name": "message_history", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "project_id_unique": { + "name": "project_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_ai_usage": { + "name": "project_ai_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "total_ai_message_count": { + "name": "total_ai_message_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "CURRENT_TIMESTAMP" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_ai_usage_project_id_project_id_fk": { + "name": "project_ai_usage_project_id_project_id_fk", + "tableFrom": "project_ai_usage", + "tableTo": "project", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_asset": { + "name": "project_asset", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan_id": { + "name": "plan_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "attachment_key": { + "name": "attachment_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_asset_project_id_project_id_fk": { + "name": "project_asset_project_id_project_id_fk", + "tableFrom": "project_asset", + "tableTo": "project", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscription_limit": { + "name": "subscription_limit", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan_name": { + "name": "plan_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan_id": { + "name": "plan_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ai_nums": { + "name": "ai_nums", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "enhance_nums": { + "name": "enhance_nums", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "upload_limit": { + "name": "upload_limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "deploy_limit": { + "name": "deploy_limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "seats": { + "name": "seats", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "project_nums": { + "name": "project_nums", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "period_start": { + "name": "period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "period_end": { + "name": "period_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_quota_refresh": { + "name": "last_quota_refresh", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'month'" + } + }, + "indexes": { + "subscription_limit_org_plan_active_idx": { + "name": "subscription_limit_org_plan_active_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "plan_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"subscription_limit\".\"is_active\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json index e6b22da..c96dd86 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1752239997146, "tag": "0000_smooth_hardball", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1756822871709, + "tag": "0001_sticky_blade", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/package.json b/packages/db/package.json index a1677ce..9feb66d 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@libra/db", - "version": "1.0.0", + "version": "1.0.1", "private": true, "description": "Database schemas and utilities for Libra", "main": "index.ts", diff --git a/packages/db/schema/project-schema.ts b/packages/db/schema/project-schema.ts index a54cd94..f511b39 100644 --- a/packages/db/schema/project-schema.ts +++ b/packages/db/schema/project-schema.ts @@ -134,6 +134,13 @@ export const subscriptionLimit = pgTable('subscription_limit', { .defaultNow() .notNull() .$onUpdate(() => sql`now()`), + // Track last quota refresh time for annual subscriptions + lastQuotaRefresh: timestamp('last_quota_refresh', { + withTimezone: true, + mode: 'string' + }), + // Track billing interval: 'month' or 'year' + billingInterval: text('billing_interval').default('month'), }, (table) => ({ // Partial unique constraint: only active plans must be unique per organization // Allows multiple inactive (historical) records for the same org+plan combination From 1eefa958e86ddafe918bdbd3b3b67c34fe6d7f13 Mon Sep 17 00:00:00 2001 From: tianzx Date: Wed, 3 Sep 2025 15:36:05 +0800 Subject: [PATCH 2/2] chore: update dependencies and improve subscription handling logic on branch 0903 - Update dependency versions across multiple packages and lockfiles - Adjust subscription handling to skip quota refresh for free plans - Use `Math.floor` for monthly period calculation in billing logic --- apps/builder/package.json | 8 +- apps/cdn/package.json | 2 +- apps/deploy-workflow/package.json | 2 +- apps/deploy/package.json | 2 +- apps/dispatcher/package.json | 6 +- apps/docs/package.json | 6 +- apps/screenshot/package.json | 2 +- apps/web/package.json | 14 +- bun.lock | 212 ++++++++++-------- package.json | 6 +- .../plugins/stripe/subscription-lifecycle.ts | 2 +- .../plugins/stripe/subscription-updates.ts | 2 +- .../auth/utils/subscription-limits/core.ts | 4 +- packages/better-auth-cloudflare/package.json | 4 +- packages/shikicode/package.json | 2 +- 15 files changed, 146 insertions(+), 128 deletions(-) diff --git a/apps/builder/package.json b/apps/builder/package.json index 16ca526..d6b8075 100644 --- a/apps/builder/package.json +++ b/apps/builder/package.json @@ -46,8 +46,8 @@ "tailwind-merge": "^3.3.1", "zod": "^4.1.5", "@hookform/resolvers": "^5.2.1", - "hono": "^4.9.5", - "@tanstack/react-query": "^5.85.6", + "hono": "^4.9.6", + "@tanstack/react-query": "^5.85.9", "recharts": "^2.15.4", "vaul": "^1.1.2", "react": "^19.1.1", @@ -69,8 +69,8 @@ "tailwindcss-animate": "^1.0.7", "typescript": "^5.9.2", "vite": "^6.3.5", - "@cloudflare/vite-plugin": "^1.12.1", + "@cloudflare/vite-plugin": "^1.12.2", "wrangler": "4.27.0", - "@cloudflare/workers-types": "^4.20250902.0" + "@cloudflare/workers-types": "^4.20250903.0" } } diff --git a/apps/cdn/package.json b/apps/cdn/package.json index ce684b9..a5228f8 100644 --- a/apps/cdn/package.json +++ b/apps/cdn/package.json @@ -13,7 +13,7 @@ "@libra/db": "*", "@libra/middleware": "*", "@libra/typescript-config": "*", - "hono": "^4.9.5", + "hono": "^4.9.6", "@scalar/hono-api-reference": "^0.9.16", "@hono/zod-openapi": "^0.19.10" }, diff --git a/apps/deploy-workflow/package.json b/apps/deploy-workflow/package.json index 6175585..5c869cc 100644 --- a/apps/deploy-workflow/package.json +++ b/apps/deploy-workflow/package.json @@ -16,7 +16,7 @@ "@libra/sandbox": "*", "@libra/templates": "*", "@libra/typescript-config": "*", - "hono": "^4.9.5", + "hono": "^4.9.6", "@scalar/hono-api-reference": "^0.9.16", "@hono/zod-openapi": "^0.19.10" }, diff --git a/apps/deploy/package.json b/apps/deploy/package.json index abaaff4..f538180 100644 --- a/apps/deploy/package.json +++ b/apps/deploy/package.json @@ -20,7 +20,7 @@ "@libra/sandbox": "*", "@libra/templates": "*", "@libra/typescript-config": "*", - "hono": "^4.9.5", + "hono": "^4.9.6", "@scalar/hono-api-reference": "^0.9.16", "@hono/zod-openapi": "^0.19.10", "zod": "^4.1.5", diff --git a/apps/dispatcher/package.json b/apps/dispatcher/package.json index 6c6e5ba..668b156 100644 --- a/apps/dispatcher/package.json +++ b/apps/dispatcher/package.json @@ -15,7 +15,7 @@ "@libra/db": "*", "@libra/common": "*", "@libra/middleware": "*", - "hono": "^4.9.5", + "hono": "^4.9.6", "pg": "^8.16.3", "zod": "^4.1.5", "@hono/zod-openapi": "^0.19.10", @@ -23,9 +23,9 @@ }, "devDependencies": { "wrangler": "4.27.0", - "@cloudflare/workers-types": "^4.20250902.0", + "@cloudflare/workers-types": "^4.20250903.0", "typescript": "^5.9.2", - "dotenv": "^17.2.1", + "dotenv": "^17.2.2", "dotenv-cli": "^10.0.0", "@libra/typescript-config": "*" } diff --git a/apps/docs/package.json b/apps/docs/package.json index e9d66de..032604d 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -9,9 +9,9 @@ "update": "bun update" }, "dependencies": { - "fumadocs-core": "^15.7.7", - "fumadocs-mdx": "^11.8.2", - "fumadocs-ui": "^15.7.7", + "fumadocs-core": "^15.7.8", + "fumadocs-mdx": "^11.8.3", + "fumadocs-ui": "^15.7.8", "motion": "^12.23.12", "octokit": "^5.0.3", "@orama/tokenizers": "^3.1.12", diff --git a/apps/screenshot/package.json b/apps/screenshot/package.json index 4489054..90c5c26 100644 --- a/apps/screenshot/package.json +++ b/apps/screenshot/package.json @@ -21,7 +21,7 @@ "@libra/sandbox": "*", "@libra/templates": "*", "@libra/typescript-config": "*", - "hono": "^4.9.5", + "hono": "^4.9.6", "@scalar/hono-api-reference": "^0.9.16", "@hono/zod-openapi": "^0.19.10", "zod": "^4.1.5", diff --git a/apps/web/package.json b/apps/web/package.json index 94b6632..e3fb72c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -27,7 +27,7 @@ "dependencies": { "@ai-sdk/anthropic": "^2.0.9", "@ai-sdk/azure": "^2.0.23", - "@ai-sdk/xai": "^2.0.14", + "@ai-sdk/xai": "^2.0.15", "@git-diff-view/file": "^0.0.30", "@git-diff-view/react": "^0.0.30", "@hookform/resolvers": "^5.2.1", @@ -45,13 +45,13 @@ "@lottiefiles/dotlottie-react": "^0.13.5", "@marsidev/react-turnstile": "^1.3.0", "@openrouter/ai-sdk-provider": "^1.1.2", - "@shikijs/transformers": "^3.12.1", - "@tanstack/react-query": "^5.85.6", + "@shikijs/transformers": "^3.12.2", + "@tanstack/react-query": "^5.85.9", "@tanstack/react-table": "^8.21.3", "@tsparticles/engine": "^3.9.1", "@tsparticles/react": "^3.0.0", "@tsparticles/slim": "^3.9.1", - "ai": "^5.0.29", + "ai": "^5.0.30", "date-fns": "^4.1.0", "diff": "^8.0.2", "fast-xml-parser": "^5.2.5", @@ -60,7 +60,7 @@ "motion": "^12.23.12", "next": "^15.5.2", "nextjs-toploader": "^3.8.16", - "posthog-js": "^1.261.0", + "posthog-js": "^1.261.4", "posthog-node": "^5.8.1", "react": "^19.1.1", "react-error-boundary": "^5.0.0", @@ -69,13 +69,13 @@ "react-markdown": "^10.1.0", "react-resizable-panels": "^3.0.5", "remark-gfm": "^4.0.1", - "shiki": "^3.12.1", + "shiki": "^3.12.2", "superjson": "^2.2.2", "zustand": "^5.0.8" }, "devDependencies": { "@types/diff": "^8.0.0", - "@tanstack/react-query-devtools": "^5.85.6", + "@tanstack/react-query-devtools": "^5.85.9", "@inlang/paraglide-js": "^2.2.0", "@inlang/cli": "^3.0.12", "@types/file-saver": "^2.0.7", diff --git a/bun.lock b/bun.lock index eea5fbb..7f27e5a 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,7 @@ "name": "libra", "dependencies": { "@octokit/rest": "^22.0.0", - "@opennextjs/cloudflare": "^1.7.0", + "@opennextjs/cloudflare": "^1.7.1", "better-auth": "^1.3.7", "drizzle-orm": "^0.44.5", "e2b": "1.2.0-beta.5", @@ -16,7 +16,7 @@ "@babel/runtime": "^8.0.0-beta.1", "@biomejs/biome": "^2.2.2", "@changesets/cli": "^2.29.6", - "@cloudflare/workers-types": "^4.20250902.0", + "@cloudflare/workers-types": "^4.20250903.0", "@t3-oss/env-nextjs": "^0.13.8", "@turbo/gen": "^2.5.6", "@types/node": "^24.3.0", @@ -24,7 +24,7 @@ "@types/react-dom": "^19.1.9", "babel-plugin-react-compiler": "^19.1.0-rc.1-rc-af1b7da-20250421", "changeset": "^0.2.6", - "dotenv": "^17.2.1", + "dotenv": "^17.2.2", "dotenv-cli": "^10.0.0", "drizzle-kit": "^0.31.4", "drizzle-seed": "^0.3.1", @@ -41,7 +41,7 @@ }, "apps/builder": { "name": "vite-shadcn-builder-libra", - "version": "1.0.9", + "version": "1.0.10", "dependencies": { "@hookform/resolvers": "^5.2.1", "@radix-ui/react-accordion": "^1.2.12", @@ -67,11 +67,11 @@ "@radix-ui/react-toast": "^1.2.15", "@radix-ui/react-tooltip": "^1.2.8", "@splinetool/react-spline": "^4.1.0", - "@tanstack/react-query": "^5.85.6", + "@tanstack/react-query": "^5.85.9", "class-variance-authority": "^0.7.1", "cmdk": "^0.2.1", "embla-carousel-react": "^8.6.0", - "hono": "^4.9.5", + "hono": "^4.9.6", "input-otp": "^1.4.2", "lucide-react": "^0.486.0", "motion": "^12.23.12", @@ -89,8 +89,8 @@ "zod": "^4.1.5", }, "devDependencies": { - "@cloudflare/vite-plugin": "^1.12.1", - "@cloudflare/workers-types": "^4.20250902.0", + "@cloudflare/vite-plugin": "^1.12.2", + "@cloudflare/workers-types": "^4.20250903.0", "@tailwindcss/vite": "^4.1.12", "@types/node": "^22.18.0", "@types/react": "^19.1.12", @@ -109,7 +109,7 @@ }, "apps/cdn": { "name": "@libra/cdn", - "version": "1.0.3", + "version": "1.0.5", "dependencies": { "@hono/zod-openapi": "^0.19.10", "@libra/auth": "*", @@ -117,7 +117,7 @@ "@libra/middleware": "*", "@libra/typescript-config": "*", "@scalar/hono-api-reference": "^0.9.16", - "hono": "^4.9.5", + "hono": "^4.9.6", }, "devDependencies": { "wrangler": "4.27.0", @@ -125,7 +125,7 @@ }, "apps/deploy": { "name": "@libra/deploy", - "version": "1.0.6", + "version": "1.0.8", "dependencies": { "@hono/zod-openapi": "^0.19.10", "@libra/auth": "*", @@ -137,7 +137,7 @@ "@libra/typescript-config": "*", "@scalar/hono-api-reference": "^0.9.16", "drizzle-orm": "^0.44.5", - "hono": "^4.9.5", + "hono": "^4.9.6", "zod": "^4.1.5", }, "devDependencies": { @@ -150,7 +150,7 @@ }, "apps/deploy-workflow": { "name": "@libra/deploy-workflow", - "version": "1.0.3", + "version": "1.0.4", "dependencies": { "@hono/zod-openapi": "^0.19.10", "@libra/auth": "*", @@ -161,7 +161,7 @@ "@libra/templates": "*", "@libra/typescript-config": "*", "@scalar/hono-api-reference": "^0.9.16", - "hono": "^4.9.5", + "hono": "^4.9.6", }, "devDependencies": { "wrangler": "4.27.0", @@ -169,7 +169,7 @@ }, "apps/dispatcher": { "name": "@libra/dispatcher", - "version": "1.0.9", + "version": "1.0.10", "dependencies": { "@hono/zod-openapi": "^0.19.10", "@libra/common": "*", @@ -177,14 +177,14 @@ "@libra/middleware": "*", "@scalar/hono-api-reference": "^0.9.16", "drizzle-orm": "^0.44.5", - "hono": "^4.9.5", + "hono": "^4.9.6", "pg": "^8.16.3", "zod": "^4.1.5", }, "devDependencies": { - "@cloudflare/workers-types": "^4.20250902.0", + "@cloudflare/workers-types": "^4.20250903.0", "@libra/typescript-config": "*", - "dotenv": "^17.2.1", + "dotenv": "^17.2.2", "dotenv-cli": "^10.0.0", "typescript": "^5.9.2", "wrangler": "4.27.0", @@ -192,13 +192,13 @@ }, "apps/docs": { "name": "@libra/docs", - "version": "1.0.6", + "version": "1.0.7", "dependencies": { "@orama/orama": "^3.1.12", "@orama/tokenizers": "^3.1.12", - "fumadocs-core": "^15.7.7", - "fumadocs-mdx": "^11.8.2", - "fumadocs-ui": "^15.7.7", + "fumadocs-core": "^15.7.8", + "fumadocs-mdx": "^11.8.3", + "fumadocs-ui": "^15.7.8", "motion": "^12.23.12", "octokit": "^5.0.3", "zod": "^4.1.5", @@ -233,7 +233,7 @@ }, "apps/screenshot": { "name": "@libra/screenshot", - "version": "1.0.6", + "version": "1.0.7", "dependencies": { "@hono/zod-openapi": "^0.19.10", "@libra/auth": "*", @@ -245,7 +245,7 @@ "@libra/typescript-config": "*", "@scalar/hono-api-reference": "^0.9.16", "drizzle-orm": "^0.44.5", - "hono": "^4.9.5", + "hono": "^4.9.6", "zod": "^4.1.5", }, "devDependencies": { @@ -327,11 +327,11 @@ }, "apps/web": { "name": "libra-core", - "version": "1.0.14", + "version": "1.0.15", "dependencies": { "@ai-sdk/anthropic": "^2.0.9", "@ai-sdk/azure": "^2.0.23", - "@ai-sdk/xai": "^2.0.14", + "@ai-sdk/xai": "^2.0.15", "@git-diff-view/file": "^0.0.30", "@git-diff-view/react": "^0.0.30", "@hookform/resolvers": "^5.2.1", @@ -349,13 +349,13 @@ "@lottiefiles/dotlottie-react": "^0.13.5", "@marsidev/react-turnstile": "^1.3.0", "@openrouter/ai-sdk-provider": "^1.1.2", - "@shikijs/transformers": "^3.12.1", - "@tanstack/react-query": "^5.85.6", + "@shikijs/transformers": "^3.12.2", + "@tanstack/react-query": "^5.85.9", "@tanstack/react-table": "^8.21.3", "@tsparticles/engine": "^3.9.1", "@tsparticles/react": "^3.0.0", "@tsparticles/slim": "^3.9.1", - "ai": "^5.0.29", + "ai": "^5.0.30", "date-fns": "^4.1.0", "diff": "^8.0.2", "fast-xml-parser": "^5.2.5", @@ -364,7 +364,7 @@ "motion": "^12.23.12", "next": "^15.5.2", "nextjs-toploader": "^3.8.16", - "posthog-js": "^1.261.0", + "posthog-js": "^1.261.4", "posthog-node": "^5.8.1", "react": "^19.1.1", "react-error-boundary": "^5.0.0", @@ -373,7 +373,7 @@ "react-markdown": "^10.1.0", "react-resizable-panels": "^3.0.5", "remark-gfm": "^4.0.1", - "shiki": "^3.12.1", + "shiki": "^3.12.2", "superjson": "^2.2.2", "zustand": "^5.0.8", }, @@ -381,7 +381,7 @@ "@inlang/cli": "^3.0.12", "@inlang/paraglide-js": "^2.2.0", "@next/bundle-analyzer": "15.4.2", - "@tanstack/react-query-devtools": "^5.85.6", + "@tanstack/react-query-devtools": "^5.85.9", "@types/diff": "^8.0.0", "@types/file-saver": "^2.0.7", }, @@ -403,7 +403,7 @@ }, "packages/auth": { "name": "@libra/auth", - "version": "1.0.2", + "version": "1.0.4", "dependencies": { "@libra/better-auth-cloudflare": "*", "@libra/better-auth-stripe": "*", @@ -421,7 +421,7 @@ }, "packages/better-auth-cloudflare": { "name": "@libra/better-auth-cloudflare", - "version": "1.0.7", + "version": "1.0.8", "dependencies": { "better-auth": "^1.3.7", }, @@ -429,13 +429,13 @@ "@libra/typescript-config": "*", }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20250902.0", - "@opennextjs/cloudflare": "^1.7.0", + "@cloudflare/workers-types": "^4.20250903.0", + "@opennextjs/cloudflare": "^1.7.1", }, }, "packages/better-auth-stripe": { "name": "@libra/better-auth-stripe", - "version": "1.0.2", + "version": "1.0.3", "dependencies": { "better-auth": "^1.3.7", }, @@ -446,7 +446,7 @@ }, "packages/common": { "name": "@libra/common", - "version": "1.0.1", + "version": "1.0.2", "dependencies": { "@trpc/client": "^11.5.0", "@trpc/react-query": "^11.5.0", @@ -460,7 +460,7 @@ }, "packages/db": { "name": "@libra/db", - "version": "1.0.0", + "version": "1.0.1", "dependencies": { "@libra/common": "*", "@paralleldrive/cuid2": "^2.2.2", @@ -473,7 +473,7 @@ }, "packages/email": { "name": "@libra/email", - "version": "1.0.3", + "version": "1.0.4", "dependencies": { "@react-email/components": "^0.3.3", "@react-email/tailwind": "^1.2.2", @@ -504,7 +504,7 @@ }, "packages/sandbox": { "name": "@libra/sandbox", - "version": "1.0.1", + "version": "1.0.2", "dependencies": { "@daytonaio/sdk": "^0.25.6", "@libra/common": "*", @@ -518,10 +518,10 @@ }, "packages/shikicode": { "name": "@libra/shikicode", - "version": "1.0.1", + "version": "1.0.2", "devDependencies": { "@libra/typescript-config": "*", - "shiki": "^3.12.1", + "shiki": "^3.12.2", }, }, "packages/templates": { @@ -533,7 +533,7 @@ }, "packages/ui": { "name": "@libra/ui", - "version": "1.0.2", + "version": "1.0.3", "dependencies": { "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-collapsible": "^1.1.12", @@ -589,13 +589,13 @@ "@ai-sdk/openai": ["@ai-sdk/openai@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-uOXk8HzmMUoCmD0JMX/Y1HC/ABOR/Jza2Z2rkCaJISDYz3fp5pnb6eNjcPRL48JSMzRAGp9UP5p0OpxS06IJZg=="], - "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.13", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-g46fLVWKcVg1XOFzDLoJ0XuhtY5XxxBwMQ0FT/aHwCtg6WUvk3Elrd+MKmgfvhZAdIR7CpUTvgJAAipu4RW75w=="], + "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.14", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-ccSw++MROT3DSYrvZQaufhn9RQ26qAegG/rihRnzPb/bg+SIEd+hHd0oXCnYgRZUPv57SaHjvejglE30GHduyg=="], "@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.7", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA=="], - "@ai-sdk/xai": ["@ai-sdk/xai@2.0.14", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.13", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-7LSvjpOge4hP1emOnpq/txdC1eniJy5Dj3KyAawviiXnoUVVdZnWwvdaDtmJn+bMUi+RoJcYNPTXT7GD5wXckQ=="], + "@ai-sdk/xai": ["@ai-sdk/xai@2.0.15", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.14", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-JwkNeWquaHzzOCWQoWLrBaT0uYckJ6/djzXArYmIN6+ZGVSREcImxJ+5yeOQTrinEsPYE89/gtfmlCQarUDt2Q=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], @@ -873,7 +873,7 @@ "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.5.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.19", "workerd": "^1.20250722.0" }, "optionalPeers": ["workerd"] }, "sha512-CZe9B2VbjIQjBTyc+KoZcN1oUcm4T6GgCXoel9O7647djHuSRAa6sM6G+NdxWArATZgeMMbsvn9C50GCcnIatA=="], - "@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.12.1", "", { "dependencies": { "@cloudflare/unenv-preset": "2.7.0", "@remix-run/node-fetch-server": "^0.8.0", "get-port": "^7.1.0", "miniflare": "4.20250823.1", "picocolors": "^1.1.1", "tinyglobby": "^0.2.12", "unenv": "2.0.0-rc.19", "wrangler": "4.33.1", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0" } }, "sha512-KxBMOQRhUEVfHUJZElCWzax0wMihVXXnMTfF40ekQjZ+k7hd0EBBndUwmDoPRE3kSVTVuqSklF1b/Xu26NyzyA=="], + "@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.12.2", "", { "dependencies": { "@cloudflare/unenv-preset": "2.7.1", "@remix-run/node-fetch-server": "^0.8.0", "get-port": "^7.1.0", "miniflare": "4.20250829.0", "picocolors": "^1.1.1", "tinyglobby": "^0.2.12", "unenv": "2.0.0-rc.19", "wrangler": "4.33.2", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0" } }, "sha512-iPDufX1DVYZU9ginc6d9mHmni/d93BHDHOkJXExlsrn7PTQYI45iN/c+NeON6lKBPpS5ruv0MCNvXNnjYkVM9Q=="], "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250730.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-X3egNyTjLQaECYe34x8Al7r4oXAhcN3a8+8qcpNCcq1sgtuHIeAwS9potgRR/mwkGfmrJn7nfAyDKC4vrkniQQ=="], @@ -885,7 +885,7 @@ "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250730.0", "", { "os": "win32", "cpu": "x64" }, "sha512-paVHgocuilMzOU+gEyKR/86j/yI+QzmSHRnqdd8OdQ37Hf6SyPX7kQj6VVNRXbzVHWix1WxaJsXfTGK1LK05wA=="], - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250902.0", "", {}, "sha512-V4GJTvaqoMvctCIkdx+Joq9uQsaOQ1PPsar783bUYL8+VNWJyK2me6ab1uDXeeFHmhb+26KT0hOrWHg80zC6DA=="], + "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250903.0", "", {}, "sha512-F6G3MG7EZxsJ2Fgsy3eed+U2fU/XGfKqDlyY/vCL/zUqI8KuaNx8GVnxttqzktBY55HK3xe82Zpj8xujW6PfXw=="], "@connectrpc/connect": ["@connectrpc/connect@2.0.0-rc.3", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.2.0" } }, "sha512-ARBt64yEyKbanyRETTjcjJuHr2YXorzQo0etyS5+P6oSeW8xEuzajA9g+zDnMcj1hlX2dQE93foIWQGfpru7gQ=="], @@ -1241,7 +1241,7 @@ "@opennextjs/aws": ["@opennextjs/aws@3.7.6", "", { "dependencies": { "@ast-grep/napi": "^0.35.0", "@aws-sdk/client-cloudfront": "3.398.0", "@aws-sdk/client-dynamodb": "^3.398.0", "@aws-sdk/client-lambda": "^3.398.0", "@aws-sdk/client-s3": "^3.398.0", "@aws-sdk/client-sqs": "^3.398.0", "@node-minify/core": "^8.0.6", "@node-minify/terser": "^8.0.6", "@tsconfig/node18": "^1.0.1", "aws4fetch": "^1.0.18", "chalk": "^5.3.0", "cookie": "^1.0.2", "esbuild": "0.25.4", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0", "yaml": "^2.7.0" }, "bin": { "open-next": "dist/index.js" } }, "sha512-l4UGkmaZaAjWER+PuZ/zNziMnrbf6oA9B1RUDKcyKsX+hEES1b7h6kLgpo4a4maf01M+k0yJUZQyF4sf+vI+Iw=="], - "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.7.0", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.7.6", "cloudflare": "^4.4.1", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6", "yargs": "^18.0.0" }, "peerDependencies": { "wrangler": "^4.24.4" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-0GoM7v8ODsmrvZ7JK+EaK63XL6athLwSS+jWmcfxHrJNd9XJpWVHucdwAbRZl3jfkW6padeN1vw7SLnayiwHqQ=="], + "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.7.1", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.7.6", "cloudflare": "^4.4.1", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6", "yargs": "^18.0.0" }, "peerDependencies": { "wrangler": "^4.24.4" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-g3d8LSpGGJzuCLuLYHAs7nGWFKK57qSOgxhpn3wwk6t/tXxsneXA3D5nbf6zLsAQQo8b6A8LDcT732U3rUXUeA=="], "@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@1.1.2", "", { "peerDependencies": { "ai": "^5.0.0", "zod": "^3.24.1 || ^v4" } }, "sha512-cfiKVpNygGFaJojBHFvtTf7UiF458Xh9yPcTg4FXF7bGYN5V33Rxx9dXNE12fjv6lHeC5C7jwQHDrzUIFol1iQ=="], @@ -1549,21 +1549,21 @@ "@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="], - "@shikijs/core": ["@shikijs/core@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-j9+UDQ6M50xvaSR/e9lg212H0Fqxy3lYd39Q6YITYQxfrb5VYNUKPLZp4PN9f+YmRcdpyNAm3obn/tIZ2WkUWg=="], + "@shikijs/core": ["@shikijs/core@3.12.2", "", { "dependencies": { "@shikijs/types": "3.12.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-L1Safnhra3tX/oJK5kYHaWmLEBJi1irASwewzY3taX5ibyXyMkkSDZlq01qigjryOBwrXSdFgTiZ3ryzSNeu7Q=="], - "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-mwif5T3rEBSMn/1m9dNi4WmB4dxH4VfYqreQMLpbFYov8MM3Gus98I549amFMjtEmYDAkTKGP7bmsv1n9t9I+A=="], + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.12.2", "", { "dependencies": { "@shikijs/types": "3.12.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Nm3/azSsaVS7hk6EwtHEnTythjQfwvrO5tKqMlaH9TwG1P+PNaR8M0EAKZ+GaH2DFwvcr4iSfTveyxMIvXEHMw=="], - "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-hbYq+XOc55CU7Irkhsgwh8WgQbx2W5IVzHV4l+wZ874olMLSNg5o3F73vo9m4SAhimFyqq/86xnx9h+T30HhhQ=="], + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.12.2", "", { "dependencies": { "@shikijs/types": "3.12.2", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w=="], - "@shikijs/langs": ["@shikijs/langs@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1" } }, "sha512-Y1MbMfVO5baRz7Boo7EoD36TmzfUx/I5n8e+wZumx6SlUA81Zj1ZwNJL871iIuSHrdsheV4AxJtHQ9mlooklmg=="], + "@shikijs/langs": ["@shikijs/langs@3.12.2", "", { "dependencies": { "@shikijs/types": "3.12.2" } }, "sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww=="], "@shikijs/rehype": ["@shikijs/rehype@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1", "@types/hast": "^3.0.4", "hast-util-to-string": "^3.0.1", "shiki": "3.12.1", "unified": "^11.0.5", "unist-util-visit": "^5.0.0" } }, "sha512-MEWt7qNyvlzVsQAf6WWTX+EN5IxRW3VUfzxrmQQHOUXjGePjQcVpHuaapW/BxgUX5hNhXKUtBVR7KMwY3ASLjA=="], - "@shikijs/themes": ["@shikijs/themes@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1" } }, "sha512-9JrAm9cA5hqM/YXymA3oAAZdnCgQf1zyrNDtsnM105nNEoEpux4dyzdoOjc2KawEKj1iUs/WH2ota6Atp7GYkQ=="], + "@shikijs/themes": ["@shikijs/themes@3.12.2", "", { "dependencies": { "@shikijs/types": "3.12.2" } }, "sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A=="], - "@shikijs/transformers": ["@shikijs/transformers@3.12.1", "", { "dependencies": { "@shikijs/core": "3.12.1", "@shikijs/types": "3.12.1" } }, "sha512-crGh3cSZf6mwg3K2W8i79Ja+q4tVClRHdHLnUGi5arS58+cqdzsbkrEZBDMyevf9ehmjFUWDTEwCMEyp9I3z0g=="], + "@shikijs/transformers": ["@shikijs/transformers@3.12.2", "", { "dependencies": { "@shikijs/core": "3.12.2", "@shikijs/types": "3.12.2" } }, "sha512-+z1aMq4N5RoNGY8i7qnTYmG2MBYzFmwkm/yOd6cjEI7OVzcldVvzQCfxU1YbIVgsyB0xHVc2jFe1JhgoXyUoSQ=="], - "@shikijs/types": ["@shikijs/types@3.12.1", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-Is/p+1vTss22LIsGCJTmGrxu7ZC1iBL9doJFYLaZ4aI8d0VDXb7Mn0kBzhkc7pdsRpmUbQLQ5HXwNpa3H6F8og=="], + "@shikijs/types": ["@shikijs/types@3.12.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q=="], "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], @@ -1753,13 +1753,13 @@ "@tailwindcss/vite": ["@tailwindcss/vite@4.1.12", "", { "dependencies": { "@tailwindcss/node": "4.1.12", "@tailwindcss/oxide": "4.1.12", "tailwindcss": "4.1.12" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ=="], - "@tanstack/query-core": ["@tanstack/query-core@5.85.6", "", {}, "sha512-hCj0TktzdCv2bCepIdfwqVwUVWb+GSHm1Jnn8w+40lfhQ3m7lCO7ADRUJy+2unxQ/nzjh2ipC6ye69NDW3l73g=="], + "@tanstack/query-core": ["@tanstack/query-core@5.85.9", "", {}, "sha512-5fxb9vwyftYE6KFLhhhDyLr8NO75+Wpu7pmTo+TkwKmMX2oxZDoLwcqGP8ItKSpUMwk3urWgQDZfyWr5Jm9LsQ=="], "@tanstack/query-devtools": ["@tanstack/query-devtools@5.84.0", "", {}, "sha512-fbF3n+z1rqhvd9EoGp5knHkv3p5B2Zml1yNRjh7sNXklngYI5RVIWUrUjZ1RIcEoscarUb0+bOvIs5x9dwzOXQ=="], - "@tanstack/react-query": ["@tanstack/react-query@5.85.6", "", { "dependencies": { "@tanstack/query-core": "5.85.6" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-VUAag4ERjh+qlmg0wNivQIVCZUrYndqYu3/wPCVZd4r0E+1IqotbeyGTc+ICroL/PqbpSaGZg02zSWYfcvxbdA=="], + "@tanstack/react-query": ["@tanstack/react-query@5.85.9", "", { "dependencies": { "@tanstack/query-core": "5.85.9" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-2T5zgSpcOZXGkH/UObIbIkGmUPQqZqn7esVQFXLOze622h4spgWf5jmvrqAo9dnI13/hyMcNsF1jsoDcb59nJQ=="], - "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.85.6", "", { "dependencies": { "@tanstack/query-devtools": "5.84.0" }, "peerDependencies": { "@tanstack/react-query": "^5.85.6", "react": "^18 || ^19" } }, "sha512-A6rE39FypFV7eonefk4fxC/vuV/7YJMAcQT94CFAvCpiw65QZX8MOuUpdLBeG1cXajy4Pj8T8sEWHigccntJqg=="], + "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.85.9", "", { "dependencies": { "@tanstack/query-devtools": "5.84.0" }, "peerDependencies": { "@tanstack/react-query": "^5.85.9", "react": "^18 || ^19" } }, "sha512-BAdhgwpzxkC1vdyCfiPbbC7FU/t/x6q2d9ZyhON/WykVUdznD69nlppuWpSIlIGipdRG7sF6tRZ6x3GtSq0EUQ=="], "@tanstack/react-table": ["@tanstack/react-table@8.21.3", "", { "dependencies": { "@tanstack/table-core": "8.21.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww=="], @@ -2027,7 +2027,7 @@ "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], - "ai": ["ai@5.0.29", "", { "dependencies": { "@ai-sdk/gateway": "1.0.15", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-jA/d6X5hn3r/PxgZjwzDUMJiEkLBIVVD2gcbpcT/FD4MSLxm5sn6fH1y2VFXVgBEd95mNzQ8ALQubysc6E8Y9g=="], + "ai": ["ai@5.0.30", "", { "dependencies": { "@ai-sdk/gateway": "1.0.15", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.7", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-u7WdsDde9BasP+h8Q64CtU32GFShCYmxVtBa2h5dxM1f0w/AMKwzpmIDI1t3M3ean+L6uBiwOtRs8B2KA+OHgQ=="], "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], @@ -2357,7 +2357,7 @@ "dot-case": ["dot-case@2.1.1", "", { "dependencies": { "no-case": "^2.2.0" } }, "sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug=="], - "dotenv": ["dotenv@17.2.1", "", {}, "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ=="], + "dotenv": ["dotenv@17.2.2", "", {}, "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q=="], "dotenv-cli": ["dotenv-cli@10.0.0", "", { "dependencies": { "cross-spawn": "^7.0.6", "dotenv": "^17.1.0", "dotenv-expand": "^11.0.0", "minimist": "^1.2.6" }, "bin": { "dotenv": "cli.js" } }, "sha512-lnOnttzfrzkRx2echxJHQRB6vOAMSCzzZg79IxpC00tU42wZPuZkQxNNrrwVAxaQZIIh001l4PxVlCrBxngBzA=="], @@ -2561,11 +2561,11 @@ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "fumadocs-core": ["fumadocs-core@15.7.7", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.6.1", "@orama/orama": "^3.1.12", "@shikijs/rehype": "^3.12.0", "@shikijs/transformers": "^3.12.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "image-size": "^2.0.2", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "react-remove-scroll": "^2.7.1", "remark": "^15.0.0", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^3.12.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@mixedbread/sdk": "^0.19.0", "@oramacloud/client": "1.x.x || 2.x.x", "@types/react": "*", "algoliasearch": "5.x.x", "next": "14.x.x || 15.x.x", "react": "18.x.x || 19.x.x", "react-dom": "18.x.x || 19.x.x", "react-router": "7.x.x" }, "optionalPeers": ["@mixedbread/sdk", "@oramacloud/client", "@types/react", "algoliasearch", "next", "react", "react-dom", "react-router"] }, "sha512-4mo8y1L2VV9TcrQ1gses3c5zzCaPwDPYjfrPET4Qf+m7GPOqZ7wiUeXMTYb98T+N5wS0G/fsr/xFPZkgwD44gQ=="], + "fumadocs-core": ["fumadocs-core@15.7.8", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.6.1", "@orama/orama": "^3.1.12", "@shikijs/rehype": "^3.12.0", "@shikijs/transformers": "^3.12.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "image-size": "^2.0.2", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "react-remove-scroll": "^2.7.1", "remark": "^15.0.0", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^3.12.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@mixedbread/sdk": "^0.19.0", "@oramacloud/client": "1.x.x || 2.x.x", "@tanstack/react-router": "1.x.x", "@types/react": "*", "algoliasearch": "5.x.x", "next": "14.x.x || 15.x.x", "react": "18.x.x || 19.x.x", "react-dom": "18.x.x || 19.x.x", "react-router": "7.x.x", "waku": "^0.26.0" }, "optionalPeers": ["@mixedbread/sdk", "@oramacloud/client", "@tanstack/react-router", "@types/react", "algoliasearch", "next", "react", "react-dom", "react-router", "waku"] }, "sha512-/yNfcOCOKVBhadlSr8qwz3OWF8a8dtLmLx8xbH9rBXy+qHP3EHue2mrfCNbc6yEU0KOvCEepxbpK48rqFsC6uA=="], - "fumadocs-mdx": ["fumadocs-mdx@11.8.2", "", { "dependencies": { "@mdx-js/mdx": "^3.1.0", "@standard-schema/spec": "^1.0.0", "chokidar": "^4.0.3", "esbuild": "^0.25.9", "estree-util-value-to-estree": "^3.4.0", "js-yaml": "^4.1.0", "lru-cache": "^11.1.0", "picocolors": "^1.1.1", "remark-mdx": "^3.1.0", "remark-parse": "^11.0.0", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "zod": "^4.1.4" }, "peerDependencies": { "@fumadocs/mdx-remote": "^1.4.0", "fumadocs-core": "^14.0.0 || ^15.0.0", "next": "^15.3.0", "react": "*", "vite": "6.x.x || 7.x.x" }, "optionalPeers": ["@fumadocs/mdx-remote", "next", "react", "vite"], "bin": { "fumadocs-mdx": "bin.js" } }, "sha512-omv2jNrdXfZiAC2p6YeO3y3GYyQesMRzvsCDdgf7L/MbPIfgvtPkmRG2kL42dFEVsi45zwj0UugX6AG5IhDuUQ=="], + "fumadocs-mdx": ["fumadocs-mdx@11.8.3", "", { "dependencies": { "@mdx-js/mdx": "^3.1.1", "@standard-schema/spec": "^1.0.0", "chokidar": "^4.0.3", "esbuild": "^0.25.9", "estree-util-value-to-estree": "^3.4.0", "js-yaml": "^4.1.0", "lru-cache": "^11.1.0", "picocolors": "^1.1.1", "remark-mdx": "^3.1.1", "remark-parse": "^11.0.0", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "zod": "^4.1.5" }, "peerDependencies": { "@fumadocs/mdx-remote": "^1.4.0", "fumadocs-core": "^14.0.0 || ^15.0.0", "next": "^15.3.0", "react": "*", "vite": "6.x.x || 7.x.x" }, "optionalPeers": ["@fumadocs/mdx-remote", "next", "react", "vite"], "bin": { "fumadocs-mdx": "bin.js" } }, "sha512-ZHdWaeSHUq/5ZpfMOjClFc4ZVViMlA4RwfvauHH7Byz/wZIQhhRDXTuEcQumakOliytXXArua/BQFYmbNUhooA=="], - "fumadocs-ui": ["fumadocs-ui@15.7.7", "", { "dependencies": { "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-direction": "^1.1.1", "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-presence": "^1.1.5", "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tabs": "^1.1.13", "class-variance-authority": "^0.7.1", "fumadocs-core": "15.7.7", "lodash.merge": "^4.6.2", "next-themes": "^0.4.6", "postcss-selector-parser": "^7.1.0", "react-medium-image-zoom": "^5.3.0", "scroll-into-view-if-needed": "^3.1.0", "tailwind-merge": "^3.3.1" }, "peerDependencies": { "@types/react": "*", "next": "14.x.x || 15.x.x", "react": "18.x.x || 19.x.x", "react-dom": "18.x.x || 19.x.x", "tailwindcss": "^3.4.14 || ^4.0.0" }, "optionalPeers": ["@types/react", "next", "tailwindcss"] }, "sha512-nXeEnFI0h+JAbwWsKWcc6aBuR++jWlxhMpXQnPv4zbrrbds436lilrOu/xh5KxPiEe2M9HspKMN+Oee73jHQFw=="], + "fumadocs-ui": ["fumadocs-ui@15.7.8", "", { "dependencies": { "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-direction": "^1.1.1", "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-presence": "^1.1.5", "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tabs": "^1.1.13", "class-variance-authority": "^0.7.1", "fumadocs-core": "15.7.8", "lodash.merge": "^4.6.2", "next-themes": "^0.4.6", "postcss-selector-parser": "^7.1.0", "react-medium-image-zoom": "^5.3.0", "scroll-into-view-if-needed": "^3.1.0", "tailwind-merge": "^3.3.1" }, "peerDependencies": { "@types/react": "*", "next": "14.x.x || 15.x.x", "react": "18.x.x || 19.x.x", "react-dom": "18.x.x || 19.x.x", "tailwindcss": "^3.4.14 || ^4.0.0" }, "optionalPeers": ["@types/react", "next", "tailwindcss"] }, "sha512-7SfD2V/9qvEoR1WwM9PF9LrKqVyvscXetQ8+lmWOAIBD79m/5lzDsmUUVZzY+LJ+nax24c30tlW+Rd+2bKzxSw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -2645,7 +2645,7 @@ "homedir-polyfill": ["homedir-polyfill@1.0.3", "", { "dependencies": { "parse-passwd": "^1.0.0" } }, "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA=="], - "hono": ["hono@4.9.5", "", {}, "sha512-aLAVl5/67ifNnoFVxnhR89dpmSLsgwBprw/PT671ASwUpJqmd7Ne8KPTQo37DbRZfgpHaHeZ4bPVUvbOkeedMw=="], + "hono": ["hono@4.9.6", "", {}, "sha512-doVjXhSFvYZ7y0dNokjwwSahcrAfdz+/BCLvAMa/vHLzjj8+CFyV5xteThGUsKdkaasgN+gF2mUxao+SGLpUeA=="], "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], @@ -3257,7 +3257,7 @@ "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], - "posthog-js": ["posthog-js@1.261.0", "", { "dependencies": { "@posthog/core": "1.0.2", "core-js": "^3.38.1", "fflate": "^0.4.8", "preact": "^10.19.3", "web-vitals": "^4.2.4" }, "peerDependencies": { "@rrweb/types": "2.0.0-alpha.17", "rrweb-snapshot": "2.0.0-alpha.17" }, "optionalPeers": ["@rrweb/types", "rrweb-snapshot"] }, "sha512-jyiXqyrCU+VlpbNNVRA6OQYAVut0XZMYNELCZH+XvTd981VqbE4jXn4XCBreo7XCL2gdPgDVxUVOuzNvEuKcmw=="], + "posthog-js": ["posthog-js@1.261.4", "", { "dependencies": { "@posthog/core": "1.0.2", "core-js": "^3.38.1", "fflate": "^0.4.8", "preact": "^10.19.3", "web-vitals": "^4.2.4" }, "peerDependencies": { "@rrweb/types": "2.0.0-alpha.17", "rrweb-snapshot": "2.0.0-alpha.17" }, "optionalPeers": ["@rrweb/types", "rrweb-snapshot"] }, "sha512-+XDKBO3tuwSJEdiO+XbHg3xVygI94rriI3SAGkgb3soJmDfjLes/zhb5EI4CSPQZbxRxQQK9Knb9IJ+pIyJgGw=="], "posthog-node": ["posthog-node@5.8.1", "", { "dependencies": { "@posthog/core": "1.0.2" } }, "sha512-YJYlYnlpItVjHqM9IhvZx8TzK8gnx2nU+0uhiog4RN47NnV0Z0K1AdC4ul+O8VuvS/jHqKCQvL8iAONRA37+0A=="], @@ -3483,7 +3483,7 @@ "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], - "shiki": ["shiki@3.12.1", "", { "dependencies": { "@shikijs/core": "3.12.1", "@shikijs/engine-javascript": "3.12.1", "@shikijs/engine-oniguruma": "3.12.1", "@shikijs/langs": "3.12.1", "@shikijs/themes": "3.12.1", "@shikijs/types": "3.12.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-eMlxVaXyuNQAQCaMtDKQjKv0eVm+kA6fsZtv9UqKgspP+7lWCVi7SoN+cJq1dawvIDQY7TI3SixamztotM6R6Q=="], + "shiki": ["shiki@3.12.2", "", { "dependencies": { "@shikijs/core": "3.12.2", "@shikijs/engine-javascript": "3.12.2", "@shikijs/engine-oniguruma": "3.12.2", "@shikijs/langs": "3.12.2", "@shikijs/themes": "3.12.2", "@shikijs/types": "3.12.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-uIrKI+f9IPz1zDT+GMz+0RjzKJiijVr6WDWm9Pe3NNY6QigKCfifCEv9v9R2mDASKKjzjQ2QpFLcxaR3iHSnMA=="], "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], @@ -4085,14 +4085,16 @@ "@changesets/write/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], - "@cloudflare/vite-plugin/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.19", "workerd": "^1.20250816.0" }, "optionalPeers": ["workerd"] }, "sha512-0JbEj+KTCQ4nTIWg2q8Bou+fPxzG6/zwU5O/w6Cld6WEjLl+716foT+2bjg48h09hMtjTKkJdAh1m4LybBKGCg=="], + "@cloudflare/vite-plugin/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.19", "workerd": "^1.20250828.1" }, "optionalPeers": ["workerd"] }, "sha512-b0YHedns1FHEdalv9evlydfc/hLPs+LqCbPatmiJ99ScI5QTK0NXqqBhgvQ9qch73tsYfOpdpwtBl1GOcb1C9A=="], - "@cloudflare/vite-plugin/miniflare": ["miniflare@4.20250823.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^7.10.0", "workerd": "1.20250823.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-qjbF69XXyHXk4R//q0a9MLraKE9MLKZ/94k6jKcfouJ0g+se7VyMzCBryeWA534+ZAlNM4Ay5gqYr1v3Wk6ctQ=="], + "@cloudflare/vite-plugin/miniflare": ["miniflare@4.20250829.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^7.10.0", "workerd": "1.20250829.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-V1DLPnOXjm0DtfU9K0ftrxF+G7LkQ3nDKtXGdU8+Vf+dOqdGM+3ZHZOcDC5XPOsDnI280HBd5xcos/ghtGB7cg=="], - "@cloudflare/vite-plugin/wrangler": ["wrangler@4.33.1", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.0", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250823.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.19", "workerd": "1.20250823.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250823.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-8x/3Tbt+/raBMm0+vRyAHSGu2kF1QjeiSrx47apgPk/AzSBcXI9YuUUdGrKnozMYZlEbOxdBQOMyuRRDTyNmOg=="], + "@cloudflare/vite-plugin/wrangler": ["wrangler@4.33.2", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.1", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250829.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.19", "workerd": "1.20250829.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250829.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-4cQU62098a5mj7YsECkksypMNoO9B8D6CVzP/SDEqP73ti9exBxI3OlkB+8rMawF1OyYNAihaSAzIPZ52OiK0g=="], "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + "@daytonaio/sdk/dotenv": ["dotenv@17.2.1", "", {}, "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ=="], + "@dotenvx/dotenvx/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], "@dotenvx/dotenvx/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], @@ -4203,6 +4205,10 @@ "@scalar/types/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="], + "@shikijs/rehype/@shikijs/types": ["@shikijs/types@3.12.1", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-Is/p+1vTss22LIsGCJTmGrxu7ZC1iBL9doJFYLaZ4aI8d0VDXb7Mn0kBzhkc7pdsRpmUbQLQ5HXwNpa3H6F8og=="], + + "@shikijs/rehype/shiki": ["shiki@3.12.1", "", { "dependencies": { "@shikijs/core": "3.12.1", "@shikijs/engine-javascript": "3.12.1", "@shikijs/engine-oniguruma": "3.12.1", "@shikijs/langs": "3.12.1", "@shikijs/themes": "3.12.1", "@shikijs/types": "3.12.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-eMlxVaXyuNQAQCaMtDKQjKv0eVm+kA6fsZtv9UqKgspP+7lWCVi7SoN+cJq1dawvIDQY7TI3SixamztotM6R6Q=="], + "@tailwindcss/oxide/tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], @@ -4267,6 +4273,8 @@ "dom-helpers/@babel/runtime": ["@babel/runtime@7.28.3", "", {}, "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA=="], + "dotenv-cli/dotenv": ["dotenv@17.2.1", "", {}, "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ=="], + "dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "eciesjs/@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="], @@ -4455,7 +4463,7 @@ "vite-shadcn-template-libra/tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], - "vite-shadcn-template-libra/wrangler": ["wrangler@4.33.1", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.0", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250823.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.19", "workerd": "1.20250823.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250823.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-8x/3Tbt+/raBMm0+vRyAHSGu2kF1QjeiSrx47apgPk/AzSBcXI9YuUUdGrKnozMYZlEbOxdBQOMyuRRDTyNmOg=="], + "vite-shadcn-template-libra/wrangler": ["wrangler@4.33.2", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.1", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250829.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.19", "workerd": "1.20250829.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250829.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-4cQU62098a5mj7YsECkksypMNoO9B8D6CVzP/SDEqP73ti9exBxI3OlkB+8rMawF1OyYNAihaSAzIPZ52OiK0g=="], "webpack/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], @@ -4671,7 +4679,7 @@ "@changesets/parse/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd": ["workerd@1.20250823.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250823.0", "@cloudflare/workerd-darwin-arm64": "1.20250823.0", "@cloudflare/workerd-linux-64": "1.20250823.0", "@cloudflare/workerd-linux-arm64": "1.20250823.0", "@cloudflare/workerd-windows-64": "1.20250823.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-95lToK9zeaC7bX5ZmlP/wz6zqoUPBk3hhec1JjEMGZrxsXY9cPRkjWNCcjDctQ17U97vjMcY/ymchgx7w8Cfmg=="], + "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd": ["workerd@1.20250829.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250829.0", "@cloudflare/workerd-darwin-arm64": "1.20250829.0", "@cloudflare/workerd-linux-64": "1.20250829.0", "@cloudflare/workerd-linux-arm64": "1.20250829.0", "@cloudflare/workerd-windows-64": "1.20250829.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-8qoE56hf9QHS2llMM1tybjhvFEX5vnNUa1PpuyxeNC9F0dn9/qb9eDqN/z3sBPgpYK8vfQU9J8KOxczA+qo/cQ=="], "@cloudflare/vite-plugin/miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], @@ -4679,13 +4687,13 @@ "@cloudflare/vite-plugin/miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "@cloudflare/vite-plugin/miniflare/workerd": ["workerd@1.20250823.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250823.0", "@cloudflare/workerd-darwin-arm64": "1.20250823.0", "@cloudflare/workerd-linux-64": "1.20250823.0", "@cloudflare/workerd-linux-arm64": "1.20250823.0", "@cloudflare/workerd-windows-64": "1.20250823.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-95lToK9zeaC7bX5ZmlP/wz6zqoUPBk3hhec1JjEMGZrxsXY9cPRkjWNCcjDctQ17U97vjMcY/ymchgx7w8Cfmg=="], + "@cloudflare/vite-plugin/miniflare/workerd": ["workerd@1.20250829.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250829.0", "@cloudflare/workerd-darwin-arm64": "1.20250829.0", "@cloudflare/workerd-linux-64": "1.20250829.0", "@cloudflare/workerd-linux-arm64": "1.20250829.0", "@cloudflare/workerd-windows-64": "1.20250829.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-8qoE56hf9QHS2llMM1tybjhvFEX5vnNUa1PpuyxeNC9F0dn9/qb9eDqN/z3sBPgpYK8vfQU9J8KOxczA+qo/cQ=="], "@cloudflare/vite-plugin/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], "@cloudflare/vite-plugin/wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], - "@cloudflare/vite-plugin/wrangler/workerd": ["workerd@1.20250823.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250823.0", "@cloudflare/workerd-darwin-arm64": "1.20250823.0", "@cloudflare/workerd-linux-64": "1.20250823.0", "@cloudflare/workerd-linux-arm64": "1.20250823.0", "@cloudflare/workerd-windows-64": "1.20250823.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-95lToK9zeaC7bX5ZmlP/wz6zqoUPBk3hhec1JjEMGZrxsXY9cPRkjWNCcjDctQ17U97vjMcY/ymchgx7w8Cfmg=="], + "@cloudflare/vite-plugin/wrangler/workerd": ["workerd@1.20250829.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250829.0", "@cloudflare/workerd-darwin-arm64": "1.20250829.0", "@cloudflare/workerd-linux-64": "1.20250829.0", "@cloudflare/workerd-linux-arm64": "1.20250829.0", "@cloudflare/workerd-windows-64": "1.20250829.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-8qoE56hf9QHS2llMM1tybjhvFEX5vnNUa1PpuyxeNC9F0dn9/qb9eDqN/z3sBPgpYK8vfQU9J8KOxczA+qo/cQ=="], "@dotenvx/dotenvx/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], @@ -4953,6 +4961,16 @@ "@react-email/preview-server/tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "@shikijs/rehype/shiki/@shikijs/core": ["@shikijs/core@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-j9+UDQ6M50xvaSR/e9lg212H0Fqxy3lYd39Q6YITYQxfrb5VYNUKPLZp4PN9f+YmRcdpyNAm3obn/tIZ2WkUWg=="], + + "@shikijs/rehype/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-mwif5T3rEBSMn/1m9dNi4WmB4dxH4VfYqreQMLpbFYov8MM3Gus98I549amFMjtEmYDAkTKGP7bmsv1n9t9I+A=="], + + "@shikijs/rehype/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-hbYq+XOc55CU7Irkhsgwh8WgQbx2W5IVzHV4l+wZ874olMLSNg5o3F73vo9m4SAhimFyqq/86xnx9h+T30HhhQ=="], + + "@shikijs/rehype/shiki/@shikijs/langs": ["@shikijs/langs@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1" } }, "sha512-Y1MbMfVO5baRz7Boo7EoD36TmzfUx/I5n8e+wZumx6SlUA81Zj1ZwNJL871iIuSHrdsheV4AxJtHQ9mlooklmg=="], + + "@shikijs/rehype/shiki/@shikijs/themes": ["@shikijs/themes@3.12.1", "", { "dependencies": { "@shikijs/types": "3.12.1" } }, "sha512-9JrAm9cA5hqM/YXymA3oAAZdnCgQf1zyrNDtsnM105nNEoEpux4dyzdoOjc2KawEKj1iUs/WH2ota6Atp7GYkQ=="], + "@tailwindcss/oxide/tar/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], "@tailwindcss/oxide/tar/minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="], @@ -5159,13 +5177,13 @@ "vite-shadcn-template-libra/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "vite-shadcn-template-libra/wrangler/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.19", "workerd": "^1.20250816.0" }, "optionalPeers": ["workerd"] }, "sha512-0JbEj+KTCQ4nTIWg2q8Bou+fPxzG6/zwU5O/w6Cld6WEjLl+716foT+2bjg48h09hMtjTKkJdAh1m4LybBKGCg=="], + "vite-shadcn-template-libra/wrangler/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.19", "workerd": "^1.20250828.1" }, "optionalPeers": ["workerd"] }, "sha512-b0YHedns1FHEdalv9evlydfc/hLPs+LqCbPatmiJ99ScI5QTK0NXqqBhgvQ9qch73tsYfOpdpwtBl1GOcb1C9A=="], "vite-shadcn-template-libra/wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], - "vite-shadcn-template-libra/wrangler/miniflare": ["miniflare@4.20250823.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^7.10.0", "workerd": "1.20250823.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-qjbF69XXyHXk4R//q0a9MLraKE9MLKZ/94k6jKcfouJ0g+se7VyMzCBryeWA534+ZAlNM4Ay5gqYr1v3Wk6ctQ=="], + "vite-shadcn-template-libra/wrangler/miniflare": ["miniflare@4.20250829.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^7.10.0", "workerd": "1.20250829.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-V1DLPnOXjm0DtfU9K0ftrxF+G7LkQ3nDKtXGdU8+Vf+dOqdGM+3ZHZOcDC5XPOsDnI280HBd5xcos/ghtGB7cg=="], - "vite-shadcn-template-libra/wrangler/workerd": ["workerd@1.20250823.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250823.0", "@cloudflare/workerd-darwin-arm64": "1.20250823.0", "@cloudflare/workerd-linux-64": "1.20250823.0", "@cloudflare/workerd-linux-arm64": "1.20250823.0", "@cloudflare/workerd-windows-64": "1.20250823.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-95lToK9zeaC7bX5ZmlP/wz6zqoUPBk3hhec1JjEMGZrxsXY9cPRkjWNCcjDctQ17U97vjMcY/ymchgx7w8Cfmg=="], + "vite-shadcn-template-libra/wrangler/workerd": ["workerd@1.20250829.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250829.0", "@cloudflare/workerd-darwin-arm64": "1.20250829.0", "@cloudflare/workerd-linux-64": "1.20250829.0", "@cloudflare/workerd-linux-arm64": "1.20250829.0", "@cloudflare/workerd-windows-64": "1.20250829.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-8qoE56hf9QHS2llMM1tybjhvFEX5vnNUa1PpuyxeNC9F0dn9/qb9eDqN/z3sBPgpYK8vfQU9J8KOxczA+qo/cQ=="], "webpack/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], @@ -5281,15 +5299,15 @@ "@aws-sdk/middleware-signing/@smithy/signature-v4/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], - "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250823.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-yRLJc1cQNqQYcDViOk7kpTXnR5XuBP7B/Ms5KBdlQ6eTr2Vsg9mfKqWKInjzY8/Cx+p+Sic2Tbld42gcYkiM2A=="], + "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250829.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-IkB5gaLz3gzBg9hIsC/bVvOMDaRiWA5anaPK0ERDXXXJnMiBkLnA009O5Mp0x7j0fpxbw05xaiYXcFdGPdUt3A=="], - "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250823.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KJnikUe6J29Ga1QMPKNCc8eHD56DdBlu5XE5LoBH/AYRrbS5UI1d5F844hUWoFKJb8KRaPIH9F849HZWfNa1vw=="], + "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250829.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gFYC+w0jznCweKmrv63inEYduGj6crOskgZrn5zHI8S7c3ynC1LSW6LR8E9A2mSOwuDWKM1hHypwctwGUKlikg=="], - "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250823.0", "", { "os": "linux", "cpu": "x64" }, "sha512-4QFXq4eDWEAK5QjGxRe0XUTBax1Fgarc08HETL6q0y/KPZp2nOTLfjLjklTn/qEiztafNFoJEIwhkiknHeOi/g=="], + "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250829.0", "", { "os": "linux", "cpu": "x64" }, "sha512-JS699jk+Bn7j4QF7tdF+Sqhy4EUHM2NGVLF/vOIbpPWQnBVvP6Z+vmxi5MuVUwpAH48kpqbtMx380InNvT5f1Q=="], - "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250823.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-sODSrSVe4W/maoBu76qb0sJGBhxhSM2Q2tg/+G7q1IPgRZSzArMKIPrW6nBnmBrrG1O0X6aoAdID6w5hfuEM4g=="], + "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250829.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ic/VwcrCEQiIzynmpFQnS8N3uXm8evxUxFe37r6OC8/MGcXZTcp0/lmEk+cjjz+2aw5CfPMP82IdQyRKVZ+Og=="], - "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250823.0", "", { "os": "win32", "cpu": "x64" }, "sha512-WaNqUOXUnrcEI+i2NI4+okA9CrJMI9n2XTfVtDg/pLvcA/ZPTz23MEFMZU1splr4SslS1th1NBO38RMPnDB4rA=="], + "@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250829.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6uETqeeMciRSA00GRGzH1nnz0egDP2bqMOJtTBWlLzFs88GbLe2RXECJxo4E3eVr8yvAMyqwd0WUR4dDBjO7Rg=="], "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], @@ -5329,15 +5347,15 @@ "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], - "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250823.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-yRLJc1cQNqQYcDViOk7kpTXnR5XuBP7B/Ms5KBdlQ6eTr2Vsg9mfKqWKInjzY8/Cx+p+Sic2Tbld42gcYkiM2A=="], + "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250829.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-IkB5gaLz3gzBg9hIsC/bVvOMDaRiWA5anaPK0ERDXXXJnMiBkLnA009O5Mp0x7j0fpxbw05xaiYXcFdGPdUt3A=="], - "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250823.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KJnikUe6J29Ga1QMPKNCc8eHD56DdBlu5XE5LoBH/AYRrbS5UI1d5F844hUWoFKJb8KRaPIH9F849HZWfNa1vw=="], + "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250829.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gFYC+w0jznCweKmrv63inEYduGj6crOskgZrn5zHI8S7c3ynC1LSW6LR8E9A2mSOwuDWKM1hHypwctwGUKlikg=="], - "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250823.0", "", { "os": "linux", "cpu": "x64" }, "sha512-4QFXq4eDWEAK5QjGxRe0XUTBax1Fgarc08HETL6q0y/KPZp2nOTLfjLjklTn/qEiztafNFoJEIwhkiknHeOi/g=="], + "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250829.0", "", { "os": "linux", "cpu": "x64" }, "sha512-JS699jk+Bn7j4QF7tdF+Sqhy4EUHM2NGVLF/vOIbpPWQnBVvP6Z+vmxi5MuVUwpAH48kpqbtMx380InNvT5f1Q=="], - "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250823.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-sODSrSVe4W/maoBu76qb0sJGBhxhSM2Q2tg/+G7q1IPgRZSzArMKIPrW6nBnmBrrG1O0X6aoAdID6w5hfuEM4g=="], + "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250829.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ic/VwcrCEQiIzynmpFQnS8N3uXm8evxUxFe37r6OC8/MGcXZTcp0/lmEk+cjjz+2aw5CfPMP82IdQyRKVZ+Og=="], - "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250823.0", "", { "os": "win32", "cpu": "x64" }, "sha512-WaNqUOXUnrcEI+i2NI4+okA9CrJMI9n2XTfVtDg/pLvcA/ZPTz23MEFMZU1splr4SslS1th1NBO38RMPnDB4rA=="], + "@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250829.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6uETqeeMciRSA00GRGzH1nnz0egDP2bqMOJtTBWlLzFs88GbLe2RXECJxo4E3eVr8yvAMyqwd0WUR4dDBjO7Rg=="], "@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="], @@ -5389,15 +5407,15 @@ "@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], - "@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250823.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-yRLJc1cQNqQYcDViOk7kpTXnR5XuBP7B/Ms5KBdlQ6eTr2Vsg9mfKqWKInjzY8/Cx+p+Sic2Tbld42gcYkiM2A=="], + "@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250829.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-IkB5gaLz3gzBg9hIsC/bVvOMDaRiWA5anaPK0ERDXXXJnMiBkLnA009O5Mp0x7j0fpxbw05xaiYXcFdGPdUt3A=="], - "@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250823.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KJnikUe6J29Ga1QMPKNCc8eHD56DdBlu5XE5LoBH/AYRrbS5UI1d5F844hUWoFKJb8KRaPIH9F849HZWfNa1vw=="], + "@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250829.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gFYC+w0jznCweKmrv63inEYduGj6crOskgZrn5zHI8S7c3ynC1LSW6LR8E9A2mSOwuDWKM1hHypwctwGUKlikg=="], - "@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250823.0", "", { "os": "linux", "cpu": "x64" }, "sha512-4QFXq4eDWEAK5QjGxRe0XUTBax1Fgarc08HETL6q0y/KPZp2nOTLfjLjklTn/qEiztafNFoJEIwhkiknHeOi/g=="], + "@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250829.0", "", { "os": "linux", "cpu": "x64" }, "sha512-JS699jk+Bn7j4QF7tdF+Sqhy4EUHM2NGVLF/vOIbpPWQnBVvP6Z+vmxi5MuVUwpAH48kpqbtMx380InNvT5f1Q=="], - "@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250823.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-sODSrSVe4W/maoBu76qb0sJGBhxhSM2Q2tg/+G7q1IPgRZSzArMKIPrW6nBnmBrrG1O0X6aoAdID6w5hfuEM4g=="], + "@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250829.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ic/VwcrCEQiIzynmpFQnS8N3uXm8evxUxFe37r6OC8/MGcXZTcp0/lmEk+cjjz+2aw5CfPMP82IdQyRKVZ+Og=="], - "@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250823.0", "", { "os": "win32", "cpu": "x64" }, "sha512-WaNqUOXUnrcEI+i2NI4+okA9CrJMI9n2XTfVtDg/pLvcA/ZPTz23MEFMZU1splr4SslS1th1NBO38RMPnDB4rA=="], + "@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250829.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6uETqeeMciRSA00GRGzH1nnz0egDP2bqMOJtTBWlLzFs88GbLe2RXECJxo4E3eVr8yvAMyqwd0WUR4dDBjO7Rg=="], "@node-minify/core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -5667,15 +5685,15 @@ "vite-shadcn-template-libra/wrangler/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], - "vite-shadcn-template-libra/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250823.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-yRLJc1cQNqQYcDViOk7kpTXnR5XuBP7B/Ms5KBdlQ6eTr2Vsg9mfKqWKInjzY8/Cx+p+Sic2Tbld42gcYkiM2A=="], + "vite-shadcn-template-libra/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250829.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-IkB5gaLz3gzBg9hIsC/bVvOMDaRiWA5anaPK0ERDXXXJnMiBkLnA009O5Mp0x7j0fpxbw05xaiYXcFdGPdUt3A=="], - "vite-shadcn-template-libra/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250823.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KJnikUe6J29Ga1QMPKNCc8eHD56DdBlu5XE5LoBH/AYRrbS5UI1d5F844hUWoFKJb8KRaPIH9F849HZWfNa1vw=="], + "vite-shadcn-template-libra/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250829.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gFYC+w0jznCweKmrv63inEYduGj6crOskgZrn5zHI8S7c3ynC1LSW6LR8E9A2mSOwuDWKM1hHypwctwGUKlikg=="], - "vite-shadcn-template-libra/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250823.0", "", { "os": "linux", "cpu": "x64" }, "sha512-4QFXq4eDWEAK5QjGxRe0XUTBax1Fgarc08HETL6q0y/KPZp2nOTLfjLjklTn/qEiztafNFoJEIwhkiknHeOi/g=="], + "vite-shadcn-template-libra/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250829.0", "", { "os": "linux", "cpu": "x64" }, "sha512-JS699jk+Bn7j4QF7tdF+Sqhy4EUHM2NGVLF/vOIbpPWQnBVvP6Z+vmxi5MuVUwpAH48kpqbtMx380InNvT5f1Q=="], - "vite-shadcn-template-libra/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250823.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-sODSrSVe4W/maoBu76qb0sJGBhxhSM2Q2tg/+G7q1IPgRZSzArMKIPrW6nBnmBrrG1O0X6aoAdID6w5hfuEM4g=="], + "vite-shadcn-template-libra/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250829.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ic/VwcrCEQiIzynmpFQnS8N3uXm8evxUxFe37r6OC8/MGcXZTcp0/lmEk+cjjz+2aw5CfPMP82IdQyRKVZ+Og=="], - "vite-shadcn-template-libra/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250823.0", "", { "os": "win32", "cpu": "x64" }, "sha512-WaNqUOXUnrcEI+i2NI4+okA9CrJMI9n2XTfVtDg/pLvcA/ZPTz23MEFMZU1splr4SslS1th1NBO38RMPnDB4rA=="], + "vite-shadcn-template-libra/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250829.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6uETqeeMciRSA00GRGzH1nnz0egDP2bqMOJtTBWlLzFs88GbLe2RXECJxo4E3eVr8yvAMyqwd0WUR4dDBjO7Rg=="], "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], diff --git a/package.json b/package.json index dfecf85..42227d4 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@babel/runtime": "^8.0.0-beta.1", "@biomejs/biome": "^2.2.2", "@changesets/cli": "^2.29.6", - "@cloudflare/workers-types": "^4.20250902.0", + "@cloudflare/workers-types": "^4.20250903.0", "@t3-oss/env-nextjs": "^0.13.8", "@turbo/gen": "^2.5.6", "turbo": "^2.5.6", @@ -40,7 +40,7 @@ "@types/react-dom": "^19.1.9", "babel-plugin-react-compiler": "^19.1.0-rc.1-rc-af1b7da-20250421", "changeset": "^0.2.6", - "dotenv": "^17.2.1", + "dotenv": "^17.2.2", "dotenv-cli": "^10.0.0", "drizzle-kit": "^0.31.4", "drizzle-seed": "^0.3.1", @@ -51,7 +51,7 @@ }, "dependencies": { "@octokit/rest": "^22.0.0", - "@opennextjs/cloudflare": "^1.7.0", + "@opennextjs/cloudflare": "^1.7.1", "better-auth": "^1.3.7", "drizzle-orm": "^0.44.5", "e2b": "1.2.0-beta.5", diff --git a/packages/auth/plugins/stripe/subscription-lifecycle.ts b/packages/auth/plugins/stripe/subscription-lifecycle.ts index 202537d..a690001 100644 --- a/packages/auth/plugins/stripe/subscription-lifecycle.ts +++ b/packages/auth/plugins/stripe/subscription-lifecycle.ts @@ -476,7 +476,7 @@ export const onSubscriptionComplete = async ({ event, subscription, stripeSubscr const periodStart = new Date(subscription.periodStart) const periodEnd = new Date(subscription.periodEnd) const periodDurationMs = periodEnd.getTime() - periodStart.getTime() - const monthsInPeriod = Math.round(periodDurationMs / (1000 * 60 * 60 * 24 * 30.44)) // Average days per month + const monthsInPeriod = Math.floor(periodDurationMs / (1000 * 60 * 60 * 24 * 30.44)) // Average days per month const billingInterval = monthsInPeriod >= 11 ? 'year' : 'month' // 11+ months considered annual await createOrUpdateSubscriptionLimit( diff --git a/packages/auth/plugins/stripe/subscription-updates.ts b/packages/auth/plugins/stripe/subscription-updates.ts index 7e201af..a6e36c2 100644 --- a/packages/auth/plugins/stripe/subscription-updates.ts +++ b/packages/auth/plugins/stripe/subscription-updates.ts @@ -106,7 +106,7 @@ export const onSubscriptionUpdate = async ({ event, subscription }: any) => { const periodStart = new Date(subscription.periodStart) const periodEnd = new Date(subscription.periodEnd) const periodDurationMs = periodEnd.getTime() - periodStart.getTime() - const monthsInPeriod = Math.round(periodDurationMs / (1000 * 60 * 60 * 24 * 30.44)) // Average days per month + const monthsInPeriod = Math.floor(periodDurationMs / (1000 * 60 * 60 * 24 * 30.44)) // Average days per month const billingInterval = monthsInPeriod >= 11 ? 'year' : 'month' // 11+ months considered annual // Always update limits, whether renewal or not diff --git a/packages/auth/utils/subscription-limits/core.ts b/packages/auth/utils/subscription-limits/core.ts index 960596c..93ab758 100644 --- a/packages/auth/utils/subscription-limits/core.ts +++ b/packages/auth/utils/subscription-limits/core.ts @@ -172,7 +172,7 @@ export async function createOrUpdateSubscriptionLimit( periodStart: utcPeriodStart.toISOString(), periodEnd: utcPeriodEnd.toISOString(), billingInterval: 'month', // FREE plans are always monthly - lastQuotaRefresh: utcPeriodStart.toISOString(), + lastQuotaRefresh: null, // FREE plans don't need quota refresh tracking }) log.subscription('info', 'Created new FREE plan', { @@ -212,7 +212,7 @@ export async function createOrUpdateSubscriptionLimit( periodStart: utcPeriodStart.toISOString(), periodEnd: utcPeriodEnd.toISOString(), billingInterval: billingInterval || 'month', - lastQuotaRefresh: utcPeriodStart.toISOString(), + lastQuotaRefresh: billingInterval === 'year' ? utcPeriodStart.toISOString() : null, }) // Note: No onConflict needed here because we deactivated existing plans first // Partial unique index will prevent duplicate active records diff --git a/packages/better-auth-cloudflare/package.json b/packages/better-auth-cloudflare/package.json index 625551e..9e94a61 100644 --- a/packages/better-auth-cloudflare/package.json +++ b/packages/better-auth-cloudflare/package.json @@ -37,7 +37,7 @@ "@libra/typescript-config": "*" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20250902.0", - "@opennextjs/cloudflare": "^1.7.0" + "@cloudflare/workers-types": "^4.20250903.0", + "@opennextjs/cloudflare": "^1.7.1" } } \ No newline at end of file diff --git a/packages/shikicode/package.json b/packages/shikicode/package.json index 87c320e..cd733ec 100644 --- a/packages/shikicode/package.json +++ b/packages/shikicode/package.json @@ -13,7 +13,7 @@ "update": "bun update" }, "devDependencies": { - "shiki": "^3.12.1", + "shiki": "^3.12.2", "@libra/typescript-config": "*" }, "exports": {