stripe-experiment-sync 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -41,6 +41,11 @@ declare class PostgresClient {
41
41
  raw_data: any;
42
42
  }, apiKeyHash: string): Promise<void>;
43
43
  getAllAccounts(): Promise<any[]>;
44
+ /**
45
+ * Get all accounts that have been synced to the database.
46
+ * Throws a descriptive error if the query fails.
47
+ */
48
+ getAllSyncedAccounts(): Promise<any[]>;
44
49
  /**
45
50
  * Looks up an account ID by API key hash
46
51
  * Uses the GIN index on api_key_hashes for fast lookups
@@ -58,6 +63,34 @@ declare class PostgresClient {
58
63
  getAccountRecordCounts(accountId: string): Promise<{
59
64
  [tableName: string]: number;
60
65
  }>;
66
+ deletePlan(id: string): Promise<boolean>;
67
+ deleteProduct(id: string): Promise<boolean>;
68
+ columnExists(table: string, column: string): Promise<boolean>;
69
+ deleteTaxId(id: string): Promise<boolean>;
70
+ deletePrice(id: string): Promise<boolean>;
71
+ deleteRemovedActiveEntitlements(customerId: string, currentActiveEntitlementIds: string[]): Promise<{
72
+ rowCount: number;
73
+ }>;
74
+ /**
75
+ * DANGEROUS: Delete an account and all associated data from the database
76
+ * This operation cannot be undone!
77
+ *
78
+ * @param accountId - The Stripe account ID to delete
79
+ * @param options - Options for deletion behavior
80
+ * @param options.dryRun - If true, only count records without deleting (default: false)
81
+ * @param options.useTransaction - If true, use transaction for atomic deletion (default: true)
82
+ * @returns Deletion summary with counts and warnings
83
+ */
84
+ dangerouslyDeleteSyncedAccountData(accountId: string, options?: {
85
+ dryRun?: boolean;
86
+ useTransaction?: boolean;
87
+ }): Promise<{
88
+ deletedAccountId: string;
89
+ deletedRecordCounts: {
90
+ [tableName: string]: number;
91
+ };
92
+ warnings: string[];
93
+ }>;
61
94
  deleteAccountWithCascade(accountId: string, useTransaction: boolean): Promise<{
62
95
  [tableName: string]: number;
63
96
  }>;
