stripe-experiment-sync 1.0.17 → 1.0.19
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/README.md +3 -0
- package/dist/{chunk-I7IFXSAU.js → chunk-4P6TAP7L.js} +1 -1
- package/dist/{chunk-57SXDCMH.js → chunk-CMGFQCD7.js} +1 -1
- package/dist/{chunk-YXRCT3RK.js → chunk-HZ2OIPQ5.js} +2 -2
- package/dist/{chunk-TV67ZOCK.js → chunk-XKBCLBFT.js} +151 -62
- package/dist/cli/index.cjs +151 -62
- package/dist/cli/index.js +4 -4
- package/dist/cli/lib.cjs +151 -62
- package/dist/cli/lib.js +4 -4
- package/dist/index.cjs +151 -62
- package/dist/index.d.cts +23 -5
- package/dist/index.d.ts +23 -5
- package/dist/index.js +2 -2
- package/dist/migrations/0061_add_page_cursor.sql +3 -0
- package/dist/supabase/index.cjs +1 -1
- package/dist/supabase/index.js +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -46,7 +46,7 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
|
46
46
|
// package.json
|
|
47
47
|
var package_default = {
|
|
48
48
|
name: "stripe-experiment-sync",
|
|
49
|
-
version: "1.0.
|
|
49
|
+
version: "1.0.19",
|
|
50
50
|
private: false,
|
|
51
51
|
description: "Stripe Sync Engine to sync Stripe data to Postgres",
|
|
52
52
|
type: "module",
|
|
@@ -523,7 +523,8 @@ var PostgresClient = class {
|
|
|
523
523
|
`UPDATE "${this.config.schema}"."_sync_obj_runs" o
|
|
524
524
|
SET status = 'error',
|
|
525
525
|
error_message = 'Auto-cancelled: stale (no update in 5 min)',
|
|
526
|
-
completed_at = now()
|
|
526
|
+
completed_at = now(),
|
|
527
|
+
page_cursor = NULL
|
|
527
528
|
WHERE o."_account_id" = $1
|
|
528
529
|
AND o.status = 'running'
|
|
529
530
|
AND o.updated_at < now() - interval '5 minutes'`,
|
|
@@ -630,15 +631,17 @@ var PostgresClient = class {
|
|
|
630
631
|
/**
|
|
631
632
|
* Create object run entries for a sync run.
|
|
632
633
|
* All objects start as 'pending'.
|
|
634
|
+
*
|
|
635
|
+
* @param resourceNames - Database resource names (e.g. 'products', 'customers', NOT 'product', 'customer')
|
|
633
636
|
*/
|
|
634
|
-
async createObjectRuns(accountId, runStartedAt,
|
|
635
|
-
if (
|
|
636
|
-
const values =
|
|
637
|
+
async createObjectRuns(accountId, runStartedAt, resourceNames) {
|
|
638
|
+
if (resourceNames.length === 0) return;
|
|
639
|
+
const values = resourceNames.map((_, i) => `($1, $2, $${i + 3})`).join(", ");
|
|
637
640
|
await this.query(
|
|
638
641
|
`INSERT INTO "${this.config.schema}"."_sync_obj_runs" ("_account_id", run_started_at, object)
|
|
639
642
|
VALUES ${values}
|
|
640
643
|
ON CONFLICT ("_account_id", run_started_at, object) DO NOTHING`,
|
|
641
|
-
[accountId, runStartedAt, ...
|
|
644
|
+
[accountId, runStartedAt, ...resourceNames]
|
|
642
645
|
);
|
|
643
646
|
}
|
|
644
647
|
/**
|
|
@@ -667,7 +670,7 @@ var PostgresClient = class {
|
|
|
667
670
|
*/
|
|
668
671
|
async getObjectRun(accountId, runStartedAt, object) {
|
|
669
672
|
const result = await this.query(
|
|
670
|
-
`SELECT object, status, processed_count, cursor
|
|
673
|
+
`SELECT object, status, processed_count, cursor, page_cursor
|
|
671
674
|
FROM "${this.config.schema}"."_sync_obj_runs"
|
|
672
675
|
WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
|
|
673
676
|
[accountId, runStartedAt, object]
|
|
@@ -678,7 +681,8 @@ var PostgresClient = class {
|
|
|
678
681
|
object: row.object,
|
|
679
682
|
status: row.status,
|
|
680
683
|
processedCount: row.processed_count,
|
|
681
|
-
cursor: row.cursor
|
|
684
|
+
cursor: row.cursor,
|
|
685
|
+
pageCursor: row.page_cursor
|
|
682
686
|
};
|
|
683
687
|
}
|
|
684
688
|
/**
|
|
@@ -693,6 +697,23 @@ var PostgresClient = class {
|
|
|
693
697
|
[accountId, runStartedAt, object, count]
|
|
694
698
|
);
|
|
695
699
|
}
|
|
700
|
+
/**
|
|
701
|
+
* Update the pagination page_cursor used for backfills using Stripe list calls.
|
|
702
|
+
*/
|
|
703
|
+
async updateObjectPageCursor(accountId, runStartedAt, object, pageCursor) {
|
|
704
|
+
await this.query(
|
|
705
|
+
`UPDATE "${this.config.schema}"."_sync_obj_runs"
|
|
706
|
+
SET page_cursor = $4, updated_at = now()
|
|
707
|
+
WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
|
|
708
|
+
[accountId, runStartedAt, object, pageCursor]
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Clear the pagination page_cursor for an object sync.
|
|
713
|
+
*/
|
|
714
|
+
async clearObjectPageCursor(accountId, runStartedAt, object) {
|
|
715
|
+
await this.updateObjectPageCursor(accountId, runStartedAt, object, null);
|
|
716
|
+
}
|
|
696
717
|
/**
|
|
697
718
|
* Update the cursor for an object sync.
|
|
698
719
|
* Only updates if the new cursor is higher than the existing one (cursors should never decrease).
|
|
@@ -725,9 +746,10 @@ var PostgresClient = class {
|
|
|
725
746
|
}
|
|
726
747
|
/**
|
|
727
748
|
* Get the highest cursor from previous syncs for an object type.
|
|
728
|
-
*
|
|
729
|
-
*
|
|
730
|
-
*
|
|
749
|
+
* Uses only completed object runs.
|
|
750
|
+
* - During the initial backfill we page through history, but we also update the cursor as we go.
|
|
751
|
+
* If we crash mid-backfill and reuse that cursor, we can accidentally switch into incremental mode
|
|
752
|
+
* too early and only ever fetch the newest page (breaking the historical backfill).
|
|
731
753
|
*
|
|
732
754
|
* Handles two cursor formats:
|
|
733
755
|
* - Numeric: compared as bigint for correct ordering
|
|
@@ -742,11 +764,31 @@ var PostgresClient = class {
|
|
|
742
764
|
FROM "${this.config.schema}"."_sync_obj_runs" o
|
|
743
765
|
WHERE o."_account_id" = $1
|
|
744
766
|
AND o.object = $2
|
|
745
|
-
AND o.cursor IS NOT NULL
|
|
767
|
+
AND o.cursor IS NOT NULL
|
|
768
|
+
AND o.status = 'complete'`,
|
|
746
769
|
[accountId, object]
|
|
747
770
|
);
|
|
748
771
|
return result.rows[0]?.cursor ?? null;
|
|
749
772
|
}
|
|
773
|
+
/**
|
|
774
|
+
* Get the highest cursor from previous syncs for an object type, excluding the current run.
|
|
775
|
+
*/
|
|
776
|
+
async getLastCursorBeforeRun(accountId, object, runStartedAt) {
|
|
777
|
+
const result = await this.query(
|
|
778
|
+
`SELECT CASE
|
|
779
|
+
WHEN BOOL_OR(o.cursor !~ '^\\d+$') THEN MAX(o.cursor COLLATE "C")
|
|
780
|
+
ELSE MAX(CASE WHEN o.cursor ~ '^\\d+$' THEN o.cursor::bigint END)::text
|
|
781
|
+
END as cursor
|
|
782
|
+
FROM "${this.config.schema}"."_sync_obj_runs" o
|
|
783
|
+
WHERE o."_account_id" = $1
|
|
784
|
+
AND o.object = $2
|
|
785
|
+
AND o.cursor IS NOT NULL
|
|
786
|
+
AND o.status = 'complete'
|
|
787
|
+
AND o.run_started_at < $3`,
|
|
788
|
+
[accountId, object, runStartedAt]
|
|
789
|
+
);
|
|
790
|
+
return result.rows[0]?.cursor ?? null;
|
|
791
|
+
}
|
|
750
792
|
/**
|
|
751
793
|
* Delete all sync runs and object runs for an account.
|
|
752
794
|
* Useful for testing or resetting sync state.
|
|
@@ -767,7 +809,7 @@ var PostgresClient = class {
|
|
|
767
809
|
async completeObjectSync(accountId, runStartedAt, object) {
|
|
768
810
|
await this.query(
|
|
769
811
|
`UPDATE "${this.config.schema}"."_sync_obj_runs"
|
|
770
|
-
SET status = 'complete', completed_at = now()
|
|
812
|
+
SET status = 'complete', completed_at = now(), page_cursor = NULL
|
|
771
813
|
WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
|
|
772
814
|
[accountId, runStartedAt, object]
|
|
773
815
|
);
|
|
@@ -783,7 +825,7 @@ var PostgresClient = class {
|
|
|
783
825
|
async failObjectSync(accountId, runStartedAt, object, errorMessage) {
|
|
784
826
|
await this.query(
|
|
785
827
|
`UPDATE "${this.config.schema}"."_sync_obj_runs"
|
|
786
|
-
SET status = 'error', error_message = $4, completed_at = now()
|
|
828
|
+
SET status = 'error', error_message = $4, completed_at = now(), page_cursor = NULL
|
|
787
829
|
WHERE "_account_id" = $1 AND run_started_at = $2 AND object = $3`,
|
|
788
830
|
[accountId, runStartedAt, object, errorMessage]
|
|
789
831
|
);
|
|
@@ -2084,57 +2126,74 @@ var StripeSync = class {
|
|
|
2084
2126
|
* ```
|
|
2085
2127
|
*/
|
|
2086
2128
|
async processNext(object, params) {
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
processed: 0,
|
|
2102
|
-
hasMore: false,
|
|
2103
|
-
runStartedAt
|
|
2104
|
-
};
|
|
2105
|
-
}
|
|
2106
|
-
if (objRun?.status === "pending") {
|
|
2107
|
-
const started = await this.postgresClient.tryStartObjectSync(
|
|
2108
|
-
accountId,
|
|
2109
|
-
runStartedAt,
|
|
2110
|
-
resourceName
|
|
2111
|
-
);
|
|
2112
|
-
if (!started) {
|
|
2129
|
+
try {
|
|
2130
|
+
await this.getCurrentAccount();
|
|
2131
|
+
const accountId = await this.getAccountId();
|
|
2132
|
+
const resourceName = this.getResourceName(object);
|
|
2133
|
+
let runStartedAt;
|
|
2134
|
+
if (params?.runStartedAt) {
|
|
2135
|
+
runStartedAt = params.runStartedAt;
|
|
2136
|
+
} else {
|
|
2137
|
+
const { runKey } = await this.joinOrCreateSyncRun(params?.triggeredBy ?? "processNext");
|
|
2138
|
+
runStartedAt = runKey.runStartedAt;
|
|
2139
|
+
}
|
|
2140
|
+
await this.postgresClient.createObjectRuns(accountId, runStartedAt, [resourceName]);
|
|
2141
|
+
const objRun = await this.postgresClient.getObjectRun(accountId, runStartedAt, resourceName);
|
|
2142
|
+
if (objRun?.status === "complete" || objRun?.status === "error") {
|
|
2113
2143
|
return {
|
|
2114
2144
|
processed: 0,
|
|
2115
|
-
hasMore:
|
|
2145
|
+
hasMore: false,
|
|
2116
2146
|
runStartedAt
|
|
2117
2147
|
};
|
|
2118
2148
|
}
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2149
|
+
if (objRun?.status === "pending") {
|
|
2150
|
+
const started = await this.postgresClient.tryStartObjectSync(
|
|
2151
|
+
accountId,
|
|
2152
|
+
runStartedAt,
|
|
2153
|
+
resourceName
|
|
2154
|
+
);
|
|
2155
|
+
if (!started) {
|
|
2156
|
+
return {
|
|
2157
|
+
processed: 0,
|
|
2158
|
+
hasMore: true,
|
|
2159
|
+
runStartedAt
|
|
2160
|
+
};
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
let cursor = null;
|
|
2164
|
+
if (!params?.created) {
|
|
2165
|
+
const lastCursor = await this.postgresClient.getLastCursorBeforeRun(
|
|
2166
|
+
accountId,
|
|
2167
|
+
resourceName,
|
|
2168
|
+
runStartedAt
|
|
2169
|
+
);
|
|
2126
2170
|
cursor = lastCursor ?? null;
|
|
2127
2171
|
}
|
|
2172
|
+
const result = await this.fetchOnePage(
|
|
2173
|
+
object,
|
|
2174
|
+
accountId,
|
|
2175
|
+
resourceName,
|
|
2176
|
+
runStartedAt,
|
|
2177
|
+
cursor,
|
|
2178
|
+
objRun?.pageCursor ?? null,
|
|
2179
|
+
params
|
|
2180
|
+
);
|
|
2181
|
+
return result;
|
|
2182
|
+
} catch (error) {
|
|
2183
|
+
throw this.appendMigrationHint(error);
|
|
2128
2184
|
}
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2185
|
+
}
|
|
2186
|
+
appendMigrationHint(error) {
|
|
2187
|
+
const hint = "Error occurred. Make sure you are up to date with DB migrations which can sometimes help with this. Details:";
|
|
2188
|
+
const withHint = (message) => message.includes(hint) ? message : `${hint}
|
|
2189
|
+
${message}`;
|
|
2190
|
+
if (error instanceof Error) {
|
|
2191
|
+
const { stack } = error;
|
|
2192
|
+
error.message = withHint(error.message);
|
|
2193
|
+
if (stack) error.stack = stack;
|
|
2194
|
+
return error;
|
|
2195
|
+
}
|
|
2196
|
+
return new Error(withHint(String(error)));
|
|
2138
2197
|
}
|
|
2139
2198
|
/**
|
|
2140
2199
|
* Get the database resource name for a SyncObject type
|
|
@@ -2166,7 +2225,7 @@ var StripeSync = class {
|
|
|
2166
2225
|
* Uses resourceRegistry for DRY list/upsert operations.
|
|
2167
2226
|
* Uses the observable sync system for tracking progress.
|
|
2168
2227
|
*/
|
|
2169
|
-
async fetchOnePage(object, accountId, resourceName, runStartedAt, cursor, params) {
|
|
2228
|
+
async fetchOnePage(object, accountId, resourceName, runStartedAt, cursor, pageCursor, params) {
|
|
2170
2229
|
const limit = 100;
|
|
2171
2230
|
if (object === "payment_method" || object === "tax_id") {
|
|
2172
2231
|
this.config.logger?.warn(`processNext for ${object} requires customer context`);
|
|
@@ -2194,7 +2253,16 @@ var StripeSync = class {
|
|
|
2194
2253
|
listParams.created = created;
|
|
2195
2254
|
}
|
|
2196
2255
|
}
|
|
2256
|
+
if (pageCursor) {
|
|
2257
|
+
listParams.starting_after = pageCursor;
|
|
2258
|
+
}
|
|
2197
2259
|
const response = await config.listFn(listParams);
|
|
2260
|
+
if (response.data.length === 0 && response.has_more) {
|
|
2261
|
+
const message = `Stripe returned has_more=true with empty page for ${resourceName}. Aborting to avoid infinite loop.`;
|
|
2262
|
+
this.config.logger?.warn(message);
|
|
2263
|
+
await this.postgresClient.failObjectSync(accountId, runStartedAt, resourceName, message);
|
|
2264
|
+
return { processed: 0, hasMore: false, runStartedAt };
|
|
2265
|
+
}
|
|
2198
2266
|
if (response.data.length > 0) {
|
|
2199
2267
|
this.config.logger?.info(`processNext: upserting ${response.data.length} ${resourceName}`);
|
|
2200
2268
|
await config.upsertFn(response.data, accountId, params?.backfillRelatedEntities);
|
|
@@ -2215,6 +2283,15 @@ var StripeSync = class {
|
|
|
2215
2283
|
String(maxCreated)
|
|
2216
2284
|
);
|
|
2217
2285
|
}
|
|
2286
|
+
const lastId = response.data[response.data.length - 1].id;
|
|
2287
|
+
if (response.has_more) {
|
|
2288
|
+
await this.postgresClient.updateObjectPageCursor(
|
|
2289
|
+
accountId,
|
|
2290
|
+
runStartedAt,
|
|
2291
|
+
resourceName,
|
|
2292
|
+
lastId
|
|
2293
|
+
);
|
|
2294
|
+
}
|
|
2218
2295
|
}
|
|
2219
2296
|
if (!response.has_more) {
|
|
2220
2297
|
await this.postgresClient.completeObjectSync(accountId, runStartedAt, resourceName);
|
|
@@ -2363,31 +2440,43 @@ var StripeSync = class {
|
|
|
2363
2440
|
* This is used by workers and background processes that should cooperate.
|
|
2364
2441
|
*
|
|
2365
2442
|
* @param triggeredBy - What triggered this sync (for observability)
|
|
2443
|
+
* @param objectFilter - Optional specific object to sync (e.g. 'payment_intent'). If 'all' or undefined, syncs all objects.
|
|
2366
2444
|
* @returns Run key and list of objects to sync
|
|
2367
2445
|
*/
|
|
2368
|
-
async joinOrCreateSyncRun(triggeredBy = "worker") {
|
|
2446
|
+
async joinOrCreateSyncRun(triggeredBy = "worker", objectFilter) {
|
|
2369
2447
|
await this.getCurrentAccount();
|
|
2370
2448
|
const accountId = await this.getAccountId();
|
|
2371
2449
|
const result = await this.postgresClient.getOrCreateSyncRun(accountId, triggeredBy);
|
|
2450
|
+
const objects = objectFilter === "all" || objectFilter === void 0 ? this.getSupportedSyncObjects() : [objectFilter];
|
|
2372
2451
|
if (!result) {
|
|
2373
2452
|
const activeRun = await this.postgresClient.getActiveSyncRun(accountId);
|
|
2374
2453
|
if (!activeRun) {
|
|
2375
2454
|
throw new Error("Failed to get or create sync run");
|
|
2376
2455
|
}
|
|
2456
|
+
await this.postgresClient.createObjectRuns(
|
|
2457
|
+
activeRun.accountId,
|
|
2458
|
+
activeRun.runStartedAt,
|
|
2459
|
+
objects.map((obj) => this.getResourceName(obj))
|
|
2460
|
+
);
|
|
2377
2461
|
return {
|
|
2378
2462
|
runKey: { accountId: activeRun.accountId, runStartedAt: activeRun.runStartedAt },
|
|
2379
|
-
objects
|
|
2463
|
+
objects
|
|
2380
2464
|
};
|
|
2381
2465
|
}
|
|
2382
2466
|
const { accountId: runAccountId, runStartedAt } = result;
|
|
2467
|
+
await this.postgresClient.createObjectRuns(
|
|
2468
|
+
runAccountId,
|
|
2469
|
+
runStartedAt,
|
|
2470
|
+
objects.map((obj) => this.getResourceName(obj))
|
|
2471
|
+
);
|
|
2383
2472
|
return {
|
|
2384
2473
|
runKey: { accountId: runAccountId, runStartedAt },
|
|
2385
|
-
objects
|
|
2474
|
+
objects
|
|
2386
2475
|
};
|
|
2387
2476
|
}
|
|
2388
2477
|
async processUntilDone(params) {
|
|
2389
2478
|
const { object } = params ?? { object: "all" };
|
|
2390
|
-
const { runKey } = await this.joinOrCreateSyncRun("processUntilDone");
|
|
2479
|
+
const { runKey } = await this.joinOrCreateSyncRun("processUntilDone", object);
|
|
2391
2480
|
return this.processUntilDoneWithRun(runKey.runStartedAt, object, params);
|
|
2392
2481
|
}
|
|
2393
2482
|
/**
|
package/dist/index.d.cts
CHANGED
|
@@ -137,8 +137,10 @@ declare class PostgresClient {
|
|
|
137
137
|
/**
|
|
138
138
|
* Create object run entries for a sync run.
|
|
139
139
|
* All objects start as 'pending'.
|
|
140
|
+
*
|
|
141
|
+
* @param resourceNames - Database resource names (e.g. 'products', 'customers', NOT 'product', 'customer')
|
|
140
142
|
*/
|
|
141
|
-
createObjectRuns(accountId: string, runStartedAt: Date,
|
|
143
|
+
createObjectRuns(accountId: string, runStartedAt: Date, resourceNames: string[]): Promise<void>;
|
|
142
144
|
/**
|
|
143
145
|
* Try to start an object sync (respects max_concurrent).
|
|
144
146
|
* Returns true if claimed, false if already running or at concurrency limit.
|
|
@@ -155,12 +157,21 @@ declare class PostgresClient {
|
|
|
155
157
|
status: string;
|
|
156
158
|
processedCount: number;
|
|
157
159
|
cursor: string | null;
|
|
160
|
+
pageCursor: string | null;
|
|
158
161
|
} | null>;
|
|
159
162
|
/**
|
|
160
163
|
* Update progress for an object sync.
|
|
161
164
|
* Also touches updated_at for stale detection.
|
|
162
165
|
*/
|
|
163
166
|
incrementObjectProgress(accountId: string, runStartedAt: Date, object: string, count: number): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Update the pagination page_cursor used for backfills using Stripe list calls.
|
|
169
|
+
*/
|
|
170
|
+
updateObjectPageCursor(accountId: string, runStartedAt: Date, object: string, pageCursor: string | null): Promise<void>;
|
|
171
|
+
/**
|
|
172
|
+
* Clear the pagination page_cursor for an object sync.
|
|
173
|
+
*/
|
|
174
|
+
clearObjectPageCursor(accountId: string, runStartedAt: Date, object: string): Promise<void>;
|
|
164
175
|
/**
|
|
165
176
|
* Update the cursor for an object sync.
|
|
166
177
|
* Only updates if the new cursor is higher than the existing one (cursors should never decrease).
|
|
@@ -170,15 +181,20 @@ declare class PostgresClient {
|
|
|
170
181
|
updateObjectCursor(accountId: string, runStartedAt: Date, object: string, cursor: string | null): Promise<void>;
|
|
171
182
|
/**
|
|
172
183
|
* Get the highest cursor from previous syncs for an object type.
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
184
|
+
* Uses only completed object runs.
|
|
185
|
+
* - During the initial backfill we page through history, but we also update the cursor as we go.
|
|
186
|
+
* If we crash mid-backfill and reuse that cursor, we can accidentally switch into incremental mode
|
|
187
|
+
* too early and only ever fetch the newest page (breaking the historical backfill).
|
|
176
188
|
*
|
|
177
189
|
* Handles two cursor formats:
|
|
178
190
|
* - Numeric: compared as bigint for correct ordering
|
|
179
191
|
* - Composite cursors: compared as strings with COLLATE "C"
|
|
180
192
|
*/
|
|
181
193
|
getLastCompletedCursor(accountId: string, object: string): Promise<string | null>;
|
|
194
|
+
/**
|
|
195
|
+
* Get the highest cursor from previous syncs for an object type, excluding the current run.
|
|
196
|
+
*/
|
|
197
|
+
getLastCursorBeforeRun(accountId: string, object: string, runStartedAt: Date): Promise<string | null>;
|
|
182
198
|
/**
|
|
183
199
|
* Delete all sync runs and object runs for an account.
|
|
184
200
|
* Useful for testing or resetting sync state.
|
|
@@ -585,6 +601,7 @@ declare class StripeSync {
|
|
|
585
601
|
* ```
|
|
586
602
|
*/
|
|
587
603
|
processNext(object: Exclude<SyncObject, 'all' | 'customer_with_entitlements'>, params?: ProcessNextParams): Promise<ProcessNextResult>;
|
|
604
|
+
private appendMigrationHint;
|
|
588
605
|
/**
|
|
589
606
|
* Get the database resource name for a SyncObject type
|
|
590
607
|
*/
|
|
@@ -621,9 +638,10 @@ declare class StripeSync {
|
|
|
621
638
|
* This is used by workers and background processes that should cooperate.
|
|
622
639
|
*
|
|
623
640
|
* @param triggeredBy - What triggered this sync (for observability)
|
|
641
|
+
* @param objectFilter - Optional specific object to sync (e.g. 'payment_intent'). If 'all' or undefined, syncs all objects.
|
|
624
642
|
* @returns Run key and list of objects to sync
|
|
625
643
|
*/
|
|
626
|
-
joinOrCreateSyncRun(triggeredBy?: string): Promise<{
|
|
644
|
+
joinOrCreateSyncRun(triggeredBy?: string, objectFilter?: SyncObject): Promise<{
|
|
627
645
|
runKey: RunKey;
|
|
628
646
|
objects: Exclude<SyncObject, 'all' | 'customer_with_entitlements'>[];
|
|
629
647
|
}>;
|
package/dist/index.d.ts
CHANGED
|
@@ -137,8 +137,10 @@ declare class PostgresClient {
|
|
|
137
137
|
/**
|
|
138
138
|
* Create object run entries for a sync run.
|
|
139
139
|
* All objects start as 'pending'.
|
|
140
|
+
*
|
|
141
|
+
* @param resourceNames - Database resource names (e.g. 'products', 'customers', NOT 'product', 'customer')
|
|
140
142
|
*/
|
|
141
|
-
createObjectRuns(accountId: string, runStartedAt: Date,
|
|
143
|
+
createObjectRuns(accountId: string, runStartedAt: Date, resourceNames: string[]): Promise<void>;
|
|
142
144
|
/**
|
|
143
145
|
* Try to start an object sync (respects max_concurrent).
|
|
144
146
|
* Returns true if claimed, false if already running or at concurrency limit.
|
|
@@ -155,12 +157,21 @@ declare class PostgresClient {
|
|
|
155
157
|
status: string;
|
|
156
158
|
processedCount: number;
|
|
157
159
|
cursor: string | null;
|
|
160
|
+
pageCursor: string | null;
|
|
158
161
|
} | null>;
|
|
159
162
|
/**
|
|
160
163
|
* Update progress for an object sync.
|
|
161
164
|
* Also touches updated_at for stale detection.
|
|
162
165
|
*/
|
|
163
166
|
incrementObjectProgress(accountId: string, runStartedAt: Date, object: string, count: number): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Update the pagination page_cursor used for backfills using Stripe list calls.
|
|
169
|
+
*/
|
|
170
|
+
updateObjectPageCursor(accountId: string, runStartedAt: Date, object: string, pageCursor: string | null): Promise<void>;
|
|
171
|
+
/**
|
|
172
|
+
* Clear the pagination page_cursor for an object sync.
|
|
173
|
+
*/
|
|
174
|
+
clearObjectPageCursor(accountId: string, runStartedAt: Date, object: string): Promise<void>;
|
|
164
175
|
/**
|
|
165
176
|
* Update the cursor for an object sync.
|
|
166
177
|
* Only updates if the new cursor is higher than the existing one (cursors should never decrease).
|
|
@@ -170,15 +181,20 @@ declare class PostgresClient {
|
|
|
170
181
|
updateObjectCursor(accountId: string, runStartedAt: Date, object: string, cursor: string | null): Promise<void>;
|
|
171
182
|
/**
|
|
172
183
|
* Get the highest cursor from previous syncs for an object type.
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
184
|
+
* Uses only completed object runs.
|
|
185
|
+
* - During the initial backfill we page through history, but we also update the cursor as we go.
|
|
186
|
+
* If we crash mid-backfill and reuse that cursor, we can accidentally switch into incremental mode
|
|
187
|
+
* too early and only ever fetch the newest page (breaking the historical backfill).
|
|
176
188
|
*
|
|
177
189
|
* Handles two cursor formats:
|
|
178
190
|
* - Numeric: compared as bigint for correct ordering
|
|
179
191
|
* - Composite cursors: compared as strings with COLLATE "C"
|
|
180
192
|
*/
|
|
181
193
|
getLastCompletedCursor(accountId: string, object: string): Promise<string | null>;
|
|
194
|
+
/**
|
|
195
|
+
* Get the highest cursor from previous syncs for an object type, excluding the current run.
|
|
196
|
+
*/
|
|
197
|
+
getLastCursorBeforeRun(accountId: string, object: string, runStartedAt: Date): Promise<string | null>;
|
|
182
198
|
/**
|
|
183
199
|
* Delete all sync runs and object runs for an account.
|
|
184
200
|
* Useful for testing or resetting sync state.
|
|
@@ -585,6 +601,7 @@ declare class StripeSync {
|
|
|
585
601
|
* ```
|
|
586
602
|
*/
|
|
587
603
|
processNext(object: Exclude<SyncObject, 'all' | 'customer_with_entitlements'>, params?: ProcessNextParams): Promise<ProcessNextResult>;
|
|
604
|
+
private appendMigrationHint;
|
|
588
605
|
/**
|
|
589
606
|
* Get the database resource name for a SyncObject type
|
|
590
607
|
*/
|
|
@@ -621,9 +638,10 @@ declare class StripeSync {
|
|
|
621
638
|
* This is used by workers and background processes that should cooperate.
|
|
622
639
|
*
|
|
623
640
|
* @param triggeredBy - What triggered this sync (for observability)
|
|
641
|
+
* @param objectFilter - Optional specific object to sync (e.g. 'payment_intent'). If 'all' or undefined, syncs all objects.
|
|
624
642
|
* @returns Run key and list of objects to sync
|
|
625
643
|
*/
|
|
626
|
-
joinOrCreateSyncRun(triggeredBy?: string): Promise<{
|
|
644
|
+
joinOrCreateSyncRun(triggeredBy?: string, objectFilter?: SyncObject): Promise<{
|
|
627
645
|
runKey: RunKey;
|
|
628
646
|
objects: Exclude<SyncObject, 'all' | 'customer_with_entitlements'>[];
|
|
629
647
|
}>;
|
package/dist/index.js
CHANGED
package/dist/supabase/index.cjs
CHANGED
|
@@ -54,7 +54,7 @@ var workerFunctionCode = stripe_worker_default;
|
|
|
54
54
|
// package.json
|
|
55
55
|
var package_default = {
|
|
56
56
|
name: "stripe-experiment-sync",
|
|
57
|
-
version: "1.0.
|
|
57
|
+
version: "1.0.19",
|
|
58
58
|
private: false,
|
|
59
59
|
description: "Stripe Sync Engine to sync Stripe data to Postgres",
|
|
60
60
|
type: "module",
|
package/dist/supabase/index.js
CHANGED
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
uninstall,
|
|
10
10
|
webhookFunctionCode,
|
|
11
11
|
workerFunctionCode
|
|
12
|
-
} from "../chunk-
|
|
13
|
-
import "../chunk-
|
|
12
|
+
} from "../chunk-4P6TAP7L.js";
|
|
13
|
+
import "../chunk-CMGFQCD7.js";
|
|
14
14
|
export {
|
|
15
15
|
INSTALLATION_ERROR_SUFFIX,
|
|
16
16
|
INSTALLATION_INSTALLED_SUFFIX,
|