/** * TypeScript types for Autumn billing integration. * * Type definitions adapted from autumn-js React implementation * for Svelte 5 reactive wrapper compatibility. */ import type { FunctionReference } from "convex/server"; import type { AttachFeatureOptions as AutumnAttachFeatureOptions, AttachResult as AutumnAttachResult, CheckoutResult as AutumnCheckoutResult, } from "autumn-js"; /** * Represents a billable feature in Autumn. */ export interface Feature { id: string; name: string; type: "continuous_use" | "single_use"; balance?: number; included_usage?: number; interval?: "month" | "year" | "one_time"; } /** * Represents a pricing plan or product. */ export interface Product { id: string; name: string; description?: string; items: ProductItem[]; } /** * Component of a product defining feature pricing and usage limits. */ export interface ProductItem { feature_id?: string; included_usage?: number | string; price?: number; interval?: "month" | "year" | "one_time"; } /** * Represents an entity for entity-based billing scenarios. */ export interface Entity { id: string; name: string; feature_id?: string; balance?: number; included_usage?: number; created_at?: number; } /** * Represents a customer with billing information and subscriptions. */ export interface Customer { id: string; name?: string; email?: string; products?: Product[]; features?: Record; entities?: Entity[]; stripe_customer_id?: string; created_at?: number; updated_at?: number; } /** * Parameters for checking feature access. */ export interface CheckParams { featureId: string; productId?: string; requiredBalance?: number; entityId?: string; [key: string]: unknown; } /** * Result of checking feature access. */ export interface CheckResult { allowed: boolean; balance?: number; reason?: string; } /** * Parameters for initiating checkout. */ export interface CheckoutParams { productId: string; dialog?: (url: string) => void; successUrl?: string; [key: string]: unknown; } /** * Prepaid feature quantity carried by a checkout preview. */ export type AttachFeatureOptions = AutumnAttachFeatureOptions; /** * Result of initiating checkout. * * Autumn declares `url` as `string | undefined`, but the live API answers with * `null` when the purchase can be completed in-app instead of through a hosted * Stripe session. The widened type keeps that case visible: an absent URL is * the documented signal to show the returned preview and confirm it with * `attach`, not an error. */ export type CheckoutResult = Omit & { url?: string | null; }; /** * Result of attaching a product. * * `checkout_url` is set when the stored payment method could not be charged * and Autumn wants the customer to complete payment on a hosted page. */ export type AttachResult = AutumnAttachResult; /** * Parameters for tracking usage. */ export interface TrackParams { featureId: string; value: number; entityId?: string; [key: string]: unknown; } /** * Result of tracking usage. */ export interface TrackResult { success: boolean; balance?: number; error?: string; } /** * Parameters for attaching a product. */ export interface AttachParams { productId: string; [key: string]: unknown; } /** * Parameters for canceling a subscription. */ export interface CancelParams { productId: string; [key: string]: unknown; } /** * Parameters for opening the billing portal. */ export interface BillingPortalParams { returnUrl?: string; [key: string]: unknown; } /** * Result of opening billing portal. */ export interface BillingPortalResult { url: string; } /** * Parameters for creating an entity. */ export interface CreateEntityParams { id: string; name?: string; featureId: string; [key: string]: unknown; } /** * Parameters for getting an entity. */ export interface GetEntityParams { entityId: string; expand?: ("invoices")[]; [key: string]: unknown; } /** * Parameters for setting up payment method. */ export interface SetupPaymentParams { successUrl?: string; checkoutSessionParams?: Record; [key: string]: unknown; } /** * Result of setting up payment method. */ export interface SetupPaymentResult { url: string; } /** * Parameters for creating a referral code. */ export interface CreateReferralCodeParams { programId: string; [key: string]: unknown; } /** * Result of creating a referral code. */ export interface CreateReferralCodeResult { code: string; program_id: string; } /** * Parameters for redeeming a referral code. */ export interface RedeemReferralCodeParams { code: string; [key: string]: unknown; } /** * Result of redeeming a referral code. */ export interface RedeemReferralCodeResult { success: boolean; reward?: unknown; } /** * Parameters for setting usage to an absolute value. * * Sets usage to the exact value provided, unlike track() which increments the current value. */ export interface SetUsageParams { featureId: string; value: number; entityId?: string; [key: string]: unknown; } /** * Result returned when setting usage to an absolute value. */ export interface SetUsageResult { success: boolean; } /** * Detailed feature usage data from the Customer object. * * Accessed via customer.features[featureId]. Not returned by standalone endpoints. */ export interface FeatureUsageData { id: string; name: string; type: 'continuous_use' | 'single_use'; balance?: number; included_usage?: number | string; usage?: number; interval?: 'month' | 'year' | 'one_time'; interval_count?: number; next_reset_at?: number; overage_allowed?: boolean; unlimited?: boolean; } /** * Parameters for querying customer data. */ export interface QueryParams { featureId: string | string[]; range?: "24h" | "7d" | "30d" | "90d" | "last_cycle"; [key: string]: unknown; } /** * Result of querying customer data. */ export interface QueryResult { data: Record; } /** * Event record returned from Autumn analytics endpoints. */ export interface EventRecord { id: string; timestamp: number; feature_id: string; customer_id: string; value: number; properties: Record; } /** * Parameters for listing raw usage events. */ export interface EventListParams { customerId?: string; featureId: string | string[]; offset?: number; limit?: number; customRange?: { start?: number; end?: number; }; [key: string]: unknown; } /** * Result of listing raw usage events. */ export interface EventListResult { list: EventRecord[]; has_more: boolean; offset: number; limit: number; total: number; } /** * Parameters for aggregating usage events. */ export interface EventAggregateParams { customerId?: string; featureId: string | string[]; range?: "24h" | "7d" | "30d" | "90d" | "last_cycle" | "1bc" | "3bc"; customRange?: { start: number; end: number; }; groupBy?: string; binSize?: "day" | "hour"; [key: string]: unknown; } /** * Parameters for creating or getting a customer. */ export interface CreateCustomerParams { expand?: ("invoices" | "payment_method" | "rewards" | "trials_used" | "entities" | "referrals")[]; errorOnNotFound?: boolean; [key: string]: unknown; } /** * Type definition for the Autumn Convex API surface. * * Matches Convex-generated types from api.autumn to enable direct assignment * without type casting. Uses FunctionReference to align with * Convex code generation output. * * Runtime type safety is provided by unwrapAutumnResponse(), which validates * response structure and throws on errors. The index signature allows for * additional backend functions beyond the core set defined here. */ export interface AutumnConvexApi { createCustomer: FunctionReference; check: FunctionReference; checkout: FunctionReference; track: FunctionReference; attach: FunctionReference; cancel: FunctionReference; billingPortal: FunctionReference; createEntity: FunctionReference; getEntity: FunctionReference; setupPayment: FunctionReference; createReferralCode: FunctionReference; redeemReferralCode: FunctionReference; listProducts: FunctionReference; usage: FunctionReference; query: FunctionReference; listEvents: FunctionReference; aggregateEvents: FunctionReference; [key: string]: FunctionReference; } /** * Return type for local feature access checks. */ export interface LocalCheckResult { allowed: boolean; reason?: string; } /** * Options for controlling automatic refetch behavior after mutations. */ export interface RefetchOptions { /** * Whether to automatically refetch customer data after mutation. * * When true (default), customer data is automatically refreshed after the mutation completes, * ensuring your UI stays in sync. Set to false for performance optimization in scenarios like: * - Batch operations (disable for each, then manually refetch once) * - Analytics tracking (UI doesn't depend on result) * - Background operations (user doesn't need immediate feedback) * * @default true * @example * ```typescript * // Default: auto-refetch enabled * await track({ featureId: 'messages', value: 1 }); * * // Opt-out: skip refetch for performance * await track({ featureId: 'analytics', value: 1 }, { refetch: false }); * * // Batch: disable auto-refetch, then manually refetch once * await track({ featureId: 'messages', value: 1 }, { refetch: false }); * await track({ featureId: 'uploads', value: 1 }, { refetch: false }); * await refetch(); // Single refetch for all changes * ``` */ refetch?: boolean; } /** * Response wrapper for Autumn action results. * * All Autumn Convex actions return this discriminated union format. * Either data or error is set, never both. */ export type AutumnActionResponse = | { data: T; error: null; statusCode?: number } | { data: null; error: {message: string; code: string}; statusCode?: number }; /** * Custom error class for Autumn API errors. * * Thrown when an Autumn action returns an error response. */ export class AutumnError extends Error { code: string; statusCode?: number; constructor(error: {message: string; code: string}, statusCode?: number) { super(error.message); this.name = "AutumnError"; this.code = error.code; this.statusCode = statusCode; } } /** * Unwraps an Autumn action response with type narrowing and validation. * * @param response - The wrapped response from an Autumn action * @returns The unwrapped data * @throws {AutumnError} When the response contains an error * @throws {AutumnError} When the response data is null or undefined */ export function unwrapAutumnResponse( response: AutumnActionResponse, ): T { if (response.error) { throw new AutumnError(response.error, response.statusCode); } if (response.data === null || response.data === undefined) { throw new AutumnError( {message: "No data in response", code: "NO_DATA"}, response.statusCode, ); } return response.data; }