import Stripe from 'stripe'; import pg, { PoolConfig, QueryResult } from 'pg'; import { ConnectionOptions } from 'node:tls'; export { d as SchemaInstallationStatus, S as StripeSchemaComment, p as parseSchemaComment } from './schemaComment-CfQVwFKa.js'; declare const VERSION: string; type PostgresConfig = { schema: string; /** Schema for metadata tables (accounts, _sync_runs, etc.). Defaults to schema when not provided. */ syncSchema?: string; poolConfig: PoolConfig; }; type RawJsonUpsertOptions = { /** * Columns to use as the ON CONFLICT target. * Example: ['id'] for standard Stripe objects, or a composite key for Sigma tables. */ conflictTarget: string[]; /** * Additional typed columns to insert alongside `_raw_data` (for tables that don't have `id` keys). * Values are read from `entry[entryKey]` and cast to `pgType` in SQL. */ extraColumns?: Array<{ column: string; pgType: string; entryKey: string; }>; }; declare class PostgresClient { private config; pool: pg.Pool; constructor(config: PostgresConfig); private get syncSchema(); private schemaForTable; delete(table: string, id: string): Promise; query(text: string, params?: any[]): Promise; upsertMany(entries: T[], table: string): Promise; upsertManyWithTimestampProtection(entries: T[], table: string, accountId: string, syncTimestamp?: string, upsertOptions?: RawJsonUpsertOptions, schemaOverride?: string): Promise; private cleanseArrayField; findMissingEntries(table: string, ids: string[]): Promise; upsertAccount(accountData: { id: string; raw_data: any; }, apiKeyHash: string): Promise; getAllAccounts(): Promise; /** * Get all accounts that have been synced to the database. * Throws a descriptive error if the query fails. */ getAllSyncedAccounts(): Promise; /** * Looks up an account ID by API key hash * Uses the GIN index on api_key_hashes for fast lookups * @param apiKeyHash - SHA-256 hash of the Stripe API key * @returns Account ID if found, null otherwise */ getAccountIdByApiKeyHash(apiKeyHash: string): Promise; /** * Looks up full account data by API key hash * @param apiKeyHash - SHA-256 hash of the Stripe API key * @returns Account raw data if found, null otherwise */ getAccountByApiKeyHash(apiKeyHash: string): Promise; private getAccountIdColumn; getAccountRecordCounts(accountId: string): Promise<{ [tableName: string]: number; }>; deletePlan(id: string): Promise; deleteProduct(id: string): Promise; columnExists(table: string, column: string): Promise; deleteTaxId(id: string): Promise; deletePrice(id: string): Promise; deleteRemovedActiveEntitlements(customerId: string, currentActiveEntitlementIds: string[]): Promise<{ rowCount: number; }>; /** * DANGEROUS: Delete an account and all associated data from the database * This operation cannot be undone! * * @param accountId - The Stripe account ID to delete * @param options - Options for deletion behavior * @param options.dryRun - If true, only count records without deleting (default: false) * @param options.useTransaction - If true, use transaction for atomic deletion (default: true) * @returns Deletion summary with counts and warnings */ dangerouslyDeleteSyncedAccountData(accountId: string, options?: { dryRun?: boolean; useTransaction?: boolean; }): Promise<{ deletedAccountId: string; deletedRecordCounts: { [tableName: string]: number; }; warnings: string[]; }>; deleteAccountWithCascade(accountId: string, useTransaction: boolean): Promise<{ [tableName: string]: number; }>; /** * Hash a string to a 32-bit integer for use with PostgreSQL advisory locks. * Uses a simple hash algorithm that produces consistent results. */ private hashToInt32; /** * Acquire a PostgreSQL advisory lock for the given key. * This lock is automatically released when the connection is closed or explicitly released. * Advisory locks are session-level and will block until the lock is available. * * @param key - A string key to lock on (will be hashed to an integer) */ acquireAdvisoryLock(key: string): Promise; /** * Release a PostgreSQL advisory lock for the given key. * * @param key - The same string key used to acquire the lock */ releaseAdvisoryLock(key: string): Promise; /** * Execute a function while holding an advisory lock. * The lock is automatically released after the function completes (success or error). * * IMPORTANT: This acquires a dedicated connection from the pool and holds it for the * duration of the function execution. PostgreSQL advisory locks are session-level, * so we must use the same connection for lock acquisition, operations, and release. * * @param key - A string key to lock on (will be hashed to an integer) * @param fn - The function to execute while holding the lock * @returns The result of the function */ withAdvisoryLock(key: string, fn: () => Promise): Promise; /** * Cancel stale runs (running but no object updated in 5 minutes). * Called before creating a new run to clean up crashed syncs. * Only cancels runs that have objects AND none have recent activity. * Runs without objects yet (just created) are not considered stale. */ cancelStaleRuns(accountId: string): Promise; /** * Get or create a sync run for this account. * Returns existing run if one is active for the given triggeredBy, otherwise creates new one. * Auto-cancels stale runs before checking. * * @param triggeredBy - Worker type (e.g., 'worker', 'sigma-worker'). Runs are isolated per triggeredBy. * @returns RunKey with isNew flag. Always returns a run (retries on race condition). */ getOrCreateSyncRun(accountId: string, triggeredBy?: string): Promise<{ accountId: string; runStartedAt: Date; isNew: boolean; }>; /** * Join an existing sync run or create a new one, and ensure object run rows exist. * * Combines getOrCreateSyncRun + createObjectRuns into a single atomic-ish operation. * Object runs are created idempotently (ON CONFLICT DO NOTHING) so this is safe * to call from multiple workers adding objects to the same run. * * @param accountId - The Stripe account ID * @param triggeredBy - What triggered this sync (for observability) * @param resourceNames - Database resource names (e.g. 'products', 'customers') * @returns Run key with accountId and runStartedAt */ joinOrCreateSyncRun(accountId: string, triggeredBy: string, resourceNames: string[], priorities?: Record, segmentedSync?: boolean): Promise<{ accountId: string; runStartedAt: Date; }>; /** * Find a run that completed (closed) within the given time window. * * @param intervalSeconds - How far back to look, in seconds. */ getCompletedRun(accountId: string, intervalSeconds: number): Promise<{ accountId: string; runStartedAt: Date; } | null>; /** * Start a reconciliation run only if no run completed successfully in the last 24 hours. * * @returns Run key if a new run was created, or null if a recent successful run exists. */ reconciliationRun(accountId: string, triggeredBy: string, resourceNames: string[], interval?: number, priorities?: Record, segmentedSync?: boolean): Promise<{ accountId: string; runStartedAt: Date; } | null>; /** * Get the active sync run for an account (if any). * @param triggeredBy - If provided, only returns run matching this triggeredBy value */ getActiveSyncRun(accountId: string, triggeredBy?: string): Promise<{ accountId: string; runStartedAt: Date; } | null>; /** * Get sync run config (for concurrency control). * Status is derived from sync_runs view. */ getSyncRun(accountId: string, runStartedAt: Date): Promise<{ accountId: string; runStartedAt: Date; maxConcurrent: number; closedAt: Date | null; } | null>; /** * Ensure a sync run has at least the requested max_concurrent value. */ ensureSyncRunMaxConcurrent(accountId: string, runStartedAt: Date, maxConcurrent: number): Promise; /** * Close a sync run (mark as done). * Status (complete/error) is derived from object run states. */ closeSyncRun(accountId: string, runStartedAt: Date): Promise; /** * Create object run entries for a sync run. * All objects start as 'pending'. * * @param resourceNames - Database resource names (e.g. 'products', 'customers', NOT 'product', 'customer') * @param priorities - Optional map of resource name → priority (from resourceRegistry order). * Lower values are processed first. */ createObjectRuns(accountId: string, runStartedAt: Date, resourceNames: string[], priorities?: Record): Promise; createChunkedObjectRuns(accountId: string, runStartedAt: Date, chunkCursors: Record, priorities?: Record): Promise; /** * Try to start an object sync (respects max_concurrent). * Returns true if claimed, false if already running or at concurrency limit. * * Note: There's a small race window where concurrent calls could result in * max_concurrent + 1 objects running. This is acceptable behavior. */ tryStartObjectSync(accountId: string, runStartedAt: Date, object: string): Promise; /** * Atomically claim the next pending task using FOR UPDATE SKIP LOCKED. * Two concurrent workers will never claim the same row — the second worker * skips the locked row and grabs the next one. * * Respects max_concurrent: returns null if already at the concurrency limit. */ claimNextTask(accountId: string, runStartedAt: Date): Promise<{ object: string; cursor: string | null; pageCursor: string | null; created_gte: number | null; created_lte: number | null; } | null>; /** * Get object run details. */ getObjectRun(accountId: string, runStartedAt: Date, object: string): Promise<{ object: string; status: string; processedCount: number; cursor: string | null; pageCursor: string | null; } | null>; /** * Update progress for an object sync. * Also touches updated_at for stale detection. */ incrementObjectProgress(accountId: string, runStartedAt: Date, object: string, count: number, createdGte?: number, createdLte?: number): Promise; /** * Atomically update an object sync row in a single round-trip. * Only the fields present in `updates` are written; omitted fields are left unchanged. * Auto-closes the sync run when all objects reach 'complete'. */ updateSyncObject(accountId: string, runStartedAt: Date, object: string, createdGte: number, createdLte: number, updates: { processedCount?: number; cursor?: string | null; status?: 'pending' | 'complete' | 'error'; pageCursor?: string | null; errorMessage?: string; }): Promise; /** * Update the pagination page_cursor used for backfills using Stripe list calls. */ updateObjectPageCursor(accountId: string, runStartedAt: Date, object: string, pageCursor: string | null): Promise; /** * Release a running object back to pending with an updated page_cursor. * Used after processing a single page when more pages remain. */ releaseObjectSync(accountId: string, runStartedAt: Date, object: string, pageCursor: string, createdGte?: number, createdLte?: number): Promise; /** * Clear the pagination page_cursor for an object sync. */ clearObjectPageCursor(accountId: string, runStartedAt: Date, object: string): Promise; /** * Clear the sync cursor on all previous completed runs for an object, * so the next run starts from scratch (no created.gte filter). */ clearObjectCursorHistory(accountId: string, object: string, runStartedAt: Date): Promise; updateObjectCursor(accountId: string, runStartedAt: Date, object: string, cursor: string | null, createdGte?: number, createdLte?: number): Promise; setObjectCursor(accountId: string, runStartedAt: Date, object: string, cursor: string | null): Promise; /** * List object names for a run by status, optionally filtered to a subset. */ listObjectsByStatus(accountId: string, runStartedAt: Date, status: string, objectFilter?: string[]): Promise; /** * Get per-object processed counts for a sync run. */ getObjectSyncedCounts(accountId: string, runStartedAt: Date): Promise>; /** * Get the highest cursor from previous syncs for an object type. * Uses only completed object runs. * - During the initial backfill we page through history, but we also update the cursor as we go. * If we crash mid-backfill and reuse that cursor, we can accidentally switch into incremental mode * too early and only ever fetch the newest page (breaking the historical backfill). * * Handles two cursor formats: * - Numeric: compared as bigint for correct ordering * - Composite cursors: compared as strings with COLLATE "C" */ getLastCompletedCursor(accountId: string, object: string): Promise; /** * Get the highest cursor from previous syncs for an object type, excluding the current run. */ getLastCursorBeforeRun(accountId: string, object: string, runStartedAt: Date): Promise; /** * Get the most recent cursor for an object run before the given run. * This returns the raw cursor value without interpretation. */ getLastObjectCursorBeforeRun(accountId: string, object: string, runStartedAt: Date): Promise; /** * Delete all sync runs and object runs for an account. * Useful for testing or resetting sync state. */ deleteSyncRuns(accountId: string): Promise; /** * Reset orphaned 'running' object runs back to 'pending'. * Used for crash recovery: if a worker was killed mid-sync, the object run * is left in 'running' state with no active worker. Resetting to 'pending' * allows new workers to re-claim and finish the work. */ resetStuckRunningObjects(accountId: string, runStartedAt: Date, stuckThresholdSeconds?: number): Promise; /** * Mark an object sync as complete. * Auto-closes the run when all objects are done. */ completeObjectSync(accountId: string, runStartedAt: Date, object: string, createdGte?: number, createdLte?: number): Promise; /** * Mark an object sync as failed. * Auto-closes the run when all objects are done. */ failObjectSync(accountId: string, runStartedAt: Date, object: string, errorMessage: string, createdGte?: number, createdLte?: number): Promise; /** * Check if any object in a run has errored. */ hasAnyObjectErrors(accountId: string, runStartedAt: Date): Promise; /** * Count running objects in a run. */ countRunningObjects(accountId: string, runStartedAt: Date): Promise; /** * Get the next pending object to process. * Returns null if no pending objects or at concurrency limit. */ getNextPendingObject(accountId: string, runStartedAt: Date): Promise; /** * Check if all objects in a run are complete (or error). */ areAllObjectsComplete(accountId: string, runStartedAt: Date): Promise; countObjectRuns(accountId: string, runStartedAt: Date): Promise; /** * Closes the database connection pool and cleans up resources. * Call this when you're done using the PostgresClient instance. */ close(): Promise; waitForRateLimit(maxRate: number): Promise; } type SigmaCursorColumnType = 'timestamp' | 'string' | 'number'; type SigmaCursorColumnSpec = { column: string; type: SigmaCursorColumnType; }; type SigmaCursorSpec = { version: 1; columns: SigmaCursorColumnSpec[]; }; type SigmaColumnSpec = { name: string; sigmaType: string; pgType: string; primaryKey: boolean; }; type SigmaIngestionConfig = { /** * The Sigma table name to query (no quoting, no schema). */ sigmaTable: string; /** * Destination Postgres table name (in the `stripe` schema by convention). */ destinationTable: string; /** Limit for each Sigma query page. */ pageSize: number; /** * Defines the ordering and cursor semantics. The columns must form a total order (i.e. be unique together) or pagination can be incorrect. */ cursor: SigmaCursorSpec; /** * Column metadata for the destination table. * If provided, used to dynamically create/reconcile the table. */ columns?: SigmaColumnSpec[]; /** Optional additional WHERE clause appended with AND (must not include leading WHERE). */ additionalWhere?: string; /** Columns to SELECT (defaults to `*`). */ select?: '*' | string[]; /** Postgres upsert behavior for this table (conflict target and typed columns). */ upsert: RawJsonUpsertOptions; }; type SigmaSyncProcessorConfig = { stripeSecretKey: string; enableSigma?: boolean; sigmaPageSizeOverride?: number; sigmaSchemaName?: string; logger?: Logger; }; /** * Handles all Sigma-specific sync logic: * - Building the sigma portion of the resource registry * - Fetching a single Sigma page (query + CSV parse + upsert) * - Resolving fallback cursors from destination tables * - Utility helpers (isSigmaResource, getSupportedSigmaObjects) */ declare class SigmaSyncProcessor { private readonly postgresClient; private readonly config; get sigmaSchemaName(): string; constructor(postgresClient: PostgresClient, config: SigmaSyncProcessorConfig); /** * Build the sigma portion of the resource registry. * Returns entries keyed by sigma table name with order starting after `maxCoreOrder`. */ buildSigmaRegistryEntries(maxCoreOrder: number): Record; /** * Check whether a resource exists in the sigma registry. */ isSigmaResource(sigmaRegistry: Record, object: string): boolean; /** * Get the list of Sigma-backed object types that can be synced. * Only returns sigma objects when enableSigma is true. */ getSupportedSigmaObjects(sigmaRegistry: Record): string[]; /** * Fetch the latest cursor from the destination table when no run cursor exists. * Queries the sigma schema for the max cursor column values. */ getSigmaFallbackCursorFromDestination(accountId: string, sigmaConfig: SigmaIngestionConfig): Promise; /** * Fetch one page of Sigma data, upsert to Postgres, and advance the cursor. */ fetchOneSigmaPage(accountId: string, resourceName: string, runStartedAt: Date, cursor: string | null, sigmaConfig: SigmaIngestionConfig): Promise; } interface ResourceDef { readonly order: number; readonly tableName: string; readonly dependencies?: readonly string[]; readonly list: (s: Stripe) => (p: Stripe.PaginationParams & { created?: Stripe.RangeQueryParam; }) => Promise<{ data: unknown[]; has_more: boolean; }>; readonly retrieve: (s: Stripe) => (id: string) => Promise>; readonly supportsCreatedFilter: boolean; readonly sync: boolean; readonly isFinalState?: (entity: any) => boolean; readonly childTables?: readonly string[]; readonly listExpands?: readonly Record (id: string) => Promise>>[]; } declare const RESOURCE_MAP: Record; type StripeObject = keyof typeof RESOURCE_MAP; declare const SYNC_OBJECTS: readonly ["all", "customer_with_entitlements", ...string[]]; type SyncObjectName = (typeof SYNC_OBJECTS)[number]; declare const REVALIDATE_ENTITIES: readonly [...string[], "radar.early_fraud_warning", "subscription_schedule", "entitlements"]; type RevalidateEntityName = (typeof REVALIDATE_ENTITIES)[number]; declare function getTableName(object: string, registry: Record): string; /** * Simple logger interface compatible with both pino and console */ interface Logger { info(message?: unknown, ...optionalParams: unknown[]): void; warn(message?: unknown, ...optionalParams: unknown[]): void; error(message?: unknown, ...optionalParams: unknown[]): void; } type RevalidateEntity = RevalidateEntityName; type StripeSyncConfig = { /** @deprecated Use `poolConfig` with a connection string instead. */ databaseUrl?: string; /** Stripe secret key used to authenticate requests to the Stripe API. Defaults to empty string */ stripeSecretKey: string; /** * Enables syncing Stripe Sigma (reporting) tables via the Sigma API. * * Default: false (opt-in, so workers don't enqueue Sigma jobs unexpectedly). */ enableSigma?: boolean; /** * Optional override for Sigma page size (per query). * Useful for edge function CPU limits; lower values reduce per-invocation work. */ sigmaPageSizeOverride?: number; /** * Postgres schema name for Sigma tables. * Default: "sigma" */ sigmaSchemaName?: string; /** * Postgres schema name for core Stripe data tables. * Default: "stripe" */ schemaName?: string; /** * Postgres schema name for sync metadata tables (accounts, _sync_runs, _managed_webhooks, etc.). * Defaults to schemaName when not provided. */ syncTablesSchemaName?: string; /** Stripe account ID. If not provided, will be retrieved from Stripe API. Used as fallback option. */ stripeAccountId?: string; /** Optional Stripe partner ID embedded in appInfo for telemetry (e.g. "pp_supabase"). */ partnerId?: string; /** Stripe webhook signing secret for validating webhook signatures. Required if not using managed webhooks. */ stripeWebhookSecret?: string; /** Stripe API version for the webhooks, defaults to 2020-08-27 */ stripeApiVersion?: string; /** * Stripe limits related lists like invoice items in an invoice to 10 by default. * By enabling this, sync-engine automatically fetches the remaining elements before saving * */ autoExpandLists?: boolean; /** * If true, the sync engine will backfill related entities, i.e. when a invoice webhook comes in, it ensures that the customer is present and synced. * This ensures foreign key integrity, but comes at the cost of additional queries to the database (and added latency for Stripe calls if the entity is actually missing). */ backfillRelatedEntities?: boolean; /** * If true, the webhook data is not used and instead the webhook is just a trigger to fetch the entity from Stripe again. This ensures that a race condition with failed webhooks can never accidentally overwrite the data with an older state. * * Default: false */ revalidateObjectsViaStripeApi?: Array; /** @deprecated Use `poolConfig` instead. */ maxPostgresConnections?: number; poolConfig?: PoolConfig; logger?: Logger; /** * Maximum number of customers to process concurrently when syncing payment methods. * Lower values reduce API load but increase sync time. * Default: 10 */ maxConcurrentCustomers?: number; }; type SyncObject = SyncObjectName; declare const SUPPORTED_WEBHOOK_EVENTS: Stripe.WebhookEndpointCreateParams.EnabledEvent[]; interface Sync { synced: number; } interface SyncParams { created?: { /** * Minimum value to filter by (exclusive) */ gt?: number; /** * Minimum value to filter by (inclusive) */ gte?: number; /** * Maximum value to filter by (exclusive) */ lt?: number; /** * Maximum value to filter by (inclusive) */ lte?: number; }; object?: SyncObject; backfillRelatedEntities?: boolean; } interface SyncEntitlementsParams { object: 'entitlements'; customerId: string; pagination?: Pick; } interface SyncFeaturesParams { object: 'features'; pagination?: Pick; } /** * Result of processing a single page of items via processNext() */ interface ProcessNextResult { /** Number of items processed in this page */ processed: number; /** Whether there are more items to process */ hasMore: boolean; /** The sync run this processing belongs to */ runStartedAt: Date; /** Sigma-only: whether this step started a new Sigma query run */ startedQuery?: boolean; } /** * Parameters for processNext() including optional run context */ interface ProcessNextParams extends SyncParams { /** Join an existing sync run instead of creating a new one */ runStartedAt?: Date; /** Who/what triggered this sync (for observability) */ triggeredBy?: string; } /** * Syncable resource configuration */ type BaseResourceConfig = { /** Backfill order: lower numbers sync first; parents before children for FK dependencies */ order: number; /** Database table name for this resource (e.g. 'customers', 'invoices') */ tableName: string; /** Whether this resource supports incremental sync via 'created' filter or cursor */ supportsCreatedFilter: boolean; /** Whether this resource is included in sync runs by default. Default: true */ sync?: boolean; /** Resource types that must be backfilled before this one (e.g. price depends on product) */ dependencies?: readonly string[]; /** Function to check if an entity is in a final state and doesn't need revalidation */ isFinalState?: (entity: any) => boolean; }; type StripeListResourceConfig = BaseResourceConfig & { /** Function to list items from Stripe API */ listFn: (params: Stripe.PaginationParams & { created?: Stripe.RangeQueryParam; }) => Promise<{ data: unknown[]; has_more: boolean; }>; /** Function to retrieve a single item by ID from Stripe API */ retrieveFn: (id: string) => Promise>; /** Optional list of sub-resources to expand during upsert/fetching (e.g. 'refunds', 'listLineItems') */ listExpands?: Record Promise>>[]; /** discriminator */ sigma?: undefined; }; /** * Configuration for Sigma query-backed resources. * Uses Stripe Sigma SQL queries with composite cursor pagination. */ type SigmaResourceConfig = BaseResourceConfig & { /** Sigma uses composite cursors, not created filter */ supportsCreatedFilter: false; /** Sigma ingestion configuration (query, cursor spec, upsert options) */ sigma: SigmaIngestionConfig; /** discriminator */ listFn?: undefined; /** discriminator */ retrieveFn?: undefined; /** discriminator */ listExpands?: Record Promise>>[]; }; /** Union of all resource configuration types */ type ResourceConfig = StripeListResourceConfig | SigmaResourceConfig; /** * Installation status of the stripe-sync package */ type InstallationStatus = 'not_installed' | 'installing' | 'installed' | 'error'; /** * Sync status for a single account (from sync_runs view) */ interface StripeSyncAccountState { account_id: string; started_at: string; closed_at: string | null; status: 'pending' | 'running' | 'complete' | 'error'; error_message: string | null; total_processed: number; total_objects: number; complete_count: number; error_count: number; running_count: number; pending_count: number; triggered_by: string; max_concurrent: number; } /** * Response schema for the sync status endpoint */ interface StripeSyncState { package_version: string; installation_status: InstallationStatus; sync_status: StripeSyncAccountState[]; } type StripeSyncWebhookDeps = { stripe: Stripe; postgresClient: PostgresClient; config: StripeSyncConfig; readonly accountId: string; getAccountId: (objectAccountId?: string) => Promise; upsertAny: (items: any[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string) => Promise; resourceRegistry: Record; }; declare class StripeSyncWebhook { private readonly deps; private _allowedObjects; constructor(deps: StripeSyncWebhookDeps); setObjectFilter(objects: string[] | null): void; private get syncMetadataSchemaName(); private quoteSyncMetadataSchemaName; processWebhook(payload: Buffer | Uint8Array | string, signature: string | undefined): Promise; processEvent(event: Stripe.Event): Promise; getSupportedEventTypes(): Stripe.WebhookEndpointCreateParams.EnabledEvent[]; handleDeletedEvent(event: Stripe.Event, accountId: string): Promise; defaultHandler(event: Stripe.Event, accountId: string): Promise; handleEntitlementSummaryEvent(event: Stripe.Event, accountId: string): Promise; private static readonly RESOURCE_DELETE_EVENTS; private isDeleteEvent; handleAnyEvent(event: Stripe.Event, accountId: string): Promise; getSyncTimestamp(event: Stripe.Event, refetched: boolean): string; findOrCreateManagedWebhook(url: string, params?: Omit): Promise; getManagedWebhook(id: string): Promise; getManagedWebhookByUrl(url: string): Promise; listManagedWebhooks(): Promise>; updateManagedWebhook(id: string, params: Stripe.WebhookEndpointUpdateParams): Promise; deleteManagedWebhook(id: string): Promise; upsertManagedWebhooks(webhooks: Array, accountId: string, syncTimestamp?: string): Promise>; } /** * Identifies a specific sync run. */ type RunKey = { accountId: string; runStartedAt: Date; }; declare class StripeSync { stripe: Stripe; postgresClient: PostgresClient; config: StripeSyncConfig; readonly resourceRegistry: Record; readonly sigmaRegistry: Record; webhook: StripeSyncWebhook; readonly sigma: SigmaSyncProcessor; accountId: string; private savedLogger; private previousLineCount; get sigmaSchemaName(): string; private get dataSchemaName(); private get syncMetadataSchemaName(); private quoteSyncMetadataSchemaName; private disableLogger; private enableLogger; private constructor(); /** * Create a new StripeSync instance. Resolves the default Stripe account, * stores it in the database, and makes the account ID available immediately. */ static create(config: StripeSyncConfig): Promise; /** * Get the Stripe account ID. Returns the default account ID, or resolves * a Connect sub-account ID when provided (Connect scenarios). */ getAccountId(objectAccountId?: string): Promise; /** * Get the current account being synced. Uses database lookup by API key hash, * with fallback to Stripe API if not found (first-time setup or new API key). * @param objectAccountId - Optional account ID from event data (Connect scenarios) */ getCurrentAccount(objectAccountId?: string): Promise; /** * Ordered for backfill: parents before children (products before prices, customers before subscriptions). * Order is determined by the `order` field in resourceRegistry. */ getSupportedSyncObjects(): Exclude[]; getSupportedSigmaObjects(): string[]; syncSingleEntity(stripeId: string): Promise; private getRegistryForObject; findOldestItem(listfn: NonNullable): Promise; createChunks(objects: StripeObject[], workerCount?: number): Promise<{ chunkCursors: Record; nonChunkTables: string[]; failedObjects: Array<{ tableName: string; error: string; }>; }>; /** * Build a map of table name → priority (order from resourceRegistry). * Used when creating sync object runs so workers process parents before children. */ private buildPriorityMap; initializeSegment(runKey: RunKey, objects: StripeObject[], workerCount: number): Promise; reconciliationSync(objects: StripeObject[], tableNames: string[], segmentedSync: boolean, triggeredBy?: string, interval?: number, workerCount?: number): Promise; fullSync(tables?: StripeObject[], segmentedSync?: boolean, workerCount?: number, rateLimit?: number, monitorProgress?: boolean, interval?: number): Promise<{ results: Record; totals: Record; totalSynced: number; skipped: string[]; errors: Array<{ object: string; message: string; }>; }>; upsertAny(items: { [Key: string]: any; }[], // eslint-disable-line @typescript-eslint/no-explicit-any accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; backfillAny(ids: string[], objectName: StripeObject, accountId: string, syncTimestamp?: string): Promise; /** * Upsert subscription items into a separate table and mark removed items as deleted. * Skips deleted subscriptions that have no items data. */ private syncSubscriptionItems; upsertSubscriptionItems(subscriptionItems: Stripe.SubscriptionItem[], accountId: string, syncTimestamp?: string): Promise; markDeletedSubscriptionItems(subscriptionId: string, currentSubItemIds: string[]): Promise<{ rowCount: number; }>; upsertActiveEntitlements(customerId: string, activeEntitlements: Stripe.Entitlements.ActiveEntitlement[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; fetchMissingEntities(ids: string[], fetch: (id: string) => Promise>): Promise; /** * Closes the database connection pool and cleans up resources. * Call this when you're done using the StripeSync instance. */ close(): Promise; printProgress(runKey: RunKey): Promise; /** * Periodically logs row counts for all tables, refreshing in place. * Returns the interval handle so the caller can clear it. */ startTableMonitor(intervalMs: number | undefined, runKey: RunKey): ReturnType; } type SyncTask = { object: string; cursor: string | null; pageCursor: string | null; created_gte: number; created_lte: number; }; declare class StripeSyncWorker { private readonly stripe; private readonly config; private readonly sigma; private readonly postgresClient; private readonly accountId; private readonly resourceRegistry; private readonly sigmaRegistry; private readonly runKey; private readonly upsertAny; private readonly taskLimit; private readonly rateLimit; private running; private loopPromise; private tasksCompleted; constructor(stripe: Stripe, config: StripeSyncConfig, sigma: SigmaSyncProcessor, postgresClient: PostgresClient, accountId: string, resourceRegistry: Record, sigmaRegistry: Record, runKey: RunKey, upsertAny: (items: { [Key: string]: any; }[], accountId: string, backfillRelated?: boolean) => Promise, taskLimit?: number, rateLimit?: number); start(): void; shutdown(): Promise; private loop; waitUntilDone(): Promise; fetchOnePage(object: string, cursor: string | null, pageCursor: string | null, config: ResourceConfig, created_gte?: number | null, created_lte?: number | null): Promise<{ data: unknown[]; has_more: boolean; }>; getNextTask(): Promise; updateTaskProgress(task: SyncTask, data: Stripe.Response>['data'], has_more: boolean): Promise; processSingleTask(task: SyncTask): Promise; private getConfigForTaskObject; } type EmbeddedMigration = { name: string; sql: string; }; declare const embeddedMigrations: EmbeddedMigration[]; type MigrationConfig = { databaseUrl: string; ssl?: ConnectionOptions; logger?: Logger; enableSigma?: boolean; stripeApiVersion?: string; openApiSpecPath?: string; openApiCacheDir?: string; schemaName?: string; /** Schema for sync metadata tables (accounts, _sync_runs, etc.). Defaults to schemaName. */ syncTablesSchemaName?: string; }; declare function runMigrations(config: MigrationConfig): Promise; /** * Run migrations from embedded content (for edge runtimes without filesystem migrations access). * This uses the same in-memory execution path as the Node bootstrap runner. */ declare function runMigrationsFromContent(config: MigrationConfig, migrations: EmbeddedMigration[]): Promise; /** * Hashes a Stripe API key using SHA-256 * Used to store API key hashes in the database for fast account lookups * without storing the actual API key or making Stripe API calls * * @param apiKey - The Stripe API key (e.g., sk_test_... or sk_live_...) * @returns SHA-256 hash of the API key as a hex string */ declare function hashApiKey(apiKey: string): string; interface WebhookProcessingResult { status: number; databaseUrl: string; event_type?: string; event_id?: string; error?: string; } interface StripeWebSocketOptions { stripeApiKey: string; onEvent: (event: StripeWebhookEvent) => Promise | WebhookProcessingResult | void; onReady?: (secret: string) => void; onError?: (error: Error) => void; onClose?: (code: number, reason: string) => void; } interface StripeWebSocketClient { close: () => void; isConnected: () => boolean; } interface StripeWebhookEvent { type: string; webhook_id: string; webhook_conversation_id: string; event_payload: string; http_headers: Record; endpoint: { url: string; status: string; }; } declare function createStripeWebSocketClient(options: StripeWebSocketOptions): Promise; export { type BaseResourceConfig, type EmbeddedMigration, type InstallationStatus, type Logger, PostgresClient, type ProcessNextParams, type ProcessNextResult, type ResourceConfig, type RevalidateEntity, SUPPORTED_WEBHOOK_EVENTS, type SigmaResourceConfig, type StripeListResourceConfig, StripeSync, type StripeSyncAccountState, type StripeSyncConfig, type StripeSyncState, StripeSyncWorker, type StripeWebSocketClient, type StripeWebSocketOptions, type StripeWebhookEvent, type Sync, type SyncEntitlementsParams, type SyncFeaturesParams, type SyncObject, type SyncParams, VERSION, type WebhookProcessingResult, createStripeWebSocketClient, embeddedMigrations, getTableName, hashApiKey, runMigrations, runMigrationsFromContent };