@@ -106,12 +139,47 @@ declare class PostgresClient {
106
139
  * Auto-cancels stale runs before checking.
107
140
  *
108
141
  * @param triggeredBy - Worker type (e.g., 'worker', 'sigma-worker'). Runs are isolated per triggeredBy.
109
- * @returns RunKey with isNew flag, or null if constraint violation (race condition)
142
+ * @returns RunKey with isNew flag. Always returns a run (retries on race condition).
110
143
  */
111
144
  getOrCreateSyncRun(accountId: string, triggeredBy?: string): Promise<{
112
145
  accountId: string;
113
146
  runStartedAt: Date;
114
147
  isNew: boolean;
148
+ }>;
149
+ /**
150
+ * Join an existing sync run or create a new one, and ensure object run rows exist.
151
+ *
152
+ * Combines getOrCreateSyncRun + createObjectRuns into a single atomic-ish operation.
153
+ * Object runs are created idempotently (ON CONFLICT DO NOTHING) so this is safe
154
+ * to call from multiple workers adding objects to the same run.
155
+ *
156
+ * @param accountId - The Stripe account ID
157
+ * @param triggeredBy - What triggered this sync (for observability)
158
+ * @param resourceNames - Database resource names (e.g. 'products', 'customers')
159
+ * @returns Run key with accountId and runStartedAt
160
+ */
161
+ joinOrCreateSyncRun(accountId: string, triggeredBy: string, resourceNames: string[], priorities?: Record<string, number>): Promise<{
162
+ accountId: string;
163
+ runStartedAt: Date;
164
+ }>;
165
+ /**
166
+ * Find a run that completed successfully (closed with no object errors)
167
+ * within the given time window.
168
+ *
169
+ * @param intervalSeconds - How far back to look, in seconds.
170
+ */
171
+ getCompletedRun(accountId: string, intervalSeconds: number): Promise<{
172
+ accountId: string;
173
+ runStartedAt: Date;
174
+ } | null>;
175
+ /**
176
+ * Start a reconciliation run only if no run completed successfully in the last 24 hours.
177
+ *
178
+ * @returns Run key if a new run was created, or null if a recent successful run exists.
179
+ */
180
+ reconciliationRun(accountId: string, triggeredBy: string, resourceNames: string[], interval?: number, priorities?: Record<string, number>): Promise<{
181
+ accountId: string;
182
+ runStartedAt: Date;
115
183
  } | null>;
116
184
  /**
117
185
  * Get the active sync run for an account (if any).
@@ -145,8 +213,11 @@ declare class PostgresClient {
145
213
  * All objects start as 'pending'.
146
214
  *
147
215
  * @param resourceNames - Database resource names (e.g. 'products', 'customers', NOT 'product', 'customer')
216
+ * @param priorities - Optional map of resource name → priority (from resourceRegistry order).
217
+ * Lower values are processed first.
148
218
  */
149
- createObjectRuns(accountId: string, runStartedAt: Date, resourceNames: string[]): Promise<void>;
219
+ createObjectRuns(accountId: string, runStartedAt: Date, resourceNames: string[], priorities?: Record<string, number>): Promise<void>;
220
+ createChunkedObjectRuns(accountId: string, runStartedAt: Date, chunkCursors: Record<string, number[]>, priorities?: Record<string, number>): Promise<void>;
150
221
  /**
151
222
  * Try to start an object sync (respects max_concurrent).
152
223
  * Returns true if claimed, false if already running or at concurrency limit.
@@ -155,6 +226,20 @@ declare class PostgresClient {
155
226
  * max_concurrent + 1 objects running. This is acceptable behavior.
156
227
  */
157
228
  tryStartObjectSync(accountId: string, runStartedAt: Date, object: string): Promise<boolean>;
229
+ /**
230
+ * Atomically claim the next pending task using FOR UPDATE SKIP LOCKED.
231
+ * Two concurrent workers will never claim the same row — the second worker
232
+ * skips the locked row and grabs the next one.
233
+ *
234
+ * Respects max_concurrent: returns null if already at the concurrency limit.
235
+ */
236
+ claimNextTask(accountId: string, runStartedAt: Date, rateLimit?: number): Promise<{
237
+ object: string;
238
+ cursor: string | null;
239
+ pageCursor: string | null;
240
+ created_gte: number | null;
241
+ created_lte: number | null;
242
+ } | null>;
158
243
  /**
159
244
  * Get object run details.
160
245
  */
@@ -169,27 +254,47 @@ declare class PostgresClient {
169
254
  * Update progress for an object sync.
170
255
  * Also touches updated_at for stale detection.
171
256
  */
172
- incrementObjectProgress(accountId: string, runStartedAt: Date, object: string, count: number): Promise<void>;
257
+ incrementObjectProgress(accountId: string, runStartedAt: Date, object: string, count: number, createdGte?: number, createdLte?: number): Promise<number>;
258
+ /**
259
+ * Atomically update an object sync row in a single round-trip.
260
+ * Only the fields present in `updates` are written; omitted fields are left unchanged.
261
+ * Auto-closes the sync run when all objects reach 'complete'.
262
+ */
263
+ updateSyncObject(accountId: string, runStartedAt: Date, object: string, createdGte: number, createdLte: number, updates: {
264
+ processedCount?: number;
265
+ cursor?: string | null;
266
+ status?: 'pending' | 'complete' | 'error';
267
+ pageCursor?: string | null;
268
+ errorMessage?: string;
269
+ }): Promise<number>;
173
270
  /**
174
271
  * Update the pagination page_cursor used for backfills using Stripe list calls.
175
272
  */
176
273
  updateObjectPageCursor(accountId: string, runStartedAt: Date, object: string, pageCursor: string | null): Promise<void>;
274
+ /**
275
+ * Release a running object back to pending with an updated page_cursor.
276
+ * Used after processing a single page when more pages remain.
277
+ */
278
+ releaseObjectSync(accountId: string, runStartedAt: Date, object: string, pageCursor: string, createdGte?: number, createdLte?: number): Promise<void>;
177
279
  /**
178
280
  * Clear the pagination page_cursor for an object sync.
179
281
  */
180
282
  clearObjectPageCursor(accountId: string, runStartedAt: Date, object: string): Promise<void>;
181
283
  /**
182
- * Update the cursor for an object sync.
183
- * Only updates if the new cursor is higher than the existing one (cursors should never decrease).
184
- * For numeric cursors (timestamps), uses GREATEST to ensure monotonic increase.
185
- * For non-numeric cursors, just sets the value directly.
284
+ * Clear the sync cursor on all previous completed runs for an object,
285
+ * so the next run starts from scratch (no created.gte filter).
186
286
  */
187
- updateObjectCursor(accountId: string, runStartedAt: Date, object: string, cursor: string | null): Promise<void>;
287
+ clearObjectCursorHistory(accountId: string, object: string, runStartedAt: Date): Promise<void>;
288
+ updateObjectCursor(accountId: string, runStartedAt: Date, object: string, cursor: string | null, createdGte?: number, createdLte?: number): Promise<void>;
188
289
  setObjectCursor(accountId: string, runStartedAt: Date, object: string, cursor: string | null): Promise<void>;
189
290
  /**
190
291
  * List object names for a run by status, optionally filtered to a subset.
191
292
  */
192
293
  listObjectsByStatus(accountId: string, runStartedAt: Date, status: string, objectFilter?: string[]): Promise<string[]>;
294
+ /**
295
+ * Get per-object processed counts for a sync run.
296
+ */
297
+ getObjectSyncedCounts(accountId: string, runStartedAt: Date): Promise<Record<string, number>>;
193
298
  /**
194
299
  * Get the highest cursor from previous syncs for an object type.
195
300
  * Uses only completed object runs.
@@ -216,16 +321,23 @@ declare class PostgresClient {
216
321
  * Useful for testing or resetting sync state.
217
322
  */
218
323
  deleteSyncRuns(accountId: string): Promise<void>;
324
+ /**
325
+ * Reset orphaned 'running' object runs back to 'pending'.
326
+ * Used for crash recovery: if a worker was killed mid-sync, the object run
327
+ * is left in 'running' state with no active worker. Resetting to 'pending'
328
+ * allows new workers to re-claim and finish the work.
329
+ */
330
+ resetStuckRunningObjects(accountId: string, runStartedAt: Date, stuckThresholdSeconds?: number): Promise<number>;
219
331
  /**
220
332
  * Mark an object sync as complete.
221
333
  * Auto-closes the run when all objects are done.
222
334
  */
223
- completeObjectSync(accountId: string, runStartedAt: Date, object: string): Promise<void>;
335
+ completeObjectSync(accountId: string, runStartedAt: Date, object: string, createdGte?: number, createdLte?: number): Promise<void>;
224
336
  /**
225
337
  * Mark an object sync as failed.
226
338
  * Auto-closes the run when all objects are done.
227
339
  */
228
- failObjectSync(accountId: string, runStartedAt: Date, object: string, errorMessage: string): Promise<void>;
340
+ failObjectSync(accountId: string, runStartedAt: Date, object: string, errorMessage: string, createdGte?: number, createdLte?: number): Promise<void>;
229
341
  /**
230
342
  * Check if any object in a run has errored.
231
343
  */
@@ -243,6 +355,7 @@ declare class PostgresClient {
243
355
  * Check if all objects in a run are complete (or error).
244
356
  */
245
357
  areAllObjectsComplete(accountId: string, runStartedAt: Date): Promise<boolean>;
358
+ countObjectRuns(accountId: string, runStartedAt: Date): Promise<number>;
246
359
  /**
247
360
  * Closes the database connection pool and cleans up resources.
248
361
  * Call this when you're done using the PostgresClient instance.
@@ -325,6 +438,8 @@ type StripeSyncConfig = {
325
438
  sigmaSchemaName?: string;
326
439
  /** Stripe account ID. If not provided, will be retrieved from Stripe API. Used as fallback option. */
327
440
  stripeAccountId?: string;
441
+ /** Optional Stripe partner ID embedded in appInfo for telemetry (e.g. "pp_supabase"). */
442
+ partnerId?: string;
328
443
  /** Stripe webhook signing secret for validating webhook signatures. Required if not using managed webhooks. */
329
444
  stripeWebhookSecret?: string;
330
445
  /** Stripe API version for the webhooks, defaults to 2020-08-27 */
@@ -347,29 +462,8 @@ type StripeSyncConfig = {
347
462
  revalidateObjectsViaStripeApi?: Array<RevalidateEntity>;
348
463
  /** @deprecated Use `poolConfig` instead. */
349
464
  maxPostgresConnections?: number;
350
- poolConfig: PoolConfig;
465
+ poolConfig?: PoolConfig;
351
466
  logger?: Logger;
352
- /**
353
- * Maximum number of retry attempts for 429 rate limit errors.
354
- * Default: 5
355
- */
356
- maxRetries?: number;
357
- /**
358
- * Initial delay in milliseconds before first retry attempt.
359
- * Delay increases exponentially: 1s, 2s, 4s, 8s, 16s, etc.
360
- * Default: 1000 (1 second)
361
- */
362
- initialRetryDelayMs?: number;
363
- /**
364
- * Maximum delay in milliseconds between retry attempts.
365
- * Default: 60000 (60 seconds)
366
- */
367
- maxRetryDelayMs?: number;
368
- /**
369
- * Random jitter in milliseconds added to retry delays to prevent thundering herd.
370
- * Default: 500
371
- */
372
- retryJitterMs?: number;
373
467
  /**
374
468
  * Maximum number of customers to process concurrently when syncing payment methods.
375
469
  * Lower values reduce API load but increase sync time.
@@ -378,32 +472,10 @@ type StripeSyncConfig = {
378
472
  maxConcurrentCustomers?: number;
379
473
  };
380
474
  type SyncObject = 'all' | 'customer' | 'customer_with_entitlements' | 'invoice' | 'price' | 'product' | 'subscription' | 'subscription_schedules' | 'setup_intent' | 'payment_method' | 'dispute' | 'charge' | 'payment_intent' | 'plan' | 'tax_id' | 'credit_note' | 'early_fraud_warning' | 'refund' | 'checkout_sessions';
475
+ declare const SUPPORTED_WEBHOOK_EVENTS: Stripe.WebhookEndpointCreateParams.EnabledEvent[];
381
476
  interface Sync {
382
477
  synced: number;
383
478
  }
384
- interface SyncBackfill {
385
- products?: Sync;
386
- prices?: Sync;
387
- plans?: Sync;
388
- customers?: Sync;
389
- subscriptions?: Sync;
390
- subscriptionSchedules?: Sync;
391
- invoices?: Sync;
392
- setupIntents?: Sync;
393
- paymentIntents?: Sync;
394
- paymentMethods?: Sync;
395
- disputes?: Sync;
396
- charges?: Sync;
397
- taxIds?: Sync;
398
- creditNotes?: Sync;
399
- earlyFraudWarnings?: Sync;
400
- refunds?: Sync;
401
- checkoutSessions?: Sync;
402
- subscriptionItemChangeEventsV2Beta?: Sync;
403
- exchangeRatesFromUsd?: Sync;
404
- /** Sigma-backed results by table name (e.g. subscription_item_change_events_v2_beta). */
405
- sigma?: Record<string, Sync>;
406
- }
407
479
  interface SyncParams {
408
480
  created?: {
409
481
  /**
@@ -463,8 +535,16 @@ interface ProcessNextParams extends SyncParams {
463
535
  type BaseResourceConfig = {
464
536
  /** Backfill order: lower numbers sync first; parents before children for FK dependencies */
465
537
  order: number;
538
+ /** Database table name for this resource (e.g. 'customers', 'invoices') */
539
+ tableName: string;
466
540
  /** Whether this resource supports incremental sync via 'created' filter or cursor */
467
541
  supportsCreatedFilter: boolean;
542
+ /** Whether this resource is included in sync runs by default. Default: true */
543
+ sync?: boolean;
544
+ /** Resource types that must be backfilled before this one (e.g. price depends on product) */
545
+ dependencies?: string[];
546
+ /** Function to check if an entity is in a final state and doesn't need revalidation */
547
+ isFinalState?: (entity: any) => boolean;
468
548
  };
469
549
  type StripeListResourceConfig = BaseResourceConfig & {
470
550
  /** Function to list items from Stripe API */
@@ -474,8 +554,12 @@ type StripeListResourceConfig = BaseResourceConfig & {
474
554
  data: unknown[];
475
555
  has_more: boolean;
476
556
  }>;
477
- /** Function to upsert items to database */
478
- upsertFn: (items: unknown[], accountId: string, backfillRelated?: boolean) => Promise<unknown[] | void>;
557
+ /** Function to retrieve a single item by ID from Stripe API */
558
+ retrieveFn: (id: string) => Promise<Stripe.Response<any>>;
559
+ /** Optional list of sub-resources to expand during upsert/fetching (e.g. 'refunds', 'listLineItems') */
560
+ listExpands?: Record<string, (id: string) => Promise<Stripe.ApiList<{
561
+ id?: string;
562
+ }>>>[];
479
563
  /** discriminator */
480
564
  sigma?: undefined;
481
565
  };
@@ -491,7 +575,11 @@ type SigmaResourceConfig = BaseResourceConfig & {
491
575
  /** discriminator */
492
576
  listFn?: undefined;
493
577
  /** discriminator */
494
- upsertFn?: undefined;
578
+ retrieveFn?: undefined;
579
+ /** discriminator */
580
+ listExpands?: Record<string, (id: string) => Promise<Stripe.ApiList<{
581
+ id?: string;
582
+ }>>>[];
495
583
  };
496
584
  /** Union of all resource configuration types */
497
585
  type ResourceConfig = StripeListResourceConfig | SigmaResourceConfig;
@@ -526,6 +614,87 @@ interface StripeSyncState {
526
614
  sync_status: StripeSyncAccountState[];
527
615
  }
528
616
 
617
+ type SigmaSyncProcessorConfig = {
618
+ stripeSecretKey: string;
619
+ enableSigma?: boolean;
620
+ sigmaPageSizeOverride?: number;
621
+ sigmaSchemaName?: string;
622
+ logger?: Logger;
623
+ };
624
+ /**
625
+ * Handles all Sigma-specific sync logic:
626
+ * - Building the sigma portion of the resource registry
627
+ * - Fetching a single Sigma page (query + CSV parse + upsert)
628
+ * - Resolving fallback cursors from destination tables
629
+ * - Utility helpers (isSigmaResource, getSupportedSigmaObjects)
630
+ */
631
+ declare class SigmaSyncProcessor {
632
+ private readonly postgresClient;
633
+ private readonly config;
634
+ get sigmaSchemaName(): string;
635
+ constructor(postgresClient: PostgresClient, config: SigmaSyncProcessorConfig);
636
+ /**
637
+ * Build the sigma portion of the resource registry.
638
+ * Returns entries keyed by sigma table name with order starting after `maxCoreOrder`.
639
+ */
640
+ buildSigmaRegistryEntries(maxCoreOrder: number): Record<string, ResourceConfig>;
641
+ /**
642
+ * Check whether a resource exists in the sigma registry.
643
+ */
644
+ isSigmaResource(sigmaRegistry: Record<string, ResourceConfig>, object: string): boolean;
645
+ /**
646
+ * Get the list of Sigma-backed object types that can be synced.
647
+ * Only returns sigma objects when enableSigma is true.
648
+ */
649
+ getSupportedSigmaObjects(sigmaRegistry: Record<string, ResourceConfig>): string[];
650
+ /**
651
+ * Fetch the latest cursor from the destination table when no run cursor exists.
652
+ * Queries the sigma schema for the max cursor column values.
653
+ */
654
+ getSigmaFallbackCursorFromDestination(accountId: string, sigmaConfig: SigmaIngestionConfig): Promise<string | null>;
655
+ /**
656
+ * Fetch one page of Sigma data, upsert to Postgres, and advance the cursor.
657
+ */
658
+ fetchOneSigmaPage(accountId: string, resourceName: string, runStartedAt: Date, cursor: string | null, sigmaConfig: SigmaIngestionConfig): Promise<ProcessNextResult>;
659
+ }
660
+
661
+ type StripeSyncWebhookDeps = {
662
+ stripe: Stripe;
663
+ postgresClient: PostgresClient;
664
+ config: StripeSyncConfig;
665
+ readonly accountId: string;
666
+ getAccountId: (objectAccountId?: string) => Promise<string>;
667
+ upsertAny: (items: any[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string) => Promise<unknown[]>;
668
+ resourceRegistry: Record<string, ResourceConfig>;
669
+ };
670
+ declare class StripeSyncWebhook {
671
+ private readonly deps;
672
+ constructor(deps: StripeSyncWebhookDeps);
673
+ processWebhook(payload: Buffer | Uint8Array | string, signature: string | undefined): Promise<void>;
674
+ processEvent(event: Stripe.Event): Promise<void>;
675
+ getSupportedEventTypes(): Stripe.WebhookEndpointCreateParams.EnabledEvent[];
676
+ handleDeletedEvent(event: Stripe.Event, accountId: string): Promise<void>;
677
+ defaultHandler(event: Stripe.Event, accountId: string): Promise<void>;
678
+ handleEntitlementSummaryEvent(event: Stripe.Event, accountId: string): Promise<void>;
679
+ private static readonly RESOURCE_DELETE_EVENTS;
680
+ private isDeleteEvent;
681
+ handleAnyEvent(event: Stripe.Event, accountId: string): Promise<void>;
682
+ getSyncTimestamp(event: Stripe.Event, refetched: boolean): string;
683
+ findOrCreateManagedWebhook(url: string, params?: Omit<Stripe.WebhookEndpointCreateParams, 'url'>): Promise<Stripe.WebhookEndpoint>;
684
+ getManagedWebhook(id: string): Promise<Stripe.WebhookEndpoint | null>;
685
+ getManagedWebhookByUrl(url: string): Promise<Stripe.WebhookEndpoint | null>;
686
+ listManagedWebhooks(): Promise<Array<Stripe.WebhookEndpoint>>;
687
+ updateManagedWebhook(id: string, params: Stripe.WebhookEndpointUpdateParams): Promise<Stripe.WebhookEndpoint>;
688
+ deleteManagedWebhook(id: string): Promise<boolean>;
689
+ upsertManagedWebhooks(webhooks: Array<Stripe.WebhookEndpoint>, accountId: string, syncTimestamp?: string): Promise<Array<Stripe.WebhookEndpoint>>;
690
+ }
691
+
692
+ type StripeObject = 'product' | 'price' | 'plan' | 'customer' | 'subscription' | 'subscription_schedules' | 'invoice' | 'charge' | 'setup_intent' | 'payment_method' | 'payment_intent' | 'tax_id' | 'credit_note' | 'dispute' | 'early_fraud_warning' | 'refund' | 'checkout_sessions' | 'active_entitlements' | 'review';
693
+ /**
694
+ * Get the database table name for a SyncObject type from the resource registry.
695
+ */
696
+ declare function getTableName(object: string, registry: Record<string, ResourceConfig>): string;
697
+
529
698
  /**
530
699
  * Identifies a specific sync run.
531
700
  */
@@ -534,177 +703,58 @@ type RunKey = {
534
703
  runStartedAt: Date;
535
704
  };
536
705
  declare class StripeSync {
537
- private config;
538
706
  stripe: Stripe;
539
707
  postgresClient: PostgresClient;
540
- private readonly resourceRegistry;
541
- private get sigmaSchemaName();
542
- constructor(config: StripeSyncConfig);
543
- private buildResourceRegistry;
544
- private isSigmaResource;
545
- private sigmaResultKey;
708
+ config: StripeSyncConfig;
709
+ readonly resourceRegistry: Record<StripeObject, ResourceConfig>;
710
+ readonly sigmaRegistry: Record<string, ResourceConfig>;
711
+ webhook: StripeSyncWebhook;
712
+ readonly sigma: SigmaSyncProcessor;
713
+ accountId: string;
714
+ private savedLogger;
715
+ private previousLineCount;
716
+ get sigmaSchemaName(): string;
717
+ private disableLogger;
718
+ private enableLogger;
719
+ private constructor();
546
720
  /**
547
- * Get the Stripe account ID. Delegates to getCurrentAccount() for the actual lookup.
721
+ * Create a new StripeSync instance. Resolves the default Stripe account,
722
+ * stores it in the database, and makes the account ID available immediately.
548
723
  */
549
- getAccountId(objectAccountId?: string): Promise<string>;
724
+ static create(config: StripeSyncConfig): Promise<StripeSync>;
550
725
  /**
551
- * Upsert Stripe account information to the database
552
- * @param account - Stripe account object
553
- * @param apiKeyHash - SHA-256 hash of API key to store for fast lookups
726
+ * Get the Stripe account ID. Returns the default account ID, or resolves
727
+ * a Connect sub-account ID when provided (Connect scenarios).
554
728
  */
555
- private upsertAccount;
729
+ getAccountId(objectAccountId?: string): Promise<string>;
556
730
  /**
557
731
  * Get the current account being synced. Uses database lookup by API key hash,
558
732
  * with fallback to Stripe API if not found (first-time setup or new API key).
559
733
  * @param objectAccountId - Optional account ID from event data (Connect scenarios)
560
734
  */
561
- getCurrentAccount(objectAccountId?: string): Promise<Stripe.Account | null>;
562
- /**
563
- * Get all accounts that have been synced to the database
564
- */
565
- getAllSyncedAccounts(): Promise<Stripe.Account[]>;
566
- /**
567
- * DANGEROUS: Delete an account and all associated data from the database
568
- * This operation cannot be undone!
569
- *
570
- * @param accountId - The Stripe account ID to delete
571
- * @param options - Options for deletion behavior
572
- * @param options.dryRun - If true, only count records without deleting (default: false)
573
- * @param options.useTransaction - If true, use transaction for atomic deletion (default: true)
574
- * @returns Deletion summary with counts and warnings
575
- */
576
- dangerouslyDeleteSyncedAccountData(accountId: string, options?: {
577
- dryRun?: boolean;
578
- useTransaction?: boolean;
579
- }): Promise<{
580
- deletedAccountId: string;
581
- deletedRecordCounts: {
582
- [tableName: string]: number;
583
- };
584
- warnings: string[];
585
- }>;
586
- processWebhook(payload: Buffer | string, signature: string | undefined): Promise<void>;
587
- private readonly eventHandlers;
588
- processEvent(event: Stripe.Event): Promise<void>;
589
- /**
590
- * Returns an array of all webhook event types that this sync engine can handle.
591
- * Useful for configuring webhook endpoints with specific event subscriptions.
592
- */
593
- getSupportedEventTypes(): Stripe.WebhookEndpointCreateParams.EnabledEvent[];
735
+ getCurrentAccount(objectAccountId?: string): Promise<Stripe.Account>;
594
736
  /**
595
- * Returns an array of all object types that can be synced via processNext/processUntilDone.
596
737
  * Ordered for backfill: parents before children (products before prices, customers before subscriptions).
597
738
  * Order is determined by the `order` field in resourceRegistry.
598
739
  */
599
740
  getSupportedSyncObjects(): Exclude<SyncObject, 'all' | 'customer_with_entitlements'>[];
600
- /**
601
- * Get the list of Sigma-backed object types that can be synced.
602
- * Only returns sigma objects when enableSigma is true.
603
- *
604
- * @returns Array of sigma object names (e.g. 'subscription_item_change_events_v2_beta')
605
- */
606
741
  getSupportedSigmaObjects(): string[];
607
- private handleChargeEvent;
608
- private handleCustomerDeletedEvent;
609
- private handleCustomerEvent;
610
- private handleCheckoutSessionEvent;
611
- private handleSubscriptionEvent;
612
- private handleTaxIdEvent;
613
- private handleTaxIdDeletedEvent;
614
- private handleInvoiceEvent;
615
- private handleProductEvent;
616
- private handleProductDeletedEvent;
617
- private handlePriceEvent;
618
- private handlePriceDeletedEvent;
619
- private handlePlanEvent;
620
- private handlePlanDeletedEvent;
621
- private handleSetupIntentEvent;
622
- private handleSubscriptionScheduleEvent;
623
- private handlePaymentMethodEvent;
624
- private handleDisputeEvent;
625
- private handlePaymentIntentEvent;
626
- private handleCreditNoteEvent;
627
- private handleEarlyFraudWarningEvent;
628
- private handleRefundEvent;
629
- private handleReviewEvent;
630
- private handleEntitlementSummaryEvent;
631
- private getSyncTimestamp;
632
- private shouldRefetchEntity;
633
- private fetchOrUseWebhookData;
634
- syncSingleEntity(stripeId: string): Promise<Stripe.Product[] | Stripe.Price[] | (Stripe.Customer | Stripe.DeletedCustomer)[] | Stripe.Subscription[] | Stripe.Invoice[] | Stripe.Charge[] | Stripe.SetupIntent[] | Stripe.PaymentMethod[] | Stripe.PaymentIntent[] | Stripe.TaxId[] | Stripe.CreditNote[] | Stripe.Dispute[] | Stripe.Radar.EarlyFraudWarning[] | Stripe.Refund[] | Stripe.Checkout.Session[] | Stripe.Review[] | Stripe.Entitlements.Feature[] | undefined>;
635
- /**
636
- * Process one page of items for the specified object type.
637
- * Returns the number of items processed and whether there are more pages.
638
- *
639
- * This method is designed for queue-based consumption where each page
640
- * is processed as a separate job. Uses the observable sync system for tracking.
641
- *
642
- * @param object - The Stripe object type to sync (e.g., 'customer', 'product')
643
- * @param params - Optional parameters for filtering and run context
644
- * @returns ProcessNextResult with processed count, hasMore flag, and runStartedAt
645
- *
646
- * @example
647
- * ```typescript
648
- * // Queue worker
649
- * const { hasMore, runStartedAt } = await stripeSync.processNext('customer')
650
- * if (hasMore) {
651
- * await queue.send({ object: 'customer', runStartedAt })
652
- * }
653
- * ```
654
- */
655
- processNext(object: Exclude<SyncObject, 'all' | 'customer_with_entitlements'>, params?: ProcessNextParams): Promise<ProcessNextResult>;
656
- private appendMigrationHint;
657
- /**
658
- * Get the database resource name for a SyncObject type
659
- */
660
- private getResourceName;
661
- /**
662
- * Fetch one page of items from Stripe and upsert to database.
663
- * Uses resourceRegistry for DRY list/upsert operations.
664
- * Uses the observable sync system for tracking progress.
665
- */
666
- private fetchOnePage;
667
- private getSigmaFallbackCursorFromDestination;
668
- private fetchOneSigmaPage;
669
- /**
670
- * Process all pages for all (or specified) object types until complete.
671
- *
672
- * @param params - Optional parameters for filtering and specifying object types
673
- * @returns SyncBackfill with counts for each synced resource type
674
- */
675
- /**
676
- * Process all pages for a single object type until complete.
677
- * Loops processNext() internally until hasMore is false.
678
- *
679
- * @param object - The object type to sync
680
- * @param runStartedAt - The sync run to use (for sharing across objects)
681
- * @param params - Optional sync parameters
682
- * @returns Sync result with count of synced items
683
- */
684
- private processObjectUntilDone;
742
+ syncSingleEntity(stripeId: string): Promise<void>;
743
+ private getRegistryForObject;
744
+ findOldestItem(listfn: NonNullable<ResourceConfig['listFn']>): Promise<number | null>;
745
+ createChunks(objects: StripeObject[], workerCount?: number): Promise<{
746
+ chunkCursors: Record<string, number[]>;
747
+ nonChunkTables: string[];
748
+ }>;
685
749
  /**
686
- * Join existing sync run or create a new one.
687
- * Returns sync run key and list of supported objects to sync.
688
- *
689
- * Cooperative behavior: If a sync run already exists, joins it instead of failing.
690
- * This is used by workers and background processes that should cooperate.
691
- *
692
- * @param triggeredBy - What triggered this sync (for observability)
693
- * @param objectFilter - Optional specific object to sync (e.g. 'payment_intent'). If 'all' or undefined, syncs all objects.
694
- * @returns Run key and list of objects to sync
750
+ * Build a map of table name priority (order from resourceRegistry).
751
+ * Used when creating sync object runs so workers process parents before children.
695
752
  */
696
- joinOrCreateSyncRun(triggeredBy?: string, objectFilter?: SyncObject): Promise<{
697
- runKey: RunKey;
698
- objects: Exclude<SyncObject, 'all' | 'customer_with_entitlements'>[];
699
- }>;
700
- private applySyncBackfillResult;
701
- processUntilDoneParallel(params?: SyncParams & {
702
- maxParallel?: number;
703
- triggeredBy?: string;
704
- continueOnError?: boolean;
705
- skipInaccessibleSigmaTables?: boolean;
706
- }): Promise<{
707
- results: SyncBackfill;
753
+ private buildPriorityMap;
754
+ initializeSegment(runKey: RunKey, objects: StripeObject[], workerCount: number): Promise<RunKey>;
755
+ reconciliationSync(objects: StripeObject[], tableNames: string[], segmentedSync: boolean, triggeredBy?: string, interval?: number, workerCount?: number): Promise<RunKey | null>;
756
+ fullSync(tables?: StripeObject[], segmentedSync?: boolean, workerCount?: number, rateLimit?: number, monitorProgress?: boolean, interval?: number): Promise<{
757
+ results: Record<string, Sync>;
708
758
  totals: Record<string, number>;
709
759
  totalSynced: number;
710
760
  skipped: string[];
@@ -713,120 +763,80 @@ declare class StripeSync {
713
763
  message: string;
714
764
  }>;
715
765
  }>;
716
- processUntilDone(params?: SyncParams): Promise<SyncBackfill>;
717
- /**
718
- * Internal implementation of processUntilDone with an existing run.
719
- */
720
- private processUntilDoneWithRun;
721
- /**
722
- * Sync payment methods with an existing run (special case - iterates customers)
723
- */
724
- private syncPaymentMethodsWithRun;
725
- syncProducts(syncParams?: SyncParams): Promise<Sync>;
726
- syncPrices(syncParams?: SyncParams): Promise<Sync>;
727
- syncPlans(syncParams?: SyncParams): Promise<Sync>;
728
- syncCustomers(syncParams?: SyncParams): Promise<Sync>;
729
- syncSubscriptions(syncParams?: SyncParams): Promise<Sync>;
730
- syncSubscriptionSchedules(syncParams?: SyncParams): Promise<Sync>;
731
- syncInvoices(syncParams?: SyncParams): Promise<Sync>;
732
- syncCharges(syncParams?: SyncParams): Promise<Sync>;
733
- syncSetupIntents(syncParams?: SyncParams): Promise<Sync>;
734
- syncPaymentIntents(syncParams?: SyncParams): Promise<Sync>;
735
- syncTaxIds(syncParams?: SyncParams): Promise<Sync>;
736
- syncPaymentMethods(syncParams?: SyncParams): Promise<Sync>;
737
- syncDisputes(syncParams?: SyncParams): Promise<Sync>;
738
- syncEarlyFraudWarnings(syncParams?: SyncParams): Promise<Sync>;
739
- syncRefunds(syncParams?: SyncParams): Promise<Sync>;
740
- syncCreditNotes(syncParams?: SyncParams): Promise<Sync>;
741
- syncFeatures(syncParams?: SyncFeaturesParams): Promise<Sync>;
742
- syncEntitlements(customerId: string, syncParams?: SyncEntitlementsParams): Promise<Sync>;
743
- syncCheckoutSessions(syncParams?: SyncParams): Promise<Sync>;
744
- /**
745
- * Helper to wrap a sync operation in the observable sync system.
746
- * Creates/gets a sync run, sets up the object run, gets cursor, and handles completion.
747
- *
748
- * @param resourceName - The resource being synced (e.g., 'products', 'customers')
749
- * @param triggeredBy - What triggered this sync (for observability)
750
- * @param fn - The sync function to execute, receives cursor and runStartedAt
751
- * @returns The result of the sync function
752
- */
753
- private withSyncRun;
754
- private fetchAndUpsert;
755
- private upsertCharges;
756
- private backfillCharges;
757
- private backfillPaymentIntents;
758
- private upsertCreditNotes;
759
- upsertCheckoutSessions(checkoutSessions: Stripe.Checkout.Session[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.Checkout.Session[]>;
760
- upsertEarlyFraudWarning(earlyFraudWarnings: Stripe.Radar.EarlyFraudWarning[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.Radar.EarlyFraudWarning[]>;
761
- upsertRefunds(refunds: Stripe.Refund[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.Refund[]>;
762
- upsertReviews(reviews: Stripe.Review[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.Review[]>;
763
- upsertCustomers(customers: (Stripe.Customer | Stripe.DeletedCustomer)[], accountId: string, syncTimestamp?: string): Promise<(Stripe.Customer | Stripe.DeletedCustomer)[]>;
764
- backfillCustomers(customerIds: string[], accountId: string): Promise<void>;
765
- upsertDisputes(disputes: Stripe.Dispute[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.Dispute[]>;
766
- upsertInvoices(invoices: Stripe.Invoice[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.Invoice[]>;
767
- backfillInvoices: (invoiceIds: string[], accountId: string) => Promise<void>;
768
- backfillPrices: (priceIds: string[], accountId: string) => Promise<void>;
769
- upsertPlans(plans: Stripe.Plan[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.Plan[]>;
770
- deletePlan(id: string): Promise<boolean>;
771
- upsertPrices(prices: Stripe.Price[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.Price[]>;
772
- deletePrice(id: string): Promise<boolean>;
773
- upsertProducts(products: Stripe.Product[], accountId: string, syncTimestamp?: string): Promise<Stripe.Product[]>;
774
- deleteProduct(id: string): Promise<boolean>;
775
- backfillProducts(productIds: string[], accountId: string): Promise<void>;
776
- upsertPaymentIntents(paymentIntents: Stripe.PaymentIntent[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.PaymentIntent[]>;
777
- upsertPaymentMethods(paymentMethods: Stripe.PaymentMethod[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.PaymentMethod[]>;
778
- upsertSetupIntents(setupIntents: Stripe.SetupIntent[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.SetupIntent[]>;
779
- upsertTaxIds(taxIds: Stripe.TaxId[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.TaxId[]>;
780
- deleteTaxId(id: string): Promise<boolean>;
766
+ upsertAny(items: {
767
+ [Key: string]: any;
768
+ }[], // eslint-disable-line @typescript-eslint/no-explicit-any
769
+ accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<unknown[]>;
770
+ backfillAny(ids: string[], objectName: StripeObject, accountId: string, syncTimestamp?: string): Promise<unknown[]>;
771
+ /**
772
+ * Upsert subscription items into a separate table and mark removed items as deleted.
773
+ * Skips deleted subscriptions that have no items data.
774
+ */
775
+ private syncSubscriptionItems;
781
776
  upsertSubscriptionItems(subscriptionItems: Stripe.SubscriptionItem[], accountId: string, syncTimestamp?: string): Promise<void>;
782
- fillCheckoutSessionsLineItems(checkoutSessionIds: string[], accountId: string, syncTimestamp?: string): Promise<void>;
783
- upsertCheckoutSessionLineItems(lineItems: Stripe.LineItem[], checkoutSessionId: string, accountId: string, syncTimestamp?: string): Promise<void>;
784
777
  markDeletedSubscriptionItems(subscriptionId: string, currentSubItemIds: string[]): Promise<{
785
778
  rowCount: number;
786
779
  }>;
787
- upsertSubscriptionSchedules(subscriptionSchedules: Stripe.SubscriptionSchedule[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.SubscriptionSchedule[]>;
788
- upsertSubscriptions(subscriptions: Stripe.Subscription[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<Stripe.Subscription[]>;
789
- deleteRemovedActiveEntitlements(customerId: string, currentActiveEntitlementIds: string[]): Promise<{
790
- rowCount: number;
791
- }>;
792
- upsertFeatures(features: Stripe.Entitlements.Feature[], accountId: string, syncTimestamp?: string): Promise<Stripe.Entitlements.Feature[]>;
793
- backfillFeatures(featureIds: string[], accountId: string): Promise<void>;
794
- upsertActiveEntitlements(customerId: string, activeEntitlements: Stripe.Entitlements.ActiveEntitlement[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<{
795
- id: string;
796
- object: "entitlements.active_entitlement";
797
- feature: string;
798
- customer: string;
799
- livemode: boolean;
800
- lookup_key: string;
801
- }[]>;
802
- findOrCreateManagedWebhook(url: string, params?: Omit<Stripe.WebhookEndpointCreateParams, 'url'>): Promise<Stripe.WebhookEndpoint>;
803
- getManagedWebhook(id: string): Promise<Stripe.WebhookEndpoint | null>;
804
- /**
805
- * Get a managed webhook by URL and account ID.
806
- * Used for race condition recovery: when createManagedWebhook hits a unique constraint
807
- * violation (another instance created the webhook), we need to fetch the existing webhook
808
- * by URL since we only know the URL, not the ID of the webhook that won the race.
809
- */
810
- getManagedWebhookByUrl(url: string): Promise<Stripe.WebhookEndpoint | null>;
811
- listManagedWebhooks(): Promise<Array<Stripe.WebhookEndpoint>>;
812
- updateManagedWebhook(id: string, params: Stripe.WebhookEndpointUpdateParams): Promise<Stripe.WebhookEndpoint>;
813
- deleteManagedWebhook(id: string): Promise<boolean>;
814
- upsertManagedWebhooks(webhooks: Array<Stripe.WebhookEndpoint>, accountId: string, syncTimestamp?: string): Promise<Array<Stripe.WebhookEndpoint>>;
815
- backfillSubscriptions(subscriptionIds: string[], accountId: string): Promise<void>;
816
- backfillSubscriptionSchedules: (subscriptionIds: string[], accountId: string) => Promise<void>;
817
- /**
818
- * Stripe only sends the first 10 entries by default, the option will actively fetch all entries.
819
- * Uses manual pagination - each fetch() gets automatic retry protection.
820
- */
821
- private expandEntity;
822
- private fetchMissingEntities;
780
+ upsertActiveEntitlements(customerId: string, activeEntitlements: Stripe.Entitlements.ActiveEntitlement[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<unknown[]>;
781
+ fetchMissingEntities<T>(ids: string[], fetch: (id: string) => Promise<Stripe.Response<T>>): Promise<T[]>;
823
782
  /**
824
783
  * Closes the database connection pool and cleans up resources.
825
784
  * Call this when you're done using the StripeSync instance.
826
785
  */
827
786
  close(): Promise<void>;
787
+ printProgress(runKey: RunKey): Promise<void>;
788
+ /**
789
+ * Periodically logs row counts for all tables, refreshing in place.
790
+ * Returns the interval handle so the caller can clear it.
791
+ */
792
+ startTableMonitor(intervalMs: number | undefined, runKey: RunKey): ReturnType<typeof setInterval>;
828
793
  }
829
794
 
795
+ type SyncTask = {
796
+ object: string;
797
+ cursor: string | null;
798
+ pageCursor: string | null;
799
+ created_gte: number;
800
+ created_lte: number;
801
+ };
802
+ declare class StripeSyncWorker {
803
+ private readonly stripe;
804
+ private readonly config;
805
+ private readonly sigma;
806
+ private readonly postgresClient;
807
+ private readonly accountId;
808
+ private readonly resourceRegistry;
809
+ private readonly sigmaRegistry;
810
+ private readonly runKey;
811
+ private readonly upsertAny;
812
+ private readonly taskLimit;
813
+ private readonly rateLimit;
814
+ private running;
815
+ private loopPromise;
816
+ private tasksCompleted;
817
+ constructor(stripe: Stripe, config: StripeSyncConfig, sigma: SigmaSyncProcessor, postgresClient: PostgresClient, accountId: string, resourceRegistry: Record<string, ResourceConfig>, sigmaRegistry: Record<string, ResourceConfig>, runKey: RunKey, upsertAny: (items: {
818
+ [Key: string]: any;
819
+ }[], accountId: string, backfillRelated?: boolean) => Promise<unknown[] | void>, taskLimit?: number, rateLimit?: number);
820
+ start(): void;
821
+ shutdown(): Promise<void>;
822
+ private loop;
823
+ waitUntilDone(): Promise<void>;
824
+ fetchOnePage(object: string, cursor: string | null, pageCursor: string | null, config: ResourceConfig, created_gte?: number | null, created_lte?: number | null): Promise<{
825
+ data: unknown[];
826
+ has_more: boolean;
827
+ }>;
828
+ getNextTask(): Promise<SyncTask | null>;
829
+ updateTaskProgress(task: SyncTask, data: Stripe.Response<Stripe.ApiList<unknown>>['data'], has_more: boolean): Promise<void>;
830
+ processSingleTask(task: SyncTask): Promise<ProcessNextResult>;
831
+ private getConfigForTaskObject;
832
+ }
833
+
834
+ type EmbeddedMigration = {
835
+ name: string;
836
+ sql: string;
837
+ };
838
+ declare const embeddedMigrations: EmbeddedMigration[];
839
+
830
840
  type MigrationConfig = {
831
841
  databaseUrl: string;
832
842
  ssl?: ConnectionOptions;
@@ -834,6 +844,11 @@ type MigrationConfig = {
834
844
  enableSigma?: boolean;
835
845
  };
836
846
  declare function runMigrations(config: MigrationConfig): Promise<void>;
847
+ /**
848
+ * Run migrations from embedded content (for use in edge functions without filesystem access).
849
+ * This is compatible with pg-node-migrations table format.
850
+ */
851
+ declare function runMigrationsFromContent(config: MigrationConfig, migrations: EmbeddedMigration[]): Promise<void>;
837
852
 
838
853
  /**
839
854
  * Hashes a Stripe API key using SHA-256
@@ -878,4 +893,4 @@ declare function createStripeWebSocketClient(options: StripeWebSocketOptions): P
878
893
 
879
894
  declare const VERSION: string;
880
895
 
881
- export { type BaseResourceConfig, type InstallationStatus, type Logger, PostgresClient, type ProcessNextParams, type ProcessNextResult, type ResourceConfig, type RevalidateEntity, type SigmaResourceConfig, type StripeListResourceConfig, StripeSync, type StripeSyncAccountState, type StripeSyncConfig, type StripeSyncState, type StripeWebSocketClient, type StripeWebSocketOptions, type StripeWebhookEvent, type Sync, type SyncBackfill, type SyncEntitlementsParams, type SyncFeaturesParams, type SyncObject, type SyncParams, VERSION, type WebhookProcessingResult, createStripeWebSocketClient, hashApiKey, runMigrations };
896
+ 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 };