stripe-experiment-sync 1.0.0 → 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 +117 -102
- package/dist/index.d.cts +32 -39
- package/dist/index.d.ts +32 -39
- package/dist/index.js +117 -102
- package/dist/migrations/0055_bigint_money_columns.sql +72 -0
- package/dist/migrations/0056_sync_run_closed_at.sql +53 -0
- package/package.json +8 -32
- package/dist/adapter-BtXT5w9r.d.cts +0 -51
- package/dist/adapter-BtXT5w9r.d.ts +0 -51
- package/dist/pg.cjs +0 -87
- package/dist/pg.d.cts +0 -28
- package/dist/pg.d.ts +0 -28
- package/dist/pg.js +0 -50
- package/dist/postgres-js.cjs +0 -90
- package/dist/postgres-js.d.cts +0 -31
- package/dist/postgres-js.d.ts +0 -31
- package/dist/postgres-js.js +0 -53
package/dist/index.cjs
CHANGED
|
@@ -45,47 +45,25 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
|
45
45
|
// package.json
|
|
46
46
|
var package_default = {
|
|
47
47
|
name: "stripe-experiment-sync",
|
|
48
|
-
version: "1.0.
|
|
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",
|
|
52
52
|
main: "./dist/index.cjs",
|
|
53
53
|
exports: {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
default: "./dist/index.js"
|
|
58
|
-
},
|
|
59
|
-
require: {
|
|
60
|
-
types: "./dist/index.d.cts",
|
|
61
|
-
default: "./dist/index.cjs"
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
"./pg": {
|
|
65
|
-
import: {
|
|
66
|
-
types: "./dist/pg.d.ts",
|
|
67
|
-
default: "./dist/pg.js"
|
|
68
|
-
},
|
|
69
|
-
require: {
|
|
70
|
-
types: "./dist/pg.d.cts",
|
|
71
|
-
default: "./dist/pg.cjs"
|
|
72
|
-
}
|
|
54
|
+
import: {
|
|
55
|
+
types: "./dist/index.d.ts",
|
|
56
|
+
import: "./dist/index.js"
|
|
73
57
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
default: "./dist/postgres-js.js"
|
|
78
|
-
},
|
|
79
|
-
require: {
|
|
80
|
-
types: "./dist/postgres-js.d.cts",
|
|
81
|
-
default: "./dist/postgres-js.cjs"
|
|
82
|
-
}
|
|
58
|
+
require: {
|
|
59
|
+
types: "./dist/index.d.cts",
|
|
60
|
+
require: "./dist/index.cjs"
|
|
83
61
|
}
|
|
84
62
|
},
|
|
85
63
|
scripts: {
|
|
86
64
|
clean: "rimraf dist",
|
|
87
65
|
prebuild: "npm run clean",
|
|
88
|
-
build: "tsup src/index.ts
|
|
66
|
+
build: "tsup src/index.ts --format esm,cjs --dts --shims && cp -r src/database/migrations dist/migrations",
|
|
89
67
|
lint: "eslint src --ext .ts",
|
|
90
68
|
test: "vitest"
|
|
91
69
|
},
|
|
@@ -95,7 +73,6 @@ var package_default = {
|
|
|
95
73
|
dependencies: {
|
|
96
74
|
pg: "^8.16.3",
|
|
97
75
|
"pg-node-migrations": "0.0.8",
|
|
98
|
-
postgres: "^3.4.7",
|
|
99
76
|
ws: "^8.18.0",
|
|
100
77
|
yesql: "^7.0.0"
|
|
101
78
|
},
|
|
@@ -108,7 +85,6 @@ var package_default = {
|
|
|
108
85
|
"@types/ws": "^8.5.13",
|
|
109
86
|
"@types/yesql": "^4.1.4",
|
|
110
87
|
"@vitest/ui": "^4.0.9",
|
|
111
|
-
stripe: "^20.0.0",
|
|
112
88
|
vitest: "^3.2.4"
|
|
113
89
|
},
|
|
114
90
|
repository: {
|
|
@@ -137,6 +113,7 @@ var import_stripe2 = __toESM(require("stripe"), 1);
|
|
|
137
113
|
var import_yesql2 = require("yesql");
|
|
138
114
|
|
|
139
115
|
// src/database/postgres.ts
|
|
116
|
+
var import_pg = __toESM(require("pg"), 1);
|
|
140
117
|
var import_yesql = require("yesql");
|
|
141
118
|
var ORDERED_STRIPE_TABLES = [
|
|
142
119
|
"subscription_items",
|
|
@@ -170,22 +147,9 @@ var TABLES_WITH_ACCOUNT_ID = /* @__PURE__ */ new Set(["_managed_webhooks"]);
|
|
|
170
147
|
var PostgresClient = class {
|
|
171
148
|
constructor(config) {
|
|
172
149
|
this.config = config;
|
|
173
|
-
this.
|
|
174
|
-
}
|
|
175
|
-
adapter;
|
|
176
|
-
/**
|
|
177
|
-
* Get the underlying adapter.
|
|
178
|
-
* Useful for accessing adapter-specific features.
|
|
179
|
-
*/
|
|
180
|
-
getAdapter() {
|
|
181
|
-
return this.adapter;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Close all database connections.
|
|
185
|
-
*/
|
|
186
|
-
async end() {
|
|
187
|
-
await this.adapter.end();
|
|
150
|
+
this.pool = new import_pg.default.Pool(config.poolConfig);
|
|
188
151
|
}
|
|
152
|
+
pool;
|
|
189
153
|
async delete(table, id) {
|
|
190
154
|
const prepared = (0, import_yesql.pg)(`
|
|
191
155
|
delete from "${this.config.schema}"."${table}"
|
|
@@ -197,7 +161,7 @@ var PostgresClient = class {
|
|
|
197
161
|
}
|
|
198
162
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
199
163
|
async query(text, params) {
|
|
200
|
-
return this.
|
|
164
|
+
return this.pool.query(text, params);
|
|
201
165
|
}
|
|
202
166
|
async upsertMany(entries, table) {
|
|
203
167
|
if (!entries.length) return [];
|
|
@@ -216,7 +180,7 @@ var PostgresClient = class {
|
|
|
216
180
|
"_raw_data" = EXCLUDED."_raw_data"
|
|
217
181
|
RETURNING *
|
|
218
182
|
`;
|
|
219
|
-
queries.push(this.
|
|
183
|
+
queries.push(this.pool.query(upsertSql, [rawData]));
|
|
220
184
|
});
|
|
221
185
|
results.push(...await Promise.all(queries));
|
|
222
186
|
}
|
|
@@ -255,7 +219,7 @@ var PostgresClient = class {
|
|
|
255
219
|
cleansed.last_synced_at = timestamp;
|
|
256
220
|
cleansed.account_id = accountId;
|
|
257
221
|
const prepared = (0, import_yesql.pg)(upsertSql, { useNullForMissing: true })(cleansed);
|
|
258
|
-
queries.push(this.
|
|
222
|
+
queries.push(this.pool.query(prepared.text, prepared.values));
|
|
259
223
|
} else {
|
|
260
224
|
const rawData = JSON.stringify(entry);
|
|
261
225
|
const upsertSql = `
|
|
@@ -270,7 +234,7 @@ var PostgresClient = class {
|
|
|
270
234
|
OR "${table}"."_last_synced_at" < $2
|
|
271
235
|
RETURNING *
|
|
272
236
|
`;
|
|
273
|
-
queries.push(this.
|
|
237
|
+
queries.push(this.pool.query(upsertSql, [rawData, timestamp, accountId]));
|
|
274
238
|
}
|
|
275
239
|
});
|
|
276
240
|
results.push(...await Promise.all(queries));
|
|
@@ -442,13 +406,27 @@ var PostgresClient = class {
|
|
|
442
406
|
* Execute a function while holding an advisory lock.
|
|
443
407
|
* The lock is automatically released after the function completes (success or error).
|
|
444
408
|
*
|
|
409
|
+
* IMPORTANT: This acquires a dedicated connection from the pool and holds it for the
|
|
410
|
+
* duration of the function execution. PostgreSQL advisory locks are session-level,
|
|
411
|
+
* so we must use the same connection for lock acquisition, operations, and release.
|
|
412
|
+
*
|
|
445
413
|
* @param key - A string key to lock on (will be hashed to an integer)
|
|
446
414
|
* @param fn - The function to execute while holding the lock
|
|
447
415
|
* @returns The result of the function
|
|
448
416
|
*/
|
|
449
417
|
async withAdvisoryLock(key, fn) {
|
|
450
418
|
const lockId = this.hashToInt32(key);
|
|
451
|
-
|
|
419
|
+
const client = await this.pool.connect();
|
|
420
|
+
try {
|
|
421
|
+
await client.query("SELECT pg_advisory_lock($1)", [lockId]);
|
|
422
|
+
return await fn();
|
|
423
|
+
} finally {
|
|
424
|
+
try {
|
|
425
|
+
await client.query("SELECT pg_advisory_unlock($1)", [lockId]);
|
|
426
|
+
} finally {
|
|
427
|
+
client.release();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
452
430
|
}
|
|
453
431
|
// =============================================================================
|
|
454
432
|
// Observable Sync System Methods
|
|
@@ -464,12 +442,20 @@ var PostgresClient = class {
|
|
|
464
442
|
*/
|
|
465
443
|
async cancelStaleRuns(accountId) {
|
|
466
444
|
await this.query(
|
|
467
|
-
`UPDATE "${this.config.schema}"."
|
|
445
|
+
`UPDATE "${this.config.schema}"."_sync_obj_run" o
|
|
468
446
|
SET status = 'error',
|
|
469
447
|
error_message = 'Auto-cancelled: stale (no update in 5 min)',
|
|
470
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()
|
|
471
457
|
WHERE r."_account_id" = $1
|
|
472
|
-
AND r.
|
|
458
|
+
AND r.closed_at IS NULL
|
|
473
459
|
AND EXISTS (
|
|
474
460
|
SELECT 1 FROM "${this.config.schema}"."_sync_obj_run" o
|
|
475
461
|
WHERE o."_account_id" = r."_account_id"
|
|
@@ -479,7 +465,7 @@ var PostgresClient = class {
|
|
|
479
465
|
SELECT 1 FROM "${this.config.schema}"."_sync_obj_run" o
|
|
480
466
|
WHERE o."_account_id" = r."_account_id"
|
|
481
467
|
AND o.run_started_at = r.started_at
|
|
482
|
-
AND o.
|
|
468
|
+
AND o.status IN ('pending', 'running')
|
|
483
469
|
)`,
|
|
484
470
|
[accountId]
|
|
485
471
|
);
|
|
@@ -495,7 +481,7 @@ var PostgresClient = class {
|
|
|
495
481
|
await this.cancelStaleRuns(accountId);
|
|
496
482
|
const existing = await this.query(
|
|
497
483
|
`SELECT "_account_id", started_at FROM "${this.config.schema}"."_sync_run"
|
|
498
|
-
WHERE "_account_id" = $1 AND
|
|
484
|
+
WHERE "_account_id" = $1 AND closed_at IS NULL`,
|
|
499
485
|
[accountId]
|
|
500
486
|
);
|
|
501
487
|
if (existing.rows.length > 0) {
|
|
@@ -524,7 +510,7 @@ var PostgresClient = class {
|
|
|
524
510
|
async getActiveSyncRun(accountId) {
|
|
525
511
|
const result = await this.query(
|
|
526
512
|
`SELECT "_account_id", started_at FROM "${this.config.schema}"."_sync_run"
|
|
527
|
-
WHERE "_account_id" = $1 AND
|
|
513
|
+
WHERE "_account_id" = $1 AND closed_at IS NULL`,
|
|
528
514
|
[accountId]
|
|
529
515
|
);
|
|
530
516
|
if (result.rows.length === 0) return null;
|
|
@@ -532,11 +518,12 @@ var PostgresClient = class {
|
|
|
532
518
|
return { accountId: row._account_id, runStartedAt: row.started_at };
|
|
533
519
|
}
|
|
534
520
|
/**
|
|
535
|
-
* Get
|
|
521
|
+
* Get sync run config (for concurrency control).
|
|
522
|
+
* Status is derived from sync_dashboard view.
|
|
536
523
|
*/
|
|
537
524
|
async getSyncRun(accountId, runStartedAt) {
|
|
538
525
|
const result = await this.query(
|
|
539
|
-
`SELECT "_account_id", started_at,
|
|
526
|
+
`SELECT "_account_id", started_at, max_concurrent, closed_at
|
|
540
527
|
FROM "${this.config.schema}"."_sync_run"
|
|
541
528
|
WHERE "_account_id" = $1 AND started_at = $2`,
|
|
542
529
|
[accountId, runStartedAt]
|
|
@@ -546,32 +533,22 @@ var PostgresClient = class {
|
|
|
546
533
|
return {
|
|
547
534
|
accountId: row._account_id,
|
|
548
535
|
runStartedAt: row.started_at,
|
|
549
|
-
|
|
550
|
-
|
|
536
|
+
maxConcurrent: row.max_concurrent,
|
|
537
|
+
closedAt: row.closed_at
|
|
551
538
|
};
|
|
552
539
|
}
|
|
553
540
|
/**
|
|
554
|
-
*
|
|
541
|
+
* Close a sync run (mark as done).
|
|
542
|
+
* Status (complete/error) is derived from object run states.
|
|
555
543
|
*/
|
|
556
|
-
async
|
|
544
|
+
async closeSyncRun(accountId, runStartedAt) {
|
|
557
545
|
await this.query(
|
|
558
546
|
`UPDATE "${this.config.schema}"."_sync_run"
|
|
559
|
-
SET
|
|
560
|
-
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`,
|
|
561
549
|
[accountId, runStartedAt]
|
|
562
550
|
);
|
|
563
551
|
}
|
|
564
|
-
/**
|
|
565
|
-
* Mark a sync run as failed.
|
|
566
|
-
*/
|
|
567
|
-
async failSyncRun(accountId, runStartedAt, errorMessage) {
|
|
568
|
-
await this.query(
|
|
569
|
-
`UPDATE "${this.config.schema}"."_sync_run"
|
|
570
|
-
SET status = 'error', error_message = $3, completed_at = now()
|
|
571
|
-
WHERE "_account_id" = $1 AND started_at = $2`,
|
|
572
|
-
[accountId, runStartedAt, errorMessage]
|
|
573
|
-
);
|
|
574
|
-
}
|
|
575
552
|
/**
|
|
576
553
|
* Create object run entries for a sync run.
|
|
577
554
|
* All objects start as 'pending'.
|
|
@@ -695,6 +672,7 @@ var PostgresClient = class {
|
|
|
695
672
|
}
|
|
696
673
|
/**
|
|
697
674
|
* Mark an object sync as complete.
|
|
675
|
+
* Auto-closes the run when all objects are done.
|
|
698
676
|
*/
|
|
699
677
|
async completeObjectSync(accountId, runStartedAt, object) {
|
|
700
678
|
await this.query(
|
|
@@ -703,9 +681,14 @@ var PostgresClient = class {
|
|
|
703
681
|
WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
|
|
704
682
|
[accountId, runStartedAt, object]
|
|
705
683
|
);
|
|
684
|
+
const allDone = await this.areAllObjectsComplete(accountId, runStartedAt);
|
|
685
|
+
if (allDone) {
|
|
686
|
+
await this.closeSyncRun(accountId, runStartedAt);
|
|
687
|
+
}
|
|
706
688
|
}
|
|
707
689
|
/**
|
|
708
690
|
* Mark an object sync as failed.
|
|
691
|
+
* Auto-closes the run when all objects are done.
|
|
709
692
|
*/
|
|
710
693
|
async failObjectSync(accountId, runStartedAt, object, errorMessage) {
|
|
711
694
|
await this.query(
|
|
@@ -714,6 +697,21 @@ var PostgresClient = class {
|
|
|
714
697
|
WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
|
|
715
698
|
[accountId, runStartedAt, object, errorMessage]
|
|
716
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;
|
|
717
715
|
}
|
|
718
716
|
/**
|
|
719
717
|
* Count running objects in a run.
|
|
@@ -952,9 +950,22 @@ var StripeSync = class {
|
|
|
952
950
|
{ autoExpandLists: config.autoExpandLists, stripeApiVersion: config.stripeApiVersion },
|
|
953
951
|
"StripeSync initialized"
|
|
954
952
|
);
|
|
953
|
+
const poolConfig = config.poolConfig ?? {};
|
|
954
|
+
if (config.databaseUrl) {
|
|
955
|
+
poolConfig.connectionString = config.databaseUrl;
|
|
956
|
+
}
|
|
957
|
+
if (config.maxPostgresConnections) {
|
|
958
|
+
poolConfig.max = config.maxPostgresConnections;
|
|
959
|
+
}
|
|
960
|
+
if (poolConfig.max === void 0) {
|
|
961
|
+
poolConfig.max = 10;
|
|
962
|
+
}
|
|
963
|
+
if (poolConfig.keepAlive === void 0) {
|
|
964
|
+
poolConfig.keepAlive = true;
|
|
965
|
+
}
|
|
955
966
|
this.postgresClient = new PostgresClient({
|
|
956
967
|
schema: "stripe",
|
|
957
|
-
|
|
968
|
+
poolConfig
|
|
958
969
|
});
|
|
959
970
|
}
|
|
960
971
|
stripe;
|
|
@@ -1296,8 +1307,8 @@ var StripeSync = class {
|
|
|
1296
1307
|
// Depends on invoice
|
|
1297
1308
|
listFn: (p) => this.stripe.creditNotes.list(p),
|
|
1298
1309
|
upsertFn: (items, id, bf) => this.upsertCreditNotes(items, id, bf),
|
|
1299
|
-
supportsCreatedFilter:
|
|
1300
|
-
// credit_notes
|
|
1310
|
+
supportsCreatedFilter: true
|
|
1311
|
+
// credit_notes support created filter
|
|
1301
1312
|
},
|
|
1302
1313
|
dispute: {
|
|
1303
1314
|
order: 14,
|
|
@@ -1950,14 +1961,10 @@ var StripeSync = class {
|
|
|
1950
1961
|
}
|
|
1951
1962
|
}
|
|
1952
1963
|
}
|
|
1953
|
-
await this.postgresClient.
|
|
1964
|
+
await this.postgresClient.closeSyncRun(accountId, runStartedAt);
|
|
1954
1965
|
return results;
|
|
1955
1966
|
} catch (error) {
|
|
1956
|
-
await this.postgresClient.
|
|
1957
|
-
accountId,
|
|
1958
|
-
runStartedAt,
|
|
1959
|
-
error instanceof Error ? error.message : "Unknown error"
|
|
1960
|
-
);
|
|
1967
|
+
await this.postgresClient.closeSyncRun(accountId, runStartedAt);
|
|
1961
1968
|
throw error;
|
|
1962
1969
|
}
|
|
1963
1970
|
}
|
|
@@ -2488,12 +2495,13 @@ var StripeSync = class {
|
|
|
2488
2495
|
await this.postgresClient.tryStartObjectSync(accountId, runStartedAt, resourceName);
|
|
2489
2496
|
try {
|
|
2490
2497
|
const result = await fn(cursor, runStartedAt);
|
|
2491
|
-
await this.postgresClient.
|
|
2498
|
+
await this.postgresClient.completeObjectSync(accountId, runStartedAt, resourceName);
|
|
2492
2499
|
return result;
|
|
2493
2500
|
} catch (error) {
|
|
2494
|
-
await this.postgresClient.
|
|
2501
|
+
await this.postgresClient.failObjectSync(
|
|
2495
2502
|
accountId,
|
|
2496
2503
|
runStartedAt,
|
|
2504
|
+
resourceName,
|
|
2497
2505
|
error instanceof Error ? error.message : "Unknown error"
|
|
2498
2506
|
);
|
|
2499
2507
|
throw error;
|
|
@@ -3251,6 +3259,7 @@ function chunkArray(array, chunkSize) {
|
|
|
3251
3259
|
}
|
|
3252
3260
|
|
|
3253
3261
|
// src/database/migrate.ts
|
|
3262
|
+
var import_pg2 = require("pg");
|
|
3254
3263
|
var import_pg_node_migrations = require("pg-node-migrations");
|
|
3255
3264
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
3256
3265
|
var import_node_path = __toESM(require("path"), 1);
|
|
@@ -3258,15 +3267,15 @@ var import_node_url = require("url");
|
|
|
3258
3267
|
var __filename2 = (0, import_node_url.fileURLToPath)(importMetaUrl);
|
|
3259
3268
|
var __dirname = import_node_path.default.dirname(__filename2);
|
|
3260
3269
|
async function doesTableExist(client, schema, tableName) {
|
|
3261
|
-
const result = await client.query(
|
|
3262
|
-
|
|
3270
|
+
const result = await client.query(
|
|
3271
|
+
`SELECT EXISTS (
|
|
3263
3272
|
SELECT 1
|
|
3264
3273
|
FROM information_schema.tables
|
|
3265
3274
|
WHERE table_schema = $1
|
|
3266
3275
|
AND table_name = $2
|
|
3267
3276
|
)`,
|
|
3268
|
-
|
|
3269
|
-
|
|
3277
|
+
[schema, tableName]
|
|
3278
|
+
);
|
|
3270
3279
|
return result.rows[0]?.exists || false;
|
|
3271
3280
|
}
|
|
3272
3281
|
async function renameMigrationsTableIfNeeded(client, schema = "stripe", logger) {
|
|
@@ -3284,9 +3293,9 @@ async function cleanupSchema(client, schema, logger) {
|
|
|
3284
3293
|
await client.query(`CREATE SCHEMA "${schema}"`);
|
|
3285
3294
|
logger?.info(`Schema "${schema}" has been reset`);
|
|
3286
3295
|
}
|
|
3287
|
-
async function connectAndMigrate(client, migrationsDirectory,
|
|
3296
|
+
async function connectAndMigrate(client, migrationsDirectory, config, logOnError = false) {
|
|
3288
3297
|
if (!import_node_fs.default.existsSync(migrationsDirectory)) {
|
|
3289
|
-
logger?.info(`Migrations directory ${migrationsDirectory} not found, skipping`);
|
|
3298
|
+
config.logger?.info(`Migrations directory ${migrationsDirectory} not found, skipping`);
|
|
3290
3299
|
return;
|
|
3291
3300
|
}
|
|
3292
3301
|
const optionalConfig = {
|
|
@@ -3297,18 +3306,23 @@ async function connectAndMigrate(client, migrationsDirectory, logger, logOnError
|
|
|
3297
3306
|
await (0, import_pg_node_migrations.migrate)({ client }, migrationsDirectory, optionalConfig);
|
|
3298
3307
|
} catch (error) {
|
|
3299
3308
|
if (logOnError && error instanceof Error) {
|
|
3300
|
-
logger?.error(error, "Migration error:");
|
|
3309
|
+
config.logger?.error(error, "Migration error:");
|
|
3301
3310
|
} else {
|
|
3302
3311
|
throw error;
|
|
3303
3312
|
}
|
|
3304
3313
|
}
|
|
3305
3314
|
}
|
|
3306
|
-
async function runMigrations(
|
|
3307
|
-
const client =
|
|
3315
|
+
async function runMigrations(config) {
|
|
3316
|
+
const client = new import_pg2.Client({
|
|
3317
|
+
connectionString: config.databaseUrl,
|
|
3318
|
+
ssl: config.ssl,
|
|
3319
|
+
connectionTimeoutMillis: 1e4
|
|
3320
|
+
});
|
|
3308
3321
|
const schema = "stripe";
|
|
3309
3322
|
try {
|
|
3323
|
+
await client.connect();
|
|
3310
3324
|
await client.query(`CREATE SCHEMA IF NOT EXISTS ${schema};`);
|
|
3311
|
-
await renameMigrationsTableIfNeeded(client, schema, logger);
|
|
3325
|
+
await renameMigrationsTableIfNeeded(client, schema, config.logger);
|
|
3312
3326
|
const tableExists = await doesTableExist(client, schema, "_migrations");
|
|
3313
3327
|
if (tableExists) {
|
|
3314
3328
|
const migrationCount = await client.query(
|
|
@@ -3316,16 +3330,17 @@ async function runMigrations(adapter, logger) {
|
|
|
3316
3330
|
);
|
|
3317
3331
|
const isEmpty = migrationCount.rows[0]?.count === "0";
|
|
3318
3332
|
if (isEmpty) {
|
|
3319
|
-
await cleanupSchema(client, schema, logger);
|
|
3333
|
+
await cleanupSchema(client, schema, config.logger);
|
|
3320
3334
|
}
|
|
3321
3335
|
}
|
|
3322
|
-
logger?.info("Running migrations");
|
|
3323
|
-
await connectAndMigrate(client, import_node_path.default.resolve(__dirname, "./migrations"),
|
|
3336
|
+
config.logger?.info("Running migrations");
|
|
3337
|
+
await connectAndMigrate(client, import_node_path.default.resolve(__dirname, "./migrations"), config);
|
|
3324
3338
|
} catch (err) {
|
|
3325
|
-
logger?.error(err, "Error running migrations");
|
|
3339
|
+
config.logger?.error(err, "Error running migrations");
|
|
3326
3340
|
throw err;
|
|
3327
3341
|
} finally {
|
|
3328
|
-
|
|
3342
|
+
await client.end();
|
|
3343
|
+
config.logger?.info("Finished migrations");
|
|
3329
3344
|
}
|
|
3330
3345
|
}
|
|
3331
3346
|
|
package/dist/index.d.cts
CHANGED
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
import Stripe from 'stripe';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import pg, { PoolConfig, QueryResult } from 'pg';
|
|
3
|
+
import { ConnectionOptions } from 'node:tls';
|
|
4
4
|
|
|
5
5
|
type PostgresConfig = {
|
|
6
6
|
schema: string;
|
|
7
|
-
|
|
7
|
+
poolConfig: PoolConfig;
|
|
8
8
|
};
|
|
9
9
|
declare class PostgresClient {
|
|
10
10
|
private config;
|
|
11
|
-
|
|
11
|
+
pool: pg.Pool;
|
|
12
12
|
constructor(config: PostgresConfig);
|
|
13
|
-
/**
|
|
14
|
-
* Get the underlying adapter.
|
|
15
|
-
* Useful for accessing adapter-specific features.
|
|
16
|
-
*/
|
|
17
|
-
getAdapter(): DatabaseAdapter;
|
|
18
|
-
/**
|
|
19
|
-
* Close all database connections.
|
|
20
|
-
*/
|
|
21
|
-
end(): Promise<void>;
|
|
22
13
|
delete(table: string, id: string): Promise<boolean>;
|
|
23
|
-
query
|
|
24
|
-
rows: T[];
|
|
25
|
-
rowCount: number;
|
|
26
|
-
}>;
|
|
14
|
+
query(text: string, params?: any[]): Promise<QueryResult>;
|
|
27
15
|
upsertMany<T extends {
|
|
28
16
|
[Key: string]: any;
|
|
29
17
|
}>(entries: T[], table: string): Promise<T[]>;
|
|
@@ -80,6 +68,10 @@ declare class PostgresClient {
|
|
|
80
68
|
* Execute a function while holding an advisory lock.
|
|
81
69
|
* The lock is automatically released after the function completes (success or error).
|
|
82
70
|
*
|
|
71
|
+
* IMPORTANT: This acquires a dedicated connection from the pool and holds it for the
|
|
72
|
+
* duration of the function execution. PostgreSQL advisory locks are session-level,
|
|
73
|
+
* so we must use the same connection for lock acquisition, operations, and release.
|
|
74
|
+
*
|
|
83
75
|
* @param key - A string key to lock on (will be hashed to an integer)
|
|
84
76
|
* @param fn - The function to execute while holding the lock
|
|
85
77
|
* @returns The result of the function
|
|
@@ -112,22 +104,20 @@ declare class PostgresClient {
|
|
|
112
104
|
runStartedAt: Date;
|
|
113
105
|
} | null>;
|
|
114
106
|
/**
|
|
115
|
-
* Get
|
|
107
|
+
* Get sync run config (for concurrency control).
|
|
108
|
+
* Status is derived from sync_dashboard view.
|
|
116
109
|
*/
|
|
117
110
|
getSyncRun(accountId: string, runStartedAt: Date): Promise<{
|
|
118
111
|
accountId: string;
|
|
119
112
|
runStartedAt: Date;
|
|
120
|
-
status: string;
|
|
121
113
|
maxConcurrent: number;
|
|
114
|
+
closedAt: Date | null;
|
|
122
115
|
} | null>;
|
|
123
116
|
/**
|
|
124
|
-
*
|
|
117
|
+
* Close a sync run (mark as done).
|
|
118
|
+
* Status (complete/error) is derived from object run states.
|
|
125
119
|
*/
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Mark a sync run as failed.
|
|
129
|
-
*/
|
|
130
|
-
failSyncRun(accountId: string, runStartedAt: Date, errorMessage: string): Promise<void>;
|
|
120
|
+
closeSyncRun(accountId: string, runStartedAt: Date): Promise<void>;
|
|
131
121
|
/**
|
|
132
122
|
* Create object run entries for a sync run.
|
|
133
123
|
* All objects start as 'pending'.
|
|
@@ -176,12 +166,18 @@ declare class PostgresClient {
|
|
|
176
166
|
deleteSyncRuns(accountId: string): Promise<void>;
|
|
177
167
|
/**
|
|
178
168
|
* Mark an object sync as complete.
|
|
169
|
+
* Auto-closes the run when all objects are done.
|
|
179
170
|
*/
|
|
180
171
|
completeObjectSync(accountId: string, runStartedAt: Date, object: string): Promise<void>;
|
|
181
172
|
/**
|
|
182
173
|
* Mark an object sync as failed.
|
|
174
|
+
* Auto-closes the run when all objects are done.
|
|
183
175
|
*/
|
|
184
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>;
|
|
185
181
|
/**
|
|
186
182
|
* Count running objects in a run.
|
|
187
183
|
*/
|
|
@@ -233,11 +229,9 @@ type StripeSyncConfig = {
|
|
|
233
229
|
* Default: false
|
|
234
230
|
*/
|
|
235
231
|
revalidateObjectsViaStripeApi?: Array<RevalidateEntity>;
|
|
236
|
-
/**
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
*/
|
|
240
|
-
adapter: DatabaseAdapter;
|
|
232
|
+
/** @deprecated Use `poolConfig` instead. */
|
|
233
|
+
maxPostgresConnections?: number;
|
|
234
|
+
poolConfig: PoolConfig;
|
|
241
235
|
logger?: Logger;
|
|
242
236
|
/**
|
|
243
237
|
* Maximum number of retry attempts for 429 rate limit errors.
|
|
@@ -579,6 +573,13 @@ declare class StripeSync {
|
|
|
579
573
|
private fetchMissingEntities;
|
|
580
574
|
}
|
|
581
575
|
|
|
576
|
+
type MigrationConfig = {
|
|
577
|
+
databaseUrl: string;
|
|
578
|
+
ssl?: ConnectionOptions;
|
|
579
|
+
logger?: Logger;
|
|
580
|
+
};
|
|
581
|
+
declare function runMigrations(config: MigrationConfig): Promise<void>;
|
|
582
|
+
|
|
582
583
|
/**
|
|
583
584
|
* Hashes a Stripe API key using SHA-256
|
|
584
585
|
* Used to store API key hashes in the database for fast account lookups
|
|
@@ -589,14 +590,6 @@ declare class StripeSync {
|
|
|
589
590
|
*/
|
|
590
591
|
declare function hashApiKey(apiKey: string): string;
|
|
591
592
|
|
|
592
|
-
/**
|
|
593
|
-
* Run database migrations using the provided adapter.
|
|
594
|
-
*
|
|
595
|
-
* @param adapter - Database adapter (PgAdapter or PostgresJsAdapter)
|
|
596
|
-
* @param logger - Optional logger for migration progress
|
|
597
|
-
*/
|
|
598
|
-
declare function runMigrations(adapter: DatabaseAdapter, logger?: Logger): Promise<void>;
|
|
599
|
-
|
|
600
593
|
declare const VERSION: string;
|
|
601
594
|
|
|
602
|
-
export {
|
|
595
|
+
export { type Logger, PostgresClient, type ProcessNextParams, type ProcessNextResult, type RevalidateEntity, StripeSync, type StripeSyncConfig, type Sync, type SyncBackfill, type SyncEntitlementsParams, type SyncFeaturesParams, type SyncObject, type SyncParams, VERSION, hashApiKey, runMigrations };
|