stripe-experiment-sync 1.0.1 → 1.0.2

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.cjs CHANGED
@@ -45,7 +45,7 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
45
45
  // package.json
46
46
  var package_default = {
47
47
  name: "stripe-experiment-sync",
48
- version: "1.0.1",
48
+ version: "1.0.2",
49
49
  private: false,
50
50
  description: "Stripe Sync Engine to sync Stripe data to Postgres",
51
51
  type: "module",
@@ -442,12 +442,20 @@ var PostgresClient = class {
442
442
  */
443
443
  async cancelStaleRuns(accountId) {
444
444
  await this.query(
445
- `UPDATE "${this.config.schema}"."_sync_run" r
445
+ `UPDATE "${this.config.schema}"."_sync_obj_run" o
446
446
  SET status = 'error',
447
447
  error_message = 'Auto-cancelled: stale (no update in 5 min)',
448
448
  completed_at = now()
449
+ WHERE o."_account_id" = $1
450
+ AND o.status = 'running'
451
+ AND o.updated_at < now() - interval '5 minutes'`,
452
+ [accountId]
453
+ );
454
+ await this.query(
455
+ `UPDATE "${this.config.schema}"."_sync_run" r
456
+ SET closed_at = now()
449
457
  WHERE r."_account_id" = $1
450
- AND r.status = 'running'
458
+ AND r.closed_at IS NULL
451
459
  AND EXISTS (
452
460
  SELECT 1 FROM "${this.config.schema}"."_sync_obj_run" o
453
461
  WHERE o."_account_id" = r."_account_id"
@@ -457,7 +465,7 @@ var PostgresClient = class {
457
465
  SELECT 1 FROM "${this.config.schema}"."_sync_obj_run" o
458
466
  WHERE o."_account_id" = r."_account_id"
459
467
  AND o.run_started_at = r.started_at
460
- AND o.updated_at >= now() - interval '5 minutes'
468
+ AND o.status IN ('pending', 'running')
461
469
  )`,
462
470
  [accountId]
463
471
  );
@@ -473,7 +481,7 @@ var PostgresClient = class {
473
481
  await this.cancelStaleRuns(accountId);
474
482
  const existing = await this.query(
475
483
  `SELECT "_account_id", started_at FROM "${this.config.schema}"."_sync_run"
476
- WHERE "_account_id" = $1 AND status = 'running'`,
484
+ WHERE "_account_id" = $1 AND closed_at IS NULL`,
477
485
  [accountId]
478
486
  );
479
487
  if (existing.rows.length > 0) {
@@ -502,7 +510,7 @@ var PostgresClient = class {
502
510
  async getActiveSyncRun(accountId) {
503
511
  const result = await this.query(
504
512
  `SELECT "_account_id", started_at FROM "${this.config.schema}"."_sync_run"
505
- WHERE "_account_id" = $1 AND status = 'running'`,
513
+ WHERE "_account_id" = $1 AND closed_at IS NULL`,
506
514
  [accountId]
507
515
  );
508
516
  if (result.rows.length === 0) return null;
@@ -510,11 +518,12 @@ var PostgresClient = class {
510
518
  return { accountId: row._account_id, runStartedAt: row.started_at };
511
519
  }
512
520
  /**
513
- * Get full sync run details.
521
+ * Get sync run config (for concurrency control).
522
+ * Status is derived from sync_dashboard view.
514
523
  */
515
524
  async getSyncRun(accountId, runStartedAt) {
516
525
  const result = await this.query(
517
- `SELECT "_account_id", started_at, status, max_concurrent
526
+ `SELECT "_account_id", started_at, max_concurrent, closed_at
518
527
  FROM "${this.config.schema}"."_sync_run"
519
528
  WHERE "_account_id" = $1 AND started_at = $2`,
520
529
  [accountId, runStartedAt]
@@ -524,32 +533,22 @@ var PostgresClient = class {
524
533
  return {
525
534
  accountId: row._account_id,
526
535
  runStartedAt: row.started_at,
527
- status: row.status,
528
- maxConcurrent: row.max_concurrent
536
+ maxConcurrent: row.max_concurrent,
537
+ closedAt: row.closed_at
529
538
  };
530
539
  }
531
540
  /**
532
- * Mark a sync run as complete.
541
+ * Close a sync run (mark as done).
542
+ * Status (complete/error) is derived from object run states.
533
543
  */
534
- async completeSyncRun(accountId, runStartedAt) {
544
+ async closeSyncRun(accountId, runStartedAt) {
535
545
  await this.query(
536
546
  `UPDATE "${this.config.schema}"."_sync_run"
537
- SET status = 'complete', completed_at = now()
538
- WHERE "_account_id" = $1 AND started_at = $2`,
547
+ SET closed_at = now()
548
+ WHERE "_account_id" = $1 AND started_at = $2 AND closed_at IS NULL`,
539
549
  [accountId, runStartedAt]
540
550
  );
541
551
  }
542
- /**
543
- * Mark a sync run as failed.
544
- */
545
- async failSyncRun(accountId, runStartedAt, errorMessage) {
546
- await this.query(
547
- `UPDATE "${this.config.schema}"."_sync_run"
548
- SET status = 'error', error_message = $3, completed_at = now()
549
- WHERE "_account_id" = $1 AND started_at = $2`,
550
- [accountId, runStartedAt, errorMessage]
551
- );
552
- }
553
552
  /**
554
553
  * Create object run entries for a sync run.
555
554
  * All objects start as 'pending'.
@@ -673,6 +672,7 @@ var PostgresClient = class {
673
672
  }
674
673
  /**
675
674
  * Mark an object sync as complete.
675
+ * Auto-closes the run when all objects are done.
676
676
  */
677
677
  async completeObjectSync(accountId, runStartedAt, object) {
678
678
  await this.query(
@@ -681,9 +681,14 @@ var PostgresClient = class {
681
681
  WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
682
682
  [accountId, runStartedAt, object]
683
683
  );
684
+ const allDone = await this.areAllObjectsComplete(accountId, runStartedAt);
685
+ if (allDone) {
686
+ await this.closeSyncRun(accountId, runStartedAt);
687
+ }
684
688
  }
685
689
  /**
686
690
  * Mark an object sync as failed.
691
+ * Auto-closes the run when all objects are done.
687
692
  */
688
693
  async failObjectSync(accountId, runStartedAt, object, errorMessage) {
689
694
  await this.query(
@@ -692,6 +697,21 @@ var PostgresClient = class {
692
697
  WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
693
698
  [accountId, runStartedAt, object, errorMessage]
694
699
  );
700
+ const allDone = await this.areAllObjectsComplete(accountId, runStartedAt);
701
+ if (allDone) {
702
+ await this.closeSyncRun(accountId, runStartedAt);
703
+ }
704
+ }
705
+ /**
706
+ * Check if any object in a run has errored.
707
+ */
708
+ async hasAnyObjectErrors(accountId, runStartedAt) {
709
+ const result = await this.query(
710
+ `SELECT COUNT(*) as count FROM "${this.config.schema}"."_sync_obj_run"
711
+ WHERE "_account_id" = $1 AND run_started_at = $2 AND status = 'error'`,
712
+ [accountId, runStartedAt]
713
+ );
714
+ return parseInt(result.rows[0].count) > 0;
695
715
  }
696
716
  /**
697
717
  * Count running objects in a run.
@@ -1287,8 +1307,8 @@ var StripeSync = class {
1287
1307
  // Depends on invoice
1288
1308
  listFn: (p) => this.stripe.creditNotes.list(p),
1289
1309
  upsertFn: (items, id, bf) => this.upsertCreditNotes(items, id, bf),
1290
- supportsCreatedFilter: false
1291
- // credit_notes don't support created filter
1310
+ supportsCreatedFilter: true
1311
+ // credit_notes support created filter
1292
1312
  },
1293
1313
  dispute: {
1294
1314
  order: 14,
@@ -1941,14 +1961,10 @@ var StripeSync = class {
1941
1961
  }
1942
1962
  }
1943
1963
  }
1944
- await this.postgresClient.completeSyncRun(accountId, runStartedAt);
1964
+ await this.postgresClient.closeSyncRun(accountId, runStartedAt);
1945
1965
  return results;
1946
1966
  } catch (error) {
1947
- await this.postgresClient.failSyncRun(
1948
- accountId,
1949
- runStartedAt,
1950
- error instanceof Error ? error.message : "Unknown error"
1951
- );
1967
+ await this.postgresClient.closeSyncRun(accountId, runStartedAt);
1952
1968
  throw error;
1953
1969
  }
1954
1970
  }
@@ -2479,12 +2495,13 @@ var StripeSync = class {
2479
2495
  await this.postgresClient.tryStartObjectSync(accountId, runStartedAt, resourceName);
2480
2496
  try {
2481
2497
  const result = await fn(cursor, runStartedAt);
2482
- await this.postgresClient.completeSyncRun(accountId, runStartedAt);
2498
+ await this.postgresClient.completeObjectSync(accountId, runStartedAt, resourceName);
2483
2499
  return result;
2484
2500
  } catch (error) {
2485
- await this.postgresClient.failSyncRun(
2501
+ await this.postgresClient.failObjectSync(
2486
2502
  accountId,
2487
2503
  runStartedAt,
2504
+ resourceName,
2488
2505
  error instanceof Error ? error.message : "Unknown error"
2489
2506
  );
2490
2507
  throw error;
package/dist/index.d.cts CHANGED
@@ -104,22 +104,20 @@ declare class PostgresClient {
104
104
  runStartedAt: Date;
105
105
  } | null>;
106
106
  /**
107
- * Get full sync run details.
107
+ * Get sync run config (for concurrency control).
108
+ * Status is derived from sync_dashboard view.
108
109
  */
109
110
  getSyncRun(accountId: string, runStartedAt: Date): Promise<{
110
111
  accountId: string;
111
112
  runStartedAt: Date;
112
- status: string;
113
113
  maxConcurrent: number;
114
+ closedAt: Date | null;
114
115
  } | null>;
115
116
  /**
116
- * Mark a sync run as complete.
117
- */
118
- completeSyncRun(accountId: string, runStartedAt: Date): Promise<void>;
119
- /**
120
- * Mark a sync run as failed.
117
+ * Close a sync run (mark as done).
118
+ * Status (complete/error) is derived from object run states.
121
119
  */
122
- failSyncRun(accountId: string, runStartedAt: Date, errorMessage: string): Promise<void>;
120
+ closeSyncRun(accountId: string, runStartedAt: Date): Promise<void>;
123
121
  /**
124
122
  * Create object run entries for a sync run.
125
123
  * All objects start as 'pending'.
@@ -168,12 +166,18 @@ declare class PostgresClient {
168
166
  deleteSyncRuns(accountId: string): Promise<void>;
169
167
  /**
170
168
  * Mark an object sync as complete.
169
+ * Auto-closes the run when all objects are done.
171
170
  */
172
171
  completeObjectSync(accountId: string, runStartedAt: Date, object: string): Promise<void>;
173
172
  /**
174
173
  * Mark an object sync as failed.
174
+ * Auto-closes the run when all objects are done.
175
175
  */
176
176
  failObjectSync(accountId: string, runStartedAt: Date, object: string, errorMessage: string): Promise<void>;
177
+ /**
178
+ * Check if any object in a run has errored.
179
+ */
180
+ hasAnyObjectErrors(accountId: string, runStartedAt: Date): Promise<boolean>;
177
181
  /**
178
182
  * Count running objects in a run.
179
183
  */
package/dist/index.d.ts CHANGED
@@ -104,22 +104,20 @@ declare class PostgresClient {
104
104
  runStartedAt: Date;
105
105
  } | null>;
106
106
  /**
107
- * Get full sync run details.
107
+ * Get sync run config (for concurrency control).
108
+ * Status is derived from sync_dashboard view.
108
109
  */
109
110
  getSyncRun(accountId: string, runStartedAt: Date): Promise<{
110
111
  accountId: string;
111
112
  runStartedAt: Date;
112
- status: string;
113
113
  maxConcurrent: number;
114
+ closedAt: Date | null;
114
115
  } | null>;
115
116
  /**
116
- * Mark a sync run as complete.
117
- */
118
- completeSyncRun(accountId: string, runStartedAt: Date): Promise<void>;
119
- /**
120
- * Mark a sync run as failed.
117
+ * Close a sync run (mark as done).
118
+ * Status (complete/error) is derived from object run states.
121
119
  */
122
- failSyncRun(accountId: string, runStartedAt: Date, errorMessage: string): Promise<void>;
120
+ closeSyncRun(accountId: string, runStartedAt: Date): Promise<void>;
123
121
  /**
124
122
  * Create object run entries for a sync run.
125
123
  * All objects start as 'pending'.
@@ -168,12 +166,18 @@ declare class PostgresClient {
168
166
  deleteSyncRuns(accountId: string): Promise<void>;
169
167
  /**
170
168
  * Mark an object sync as complete.
169
+ * Auto-closes the run when all objects are done.
171
170
  */
172
171
  completeObjectSync(accountId: string, runStartedAt: Date, object: string): Promise<void>;
173
172
  /**
174
173
  * Mark an object sync as failed.
174
+ * Auto-closes the run when all objects are done.
175
175
  */
176
176
  failObjectSync(accountId: string, runStartedAt: Date, object: string, errorMessage: string): Promise<void>;
177
+ /**
178
+ * Check if any object in a run has errored.
179
+ */
180
+ hasAnyObjectErrors(accountId: string, runStartedAt: Date): Promise<boolean>;
177
181
  /**
178
182
  * Count running objects in a run.
179
183
  */
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "stripe-experiment-sync",
4
- version: "1.0.1",
4
+ version: "1.0.2",
5
5
  private: false,
6
6
  description: "Stripe Sync Engine to sync Stripe data to Postgres",
7
7
  type: "module",
@@ -398,12 +398,20 @@ var PostgresClient = class {
398
398
  */
399
399
  async cancelStaleRuns(accountId) {
400
400
  await this.query(
401
- `UPDATE "${this.config.schema}"."_sync_run" r
401
+ `UPDATE "${this.config.schema}"."_sync_obj_run" o
402
402
  SET status = 'error',
403
403
  error_message = 'Auto-cancelled: stale (no update in 5 min)',
404
404
  completed_at = now()
405
+ WHERE o."_account_id" = $1
406
+ AND o.status = 'running'
407
+ AND o.updated_at < now() - interval '5 minutes'`,
408
+ [accountId]
409
+ );
410
+ await this.query(
411
+ `UPDATE "${this.config.schema}"."_sync_run" r
412
+ SET closed_at = now()
405
413
  WHERE r."_account_id" = $1
406
- AND r.status = 'running'
414
+ AND r.closed_at IS NULL
407
415
  AND EXISTS (
408
416
  SELECT 1 FROM "${this.config.schema}"."_sync_obj_run" o
409
417
  WHERE o."_account_id" = r."_account_id"
@@ -413,7 +421,7 @@ var PostgresClient = class {
413
421
  SELECT 1 FROM "${this.config.schema}"."_sync_obj_run" o
414
422
  WHERE o."_account_id" = r."_account_id"
415
423
  AND o.run_started_at = r.started_at
416
- AND o.updated_at >= now() - interval '5 minutes'
424
+ AND o.status IN ('pending', 'running')
417
425
  )`,
418
426
  [accountId]
419
427
  );
@@ -429,7 +437,7 @@ var PostgresClient = class {
429
437
  await this.cancelStaleRuns(accountId);
430
438
  const existing = await this.query(
431
439
  `SELECT "_account_id", started_at FROM "${this.config.schema}"."_sync_run"
432
- WHERE "_account_id" = $1 AND status = 'running'`,
440
+ WHERE "_account_id" = $1 AND closed_at IS NULL`,
433
441
  [accountId]
434
442
  );
435
443
  if (existing.rows.length > 0) {
@@ -458,7 +466,7 @@ var PostgresClient = class {
458
466
  async getActiveSyncRun(accountId) {
459
467
  const result = await this.query(
460
468
  `SELECT "_account_id", started_at FROM "${this.config.schema}"."_sync_run"
461
- WHERE "_account_id" = $1 AND status = 'running'`,
469
+ WHERE "_account_id" = $1 AND closed_at IS NULL`,
462
470
  [accountId]
463
471
  );
464
472
  if (result.rows.length === 0) return null;
@@ -466,11 +474,12 @@ var PostgresClient = class {
466
474
  return { accountId: row._account_id, runStartedAt: row.started_at };
467
475
  }
468
476
  /**
469
- * Get full sync run details.
477
+ * Get sync run config (for concurrency control).
478
+ * Status is derived from sync_dashboard view.
470
479
  */
471
480
  async getSyncRun(accountId, runStartedAt) {
472
481
  const result = await this.query(
473
- `SELECT "_account_id", started_at, status, max_concurrent
482
+ `SELECT "_account_id", started_at, max_concurrent, closed_at
474
483
  FROM "${this.config.schema}"."_sync_run"
475
484
  WHERE "_account_id" = $1 AND started_at = $2`,
476
485
  [accountId, runStartedAt]
@@ -480,32 +489,22 @@ var PostgresClient = class {
480
489
  return {
481
490
  accountId: row._account_id,
482
491
  runStartedAt: row.started_at,
483
- status: row.status,
484
- maxConcurrent: row.max_concurrent
492
+ maxConcurrent: row.max_concurrent,
493
+ closedAt: row.closed_at
485
494
  };
486
495
  }
487
496
  /**
488
- * Mark a sync run as complete.
497
+ * Close a sync run (mark as done).
498
+ * Status (complete/error) is derived from object run states.
489
499
  */
490
- async completeSyncRun(accountId, runStartedAt) {
500
+ async closeSyncRun(accountId, runStartedAt) {
491
501
  await this.query(
492
502
  `UPDATE "${this.config.schema}"."_sync_run"
493
- SET status = 'complete', completed_at = now()
494
- WHERE "_account_id" = $1 AND started_at = $2`,
503
+ SET closed_at = now()
504
+ WHERE "_account_id" = $1 AND started_at = $2 AND closed_at IS NULL`,
495
505
  [accountId, runStartedAt]
496
506
  );
497
507
  }
498
- /**
499
- * Mark a sync run as failed.
500
- */
501
- async failSyncRun(accountId, runStartedAt, errorMessage) {
502
- await this.query(
503
- `UPDATE "${this.config.schema}"."_sync_run"
504
- SET status = 'error', error_message = $3, completed_at = now()
505
- WHERE "_account_id" = $1 AND started_at = $2`,
506
- [accountId, runStartedAt, errorMessage]
507
- );
508
- }
509
508
  /**
510
509
  * Create object run entries for a sync run.
511
510
  * All objects start as 'pending'.
@@ -629,6 +628,7 @@ var PostgresClient = class {
629
628
  }
630
629
  /**
631
630
  * Mark an object sync as complete.
631
+ * Auto-closes the run when all objects are done.
632
632
  */
633
633
  async completeObjectSync(accountId, runStartedAt, object) {
634
634
  await this.query(
@@ -637,9 +637,14 @@ var PostgresClient = class {
637
637
  WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
638
638
  [accountId, runStartedAt, object]
639
639
  );
640
+ const allDone = await this.areAllObjectsComplete(accountId, runStartedAt);
641
+ if (allDone) {
642
+ await this.closeSyncRun(accountId, runStartedAt);
643
+ }
640
644
  }
641
645
  /**
642
646
  * Mark an object sync as failed.
647
+ * Auto-closes the run when all objects are done.
643
648
  */
644
649
  async failObjectSync(accountId, runStartedAt, object, errorMessage) {
645
650
  await this.query(
@@ -648,6 +653,21 @@ var PostgresClient = class {
648
653
  WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
649
654
  [accountId, runStartedAt, object, errorMessage]
650
655
  );
656
+ const allDone = await this.areAllObjectsComplete(accountId, runStartedAt);
657
+ if (allDone) {
658
+ await this.closeSyncRun(accountId, runStartedAt);
659
+ }
660
+ }
661
+ /**
662
+ * Check if any object in a run has errored.
663
+ */
664
+ async hasAnyObjectErrors(accountId, runStartedAt) {
665
+ const result = await this.query(
666
+ `SELECT COUNT(*) as count FROM "${this.config.schema}"."_sync_obj_run"
667
+ WHERE "_account_id" = $1 AND run_started_at = $2 AND status = 'error'`,
668
+ [accountId, runStartedAt]
669
+ );
670
+ return parseInt(result.rows[0].count) > 0;
651
671
  }
652
672
  /**
653
673
  * Count running objects in a run.
@@ -1243,8 +1263,8 @@ var StripeSync = class {
1243
1263
  // Depends on invoice
1244
1264
  listFn: (p) => this.stripe.creditNotes.list(p),
1245
1265
  upsertFn: (items, id, bf) => this.upsertCreditNotes(items, id, bf),
1246
- supportsCreatedFilter: false
1247
- // credit_notes don't support created filter
1266
+ supportsCreatedFilter: true
1267
+ // credit_notes support created filter
1248
1268
  },
1249
1269
  dispute: {
1250
1270
  order: 14,
@@ -1897,14 +1917,10 @@ var StripeSync = class {
1897
1917
  }
1898
1918
  }
1899
1919
  }
1900
- await this.postgresClient.completeSyncRun(accountId, runStartedAt);
1920
+ await this.postgresClient.closeSyncRun(accountId, runStartedAt);
1901
1921
  return results;
1902
1922
  } catch (error) {
1903
- await this.postgresClient.failSyncRun(
1904
- accountId,
1905
- runStartedAt,
1906
- error instanceof Error ? error.message : "Unknown error"
1907
- );
1923
+ await this.postgresClient.closeSyncRun(accountId, runStartedAt);
1908
1924
  throw error;
1909
1925
  }
1910
1926
  }
@@ -2435,12 +2451,13 @@ var StripeSync = class {
2435
2451
  await this.postgresClient.tryStartObjectSync(accountId, runStartedAt, resourceName);
2436
2452
  try {
2437
2453
  const result = await fn(cursor, runStartedAt);
2438
- await this.postgresClient.completeSyncRun(accountId, runStartedAt);
2454
+ await this.postgresClient.completeObjectSync(accountId, runStartedAt, resourceName);
2439
2455
  return result;
2440
2456
  } catch (error) {
2441
- await this.postgresClient.failSyncRun(
2457
+ await this.postgresClient.failObjectSync(
2442
2458
  accountId,
2443
2459
  runStartedAt,
2460
+ resourceName,
2444
2461
  error instanceof Error ? error.message : "Unknown error"
2445
2462
  );
2446
2463
  throw error;
@@ -0,0 +1,72 @@
1
+ -- Fix generated columns: must drop and recreate with ::bigint cast
2
+ -- Money columns that can overflow PostgreSQL integer max (~2.1 billion)
3
+
4
+ -- checkout_session_line_items
5
+ ALTER TABLE "stripe"."checkout_session_line_items" DROP COLUMN "amount_discount";
6
+ ALTER TABLE "stripe"."checkout_session_line_items" ADD COLUMN "amount_discount" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_discount')::bigint) STORED;
7
+ ALTER TABLE "stripe"."checkout_session_line_items" DROP COLUMN "amount_subtotal";
8
+ ALTER TABLE "stripe"."checkout_session_line_items" ADD COLUMN "amount_subtotal" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_subtotal')::bigint) STORED;
9
+ ALTER TABLE "stripe"."checkout_session_line_items" DROP COLUMN "amount_tax";
10
+ ALTER TABLE "stripe"."checkout_session_line_items" ADD COLUMN "amount_tax" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_tax')::bigint) STORED;
11
+ ALTER TABLE "stripe"."checkout_session_line_items" DROP COLUMN "amount_total";
12
+ ALTER TABLE "stripe"."checkout_session_line_items" ADD COLUMN "amount_total" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_total')::bigint) STORED;
13
+
14
+ -- checkout_sessions
15
+ ALTER TABLE "stripe"."checkout_sessions" DROP COLUMN "amount_subtotal";
16
+ ALTER TABLE "stripe"."checkout_sessions" ADD COLUMN "amount_subtotal" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_subtotal')::bigint) STORED;
17
+ ALTER TABLE "stripe"."checkout_sessions" DROP COLUMN "amount_total";
18
+ ALTER TABLE "stripe"."checkout_sessions" ADD COLUMN "amount_total" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_total')::bigint) STORED;
19
+
20
+ -- credit_notes
21
+ ALTER TABLE "stripe"."credit_notes" DROP COLUMN "amount";
22
+ ALTER TABLE "stripe"."credit_notes" ADD COLUMN "amount" bigint GENERATED ALWAYS AS ((_raw_data->>'amount')::bigint) STORED;
23
+ ALTER TABLE "stripe"."credit_notes" DROP COLUMN "amount_shipping";
24
+ ALTER TABLE "stripe"."credit_notes" ADD COLUMN "amount_shipping" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_shipping')::bigint) STORED;
25
+ ALTER TABLE "stripe"."credit_notes" DROP COLUMN "discount_amount";
26
+ ALTER TABLE "stripe"."credit_notes" ADD COLUMN "discount_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'discount_amount')::bigint) STORED;
27
+ ALTER TABLE "stripe"."credit_notes" DROP COLUMN "out_of_band_amount";
28
+ ALTER TABLE "stripe"."credit_notes" ADD COLUMN "out_of_band_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'out_of_band_amount')::bigint) STORED;
29
+ ALTER TABLE "stripe"."credit_notes" DROP COLUMN "subtotal";
30
+ ALTER TABLE "stripe"."credit_notes" ADD COLUMN "subtotal" bigint GENERATED ALWAYS AS ((_raw_data->>'subtotal')::bigint) STORED;
31
+ ALTER TABLE "stripe"."credit_notes" DROP COLUMN "subtotal_excluding_tax";
32
+ ALTER TABLE "stripe"."credit_notes" ADD COLUMN "subtotal_excluding_tax" bigint GENERATED ALWAYS AS ((_raw_data->>'subtotal_excluding_tax')::bigint) STORED;
33
+ ALTER TABLE "stripe"."credit_notes" DROP COLUMN "total";
34
+ ALTER TABLE "stripe"."credit_notes" ADD COLUMN "total" bigint GENERATED ALWAYS AS ((_raw_data->>'total')::bigint) STORED;
35
+ ALTER TABLE "stripe"."credit_notes" DROP COLUMN "total_excluding_tax";
36
+ ALTER TABLE "stripe"."credit_notes" ADD COLUMN "total_excluding_tax" bigint GENERATED ALWAYS AS ((_raw_data->>'total_excluding_tax')::bigint) STORED;
37
+
38
+ -- customers
39
+ ALTER TABLE "stripe"."customers" DROP COLUMN "balance";
40
+ ALTER TABLE "stripe"."customers" ADD COLUMN "balance" bigint GENERATED ALWAYS AS ((_raw_data->>'balance')::bigint) STORED;
41
+
42
+ -- invoices
43
+ ALTER TABLE "stripe"."invoices" DROP COLUMN "ending_balance";
44
+ ALTER TABLE "stripe"."invoices" ADD COLUMN "ending_balance" bigint GENERATED ALWAYS AS ((_raw_data->>'ending_balance')::bigint) STORED;
45
+ ALTER TABLE "stripe"."invoices" DROP COLUMN "starting_balance";
46
+ ALTER TABLE "stripe"."invoices" ADD COLUMN "starting_balance" bigint GENERATED ALWAYS AS ((_raw_data->>'starting_balance')::bigint) STORED;
47
+ ALTER TABLE "stripe"."invoices" DROP COLUMN "subtotal";
48
+ ALTER TABLE "stripe"."invoices" ADD COLUMN "subtotal" bigint GENERATED ALWAYS AS ((_raw_data->>'subtotal')::bigint) STORED;
49
+ ALTER TABLE "stripe"."invoices" DROP COLUMN "tax";
50
+ ALTER TABLE "stripe"."invoices" ADD COLUMN "tax" bigint GENERATED ALWAYS AS ((_raw_data->>'tax')::bigint) STORED;
51
+ ALTER TABLE "stripe"."invoices" DROP COLUMN "post_payment_credit_notes_amount";
52
+ ALTER TABLE "stripe"."invoices" ADD COLUMN "post_payment_credit_notes_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'post_payment_credit_notes_amount')::bigint) STORED;
53
+ ALTER TABLE "stripe"."invoices" DROP COLUMN "pre_payment_credit_notes_amount";
54
+ ALTER TABLE "stripe"."invoices" ADD COLUMN "pre_payment_credit_notes_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'pre_payment_credit_notes_amount')::bigint) STORED;
55
+
56
+ -- payment_intents
57
+ ALTER TABLE "stripe"."payment_intents" DROP COLUMN "amount";
58
+ ALTER TABLE "stripe"."payment_intents" ADD COLUMN "amount" bigint GENERATED ALWAYS AS ((_raw_data->>'amount')::bigint) STORED;
59
+ ALTER TABLE "stripe"."payment_intents" DROP COLUMN "amount_capturable";
60
+ ALTER TABLE "stripe"."payment_intents" ADD COLUMN "amount_capturable" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_capturable')::bigint) STORED;
61
+ ALTER TABLE "stripe"."payment_intents" DROP COLUMN "amount_received";
62
+ ALTER TABLE "stripe"."payment_intents" ADD COLUMN "amount_received" bigint GENERATED ALWAYS AS ((_raw_data->>'amount_received')::bigint) STORED;
63
+ ALTER TABLE "stripe"."payment_intents" DROP COLUMN "application_fee_amount";
64
+ ALTER TABLE "stripe"."payment_intents" ADD COLUMN "application_fee_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'application_fee_amount')::bigint) STORED;
65
+
66
+ -- prices
67
+ ALTER TABLE "stripe"."prices" DROP COLUMN "unit_amount";
68
+ ALTER TABLE "stripe"."prices" ADD COLUMN "unit_amount" bigint GENERATED ALWAYS AS ((_raw_data->>'unit_amount')::bigint) STORED;
69
+
70
+ -- refunds
71
+ ALTER TABLE "stripe"."refunds" DROP COLUMN "amount";
72
+ ALTER TABLE "stripe"."refunds" ADD COLUMN "amount" bigint GENERATED ALWAYS AS ((_raw_data->>'amount')::bigint) STORED;
@@ -0,0 +1,53 @@
1
+ -- Add closed_at column to _sync_run
2
+ -- closed_at IS NULL means the run is still active
3
+ -- Status is derived from object states when closed_at IS NOT NULL
4
+
5
+ -- Step 1: Drop dependent view first
6
+ DROP VIEW IF EXISTS "stripe"."sync_dashboard";
7
+
8
+ -- Step 2: Drop the old constraint, status column, and completed_at column
9
+ ALTER TABLE "stripe"."_sync_run" DROP CONSTRAINT IF EXISTS one_active_run_per_account;
10
+ ALTER TABLE "stripe"."_sync_run" DROP COLUMN IF EXISTS status;
11
+ ALTER TABLE "stripe"."_sync_run" DROP COLUMN IF EXISTS completed_at;
12
+
13
+ -- Step 3: Add closed_at column
14
+ ALTER TABLE "stripe"."_sync_run" ADD COLUMN IF NOT EXISTS closed_at TIMESTAMPTZ;
15
+
16
+ -- Step 4: Create exclusion constraint (only one active run per account)
17
+ ALTER TABLE "stripe"."_sync_run"
18
+ ADD CONSTRAINT one_active_run_per_account
19
+ EXCLUDE ("_account_id" WITH =) WHERE (closed_at IS NULL);
20
+
21
+ -- Step 5: Recreate sync_dashboard view (run-level only, one row per run)
22
+ -- Base table: _sync_run (parent sync operation)
23
+ -- Child table: _sync_obj_run (individual object syncs)
24
+ CREATE OR REPLACE VIEW "stripe"."sync_dashboard" AS
25
+ SELECT
26
+ run."_account_id" as account_id,
27
+ run.started_at,
28
+ run.closed_at,
29
+ run.max_concurrent,
30
+ run.triggered_by,
31
+ run.updated_at,
32
+ -- Derived status from object states
33
+ CASE
34
+ WHEN run.closed_at IS NULL THEN 'running'
35
+ WHEN EXISTS (
36
+ SELECT 1 FROM "stripe"."_sync_obj_run" obj
37
+ WHERE obj."_account_id" = run."_account_id"
38
+ AND obj.run_started_at = run.started_at
39
+ AND obj.status = 'error'
40
+ ) THEN 'error'
41
+ ELSE 'complete'
42
+ END as status,
43
+ -- First error message from failed objects
44
+ (SELECT obj.error_message FROM "stripe"."_sync_obj_run" obj
45
+ WHERE obj."_account_id" = run."_account_id"
46
+ AND obj.run_started_at = run.started_at
47
+ AND obj.status = 'error'
48
+ ORDER BY obj.object LIMIT 1) as error_message,
49
+ -- Total processed count across all objects
50
+ COALESCE((SELECT SUM(obj.processed_count) FROM "stripe"."_sync_obj_run" obj
51
+ WHERE obj."_account_id" = run."_account_id"
52
+ AND obj.run_started_at = run.started_at), 0) as processed_count
53
+ FROM "stripe"."_sync_run" run;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stripe-experiment-sync",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "private": false,
5
5
  "description": "Stripe Sync Engine to sync Stripe data to Postgres",
6
6
  "type": "module",