stripe-experiment-sync 0.0.1 → 0.0.3

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
@@ -31,7 +31,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  PostgresClient: () => PostgresClient,
34
- StripeAutoSync: () => StripeAutoSync
34
+ StripeAutoSync: () => StripeAutoSync,
35
+ runMigrations: () => runMigrations
35
36
  });
36
37
  module.exports = __toCommonJS(index_exports);
37
38
 
@@ -891,7 +892,8 @@ var StripeAutoSync = class {
891
892
  webhookPath: options.webhookPath || "/stripe-webhooks",
892
893
  stripeApiVersion: options.stripeApiVersion || "2020-08-27",
893
894
  autoExpandLists: options.autoExpandLists !== void 0 ? options.autoExpandLists : false,
894
- backfillRelatedEntities: options.backfillRelatedEntities !== void 0 ? options.backfillRelatedEntities : true
895
+ backfillRelatedEntities: options.backfillRelatedEntities !== void 0 ? options.backfillRelatedEntities : true,
896
+ keepWebhooksOnShutdown: options.keepWebhooksOnShutdown !== void 0 ? options.keepWebhooksOnShutdown : true
895
897
  };
896
898
  }
897
899
  /**
@@ -907,10 +909,30 @@ var StripeAutoSync = class {
907
909
  */
908
910
  async start(app) {
909
911
  try {
910
- await runMigrations({
911
- databaseUrl: this.options.databaseUrl,
912
- schema: this.options.schema
913
- });
912
+ try {
913
+ await runMigrations({
914
+ databaseUrl: this.options.databaseUrl,
915
+ schema: this.options.schema
916
+ });
917
+ } catch (migrationError) {
918
+ console.warn("Migration failed, dropping schema and retrying...");
919
+ console.warn("Migration error:", migrationError instanceof Error ? migrationError.message : String(migrationError));
920
+ const { Client: Client2 } = await import("pg");
921
+ const client = new Client2({ connectionString: this.options.databaseUrl });
922
+ try {
923
+ await client.connect();
924
+ await client.query(`DROP SCHEMA IF EXISTS "${this.options.schema}" CASCADE`);
925
+ console.log(`\u2713 Dropped schema: ${this.options.schema}`);
926
+ } finally {
927
+ await client.end();
928
+ }
929
+ console.log("Retrying migrations...");
930
+ await runMigrations({
931
+ databaseUrl: this.options.databaseUrl,
932
+ schema: this.options.schema
933
+ });
934
+ console.log("\u2713 Migrations completed successfully after retry");
935
+ }
914
936
  const poolConfig = {
915
937
  max: 10,
916
938
  connectionString: this.options.databaseUrl,
@@ -926,7 +948,7 @@ var StripeAutoSync = class {
926
948
  poolConfig
927
949
  });
928
950
  const baseUrl = this.options.baseUrl();
929
- const { webhook, uuid } = await this.stripeSync.createManagedWebhook(
951
+ const { webhook, uuid } = await this.stripeSync.findOrCreateManagedWebhook(
930
952
  `${baseUrl}${this.options.webhookPath}`,
931
953
  {
932
954
  enabled_events: ["*"],
@@ -936,6 +958,10 @@ var StripeAutoSync = class {
936
958
  );
937
959
  this.webhookId = webhook.id;
938
960
  this.webhookUuid = uuid;
961
+ console.log("Starting initial backfill of all Stripe data...");
962
+ const backfillResult = await this.stripeSync.syncBackfill({ object: "all" });
963
+ const totalSynced = Object.values(backfillResult).reduce((sum, result) => sum + (result?.synced || 0), 0);
964
+ console.log(`\u2713 Backfill complete: ${totalSynced} objects synced`);
939
965
  this.mountWebhook(app);
940
966
  app.use(this.getBodyParserMiddleware());
941
967
  return {
@@ -956,10 +982,10 @@ var StripeAutoSync = class {
956
982
  }
957
983
  /**
958
984
  * Stops all services and cleans up resources:
959
- * 1. Deletes Stripe webhook endpoint from Stripe and database
985
+ * 1. Optionally deletes Stripe webhook endpoint from Stripe and database (based on keepWebhooksOnShutdown)
960
986
  */
961
987
  async stop() {
962
- if (this.webhookId && this.stripeSync) {
988
+ if (this.webhookId && this.stripeSync && !this.options.keepWebhooksOnShutdown) {
963
989
  try {
964
990
  await this.stripeSync.deleteManagedWebhook(this.webhookId);
965
991
  } catch (error) {
@@ -1058,7 +1084,7 @@ var StripeSync = class {
1058
1084
  postgresClient;
1059
1085
  async processWebhook(payload, signature, uuid) {
1060
1086
  const result = await this.postgresClient.query(
1061
- `SELECT secret FROM "${this.config.schema || DEFAULT_SCHEMA}"."managed_webhooks" WHERE uuid = $1`,
1087
+ `SELECT secret FROM "${this.config.schema || DEFAULT_SCHEMA}"."_managed_webhooks" WHERE uuid = $1`,
1062
1088
  [uuid]
1063
1089
  );
1064
1090
  if (result.rows.length === 0) {
@@ -2275,16 +2301,38 @@ var StripeSync = class {
2275
2301
  await this.upsertManagedWebhooks([webhookWithUuid]);
2276
2302
  return { webhook, uuid };
2277
2303
  }
2304
+ async findOrCreateManagedWebhook(baseUrl, params) {
2305
+ const existingWebhooks = await this.listManagedWebhooks();
2306
+ for (const existingWebhook of existingWebhooks) {
2307
+ const existingBaseUrl = existingWebhook.url.replace(/\/[^/]+$/, "");
2308
+ if (existingBaseUrl === baseUrl) {
2309
+ try {
2310
+ const stripeWebhook = await this.stripe.webhookEndpoints.retrieve(
2311
+ existingWebhook.id
2312
+ );
2313
+ if (stripeWebhook.status === "enabled") {
2314
+ return {
2315
+ webhook: stripeWebhook,
2316
+ uuid: existingWebhook.uuid
2317
+ };
2318
+ }
2319
+ } catch (error) {
2320
+ continue;
2321
+ }
2322
+ }
2323
+ }
2324
+ return this.createManagedWebhook(baseUrl, params);
2325
+ }
2278
2326
  async getManagedWebhook(id) {
2279
2327
  const result = await this.postgresClient.query(
2280
- `SELECT * FROM "${this.config.schema || DEFAULT_SCHEMA}"."managed_webhooks" WHERE id = $1`,
2328
+ `SELECT * FROM "${this.config.schema || DEFAULT_SCHEMA}"."_managed_webhooks" WHERE id = $1`,
2281
2329
  [id]
2282
2330
  );
2283
2331
  return result.rows.length > 0 ? result.rows[0] : null;
2284
2332
  }
2285
2333
  async listManagedWebhooks() {
2286
2334
  const result = await this.postgresClient.query(
2287
- `SELECT * FROM "${this.config.schema || DEFAULT_SCHEMA}"."managed_webhooks" ORDER BY created DESC`
2335
+ `SELECT * FROM "${this.config.schema || DEFAULT_SCHEMA}"."_managed_webhooks" ORDER BY created DESC`
2288
2336
  );
2289
2337
  return result.rows;
2290
2338
  }
@@ -2297,12 +2345,12 @@ var StripeSync = class {
2297
2345
  }
2298
2346
  async deleteManagedWebhook(id) {
2299
2347
  await this.stripe.webhookEndpoints.del(id);
2300
- return this.postgresClient.delete("managed_webhooks", id);
2348
+ return this.postgresClient.delete("_managed_webhooks", id);
2301
2349
  }
2302
2350
  async upsertManagedWebhooks(webhooks, syncTimestamp) {
2303
2351
  return this.postgresClient.upsertManyWithTimestampProtection(
2304
2352
  webhooks,
2305
- "managed_webhooks",
2353
+ "_managed_webhooks",
2306
2354
  managedWebhookSchema,
2307
2355
  syncTimestamp
2308
2356
  );
@@ -2366,5 +2414,6 @@ function chunkArray(array, chunkSize) {
2366
2414
  // Annotate the CommonJS export names for ESM import in node:
2367
2415
  0 && (module.exports = {
2368
2416
  PostgresClient,
2369
- StripeAutoSync
2417
+ StripeAutoSync,
2418
+ runMigrations
2370
2419
  });
package/dist/index.d.cts CHANGED
@@ -2,6 +2,7 @@ import { Express } from 'express';
2
2
  import pg, { PoolConfig, QueryResult } from 'pg';
3
3
  import pino from 'pino';
4
4
  import Stripe from 'stripe';
5
+ import { ConnectionOptions } from 'node:tls';
5
6
 
6
7
  interface StripeAutoSyncOptions {
7
8
  databaseUrl: string;
@@ -12,6 +13,7 @@ interface StripeAutoSyncOptions {
12
13
  stripeApiVersion?: string;
13
14
  autoExpandLists?: boolean;
14
15
  backfillRelatedEntities?: boolean;
16
+ keepWebhooksOnShutdown?: boolean;
15
17
  }
16
18
  interface StripeAutoSyncInfo {
17
19
  baseUrl: string;
@@ -44,7 +46,7 @@ declare class StripeAutoSync {
44
46
  start(app: Express): Promise<StripeAutoSyncInfo>;
45
47
  /**
46
48
  * Stops all services and cleans up resources:
47
- * 1. Deletes Stripe webhook endpoint from Stripe and database
49
+ * 1. Optionally deletes Stripe webhook endpoint from Stripe and database (based on keepWebhooksOnShutdown)
48
50
  */
49
51
  stop(): Promise<void>;
50
52
  /**
@@ -219,4 +221,12 @@ declare class PostgresClient {
219
221
  private cleanseArrayField;
220
222
  }
221
223
 
222
- export { PostgresClient, type RevalidateEntity, StripeAutoSync, type StripeAutoSyncInfo, type StripeAutoSyncOptions, type StripeSyncConfig, type Sync, type SyncBackfill, type SyncBackfillParams, type SyncEntitlementsParams, type SyncFeaturesParams, type SyncObject };
224
+ type MigrationConfig = {
225
+ schema: string;
226
+ databaseUrl: string;
227
+ ssl?: ConnectionOptions;
228
+ logger?: pino.Logger;
229
+ };
230
+ declare function runMigrations(config: MigrationConfig): Promise<void>;
231
+
232
+ export { PostgresClient, type RevalidateEntity, StripeAutoSync, type StripeAutoSyncInfo, type StripeAutoSyncOptions, type StripeSyncConfig, type Sync, type SyncBackfill, type SyncBackfillParams, type SyncEntitlementsParams, type SyncFeaturesParams, type SyncObject, runMigrations };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Express } from 'express';
2
2
  import pg, { PoolConfig, QueryResult } from 'pg';
3
3
  import pino from 'pino';
4
4
  import Stripe from 'stripe';
5
+ import { ConnectionOptions } from 'node:tls';
5
6
 
6
7
  interface StripeAutoSyncOptions {
7
8
  databaseUrl: string;
@@ -12,6 +13,7 @@ interface StripeAutoSyncOptions {
12
13
  stripeApiVersion?: string;
13
14
  autoExpandLists?: boolean;
14
15
  backfillRelatedEntities?: boolean;
16
+ keepWebhooksOnShutdown?: boolean;
15
17
  }
16
18
  interface StripeAutoSyncInfo {
17
19
  baseUrl: string;
@@ -44,7 +46,7 @@ declare class StripeAutoSync {
44
46
  start(app: Express): Promise<StripeAutoSyncInfo>;
45
47
  /**
46
48
  * Stops all services and cleans up resources:
47
- * 1. Deletes Stripe webhook endpoint from Stripe and database
49
+ * 1. Optionally deletes Stripe webhook endpoint from Stripe and database (based on keepWebhooksOnShutdown)
48
50
  */
49
51
  stop(): Promise<void>;
50
52
  /**
@@ -219,4 +221,12 @@ declare class PostgresClient {
219
221
  private cleanseArrayField;
220
222
  }
221
223
 
222
- export { PostgresClient, type RevalidateEntity, StripeAutoSync, type StripeAutoSyncInfo, type StripeAutoSyncOptions, type StripeSyncConfig, type Sync, type SyncBackfill, type SyncBackfillParams, type SyncEntitlementsParams, type SyncFeaturesParams, type SyncObject };
224
+ type MigrationConfig = {
225
+ schema: string;
226
+ databaseUrl: string;
227
+ ssl?: ConnectionOptions;
228
+ logger?: pino.Logger;
229
+ };
230
+ declare function runMigrations(config: MigrationConfig): Promise<void>;
231
+
232
+ export { PostgresClient, type RevalidateEntity, StripeAutoSync, type StripeAutoSyncInfo, type StripeAutoSyncOptions, type StripeSyncConfig, type Sync, type SyncBackfill, type SyncBackfillParams, type SyncEntitlementsParams, type SyncFeaturesParams, type SyncObject, runMigrations };
package/dist/index.js CHANGED
@@ -850,7 +850,8 @@ var StripeAutoSync = class {
850
850
  webhookPath: options.webhookPath || "/stripe-webhooks",
851
851
  stripeApiVersion: options.stripeApiVersion || "2020-08-27",
852
852
  autoExpandLists: options.autoExpandLists !== void 0 ? options.autoExpandLists : false,
853
- backfillRelatedEntities: options.backfillRelatedEntities !== void 0 ? options.backfillRelatedEntities : true
853
+ backfillRelatedEntities: options.backfillRelatedEntities !== void 0 ? options.backfillRelatedEntities : true,
854
+ keepWebhooksOnShutdown: options.keepWebhooksOnShutdown !== void 0 ? options.keepWebhooksOnShutdown : true
854
855
  };
855
856
  }
856
857
  /**
@@ -866,10 +867,30 @@ var StripeAutoSync = class {
866
867
  */
867
868
  async start(app) {
868
869
  try {
869
- await runMigrations({
870
- databaseUrl: this.options.databaseUrl,
871
- schema: this.options.schema
872
- });
870
+ try {
871
+ await runMigrations({
872
+ databaseUrl: this.options.databaseUrl,
873
+ schema: this.options.schema
874
+ });
875
+ } catch (migrationError) {
876
+ console.warn("Migration failed, dropping schema and retrying...");
877
+ console.warn("Migration error:", migrationError instanceof Error ? migrationError.message : String(migrationError));
878
+ const { Client: Client2 } = await import("pg");
879
+ const client = new Client2({ connectionString: this.options.databaseUrl });
880
+ try {
881
+ await client.connect();
882
+ await client.query(`DROP SCHEMA IF EXISTS "${this.options.schema}" CASCADE`);
883
+ console.log(`\u2713 Dropped schema: ${this.options.schema}`);
884
+ } finally {
885
+ await client.end();
886
+ }
887
+ console.log("Retrying migrations...");
888
+ await runMigrations({
889
+ databaseUrl: this.options.databaseUrl,
890
+ schema: this.options.schema
891
+ });
892
+ console.log("\u2713 Migrations completed successfully after retry");
893
+ }
873
894
  const poolConfig = {
874
895
  max: 10,
875
896
  connectionString: this.options.databaseUrl,
@@ -885,7 +906,7 @@ var StripeAutoSync = class {
885
906
  poolConfig
886
907
  });
887
908
  const baseUrl = this.options.baseUrl();
888
- const { webhook, uuid } = await this.stripeSync.createManagedWebhook(
909
+ const { webhook, uuid } = await this.stripeSync.findOrCreateManagedWebhook(
889
910
  `${baseUrl}${this.options.webhookPath}`,
890
911
  {
891
912
  enabled_events: ["*"],
@@ -895,6 +916,10 @@ var StripeAutoSync = class {
895
916
  );
896
917
  this.webhookId = webhook.id;
897
918
  this.webhookUuid = uuid;
919
+ console.log("Starting initial backfill of all Stripe data...");
920
+ const backfillResult = await this.stripeSync.syncBackfill({ object: "all" });
921
+ const totalSynced = Object.values(backfillResult).reduce((sum, result) => sum + (result?.synced || 0), 0);
922
+ console.log(`\u2713 Backfill complete: ${totalSynced} objects synced`);
898
923
  this.mountWebhook(app);
899
924
  app.use(this.getBodyParserMiddleware());
900
925
  return {
@@ -915,10 +940,10 @@ var StripeAutoSync = class {
915
940
  }
916
941
  /**
917
942
  * Stops all services and cleans up resources:
918
- * 1. Deletes Stripe webhook endpoint from Stripe and database
943
+ * 1. Optionally deletes Stripe webhook endpoint from Stripe and database (based on keepWebhooksOnShutdown)
919
944
  */
920
945
  async stop() {
921
- if (this.webhookId && this.stripeSync) {
946
+ if (this.webhookId && this.stripeSync && !this.options.keepWebhooksOnShutdown) {
922
947
  try {
923
948
  await this.stripeSync.deleteManagedWebhook(this.webhookId);
924
949
  } catch (error) {
@@ -1017,7 +1042,7 @@ var StripeSync = class {
1017
1042
  postgresClient;
1018
1043
  async processWebhook(payload, signature, uuid) {
1019
1044
  const result = await this.postgresClient.query(
1020
- `SELECT secret FROM "${this.config.schema || DEFAULT_SCHEMA}"."managed_webhooks" WHERE uuid = $1`,
1045
+ `SELECT secret FROM "${this.config.schema || DEFAULT_SCHEMA}"."_managed_webhooks" WHERE uuid = $1`,
1021
1046
  [uuid]
1022
1047
  );
1023
1048
  if (result.rows.length === 0) {
@@ -2234,16 +2259,38 @@ var StripeSync = class {
2234
2259
  await this.upsertManagedWebhooks([webhookWithUuid]);
2235
2260
  return { webhook, uuid };
2236
2261
  }
2262
+ async findOrCreateManagedWebhook(baseUrl, params) {
2263
+ const existingWebhooks = await this.listManagedWebhooks();
2264
+ for (const existingWebhook of existingWebhooks) {
2265
+ const existingBaseUrl = existingWebhook.url.replace(/\/[^/]+$/, "");
2266
+ if (existingBaseUrl === baseUrl) {
2267
+ try {
2268
+ const stripeWebhook = await this.stripe.webhookEndpoints.retrieve(
2269
+ existingWebhook.id
2270
+ );
2271
+ if (stripeWebhook.status === "enabled") {
2272
+ return {
2273
+ webhook: stripeWebhook,
2274
+ uuid: existingWebhook.uuid
2275
+ };
2276
+ }
2277
+ } catch (error) {
2278
+ continue;
2279
+ }
2280
+ }
2281
+ }
2282
+ return this.createManagedWebhook(baseUrl, params);
2283
+ }
2237
2284
  async getManagedWebhook(id) {
2238
2285
  const result = await this.postgresClient.query(
2239
- `SELECT * FROM "${this.config.schema || DEFAULT_SCHEMA}"."managed_webhooks" WHERE id = $1`,
2286
+ `SELECT * FROM "${this.config.schema || DEFAULT_SCHEMA}"."_managed_webhooks" WHERE id = $1`,
2240
2287
  [id]
2241
2288
  );
2242
2289
  return result.rows.length > 0 ? result.rows[0] : null;
2243
2290
  }
2244
2291
  async listManagedWebhooks() {
2245
2292
  const result = await this.postgresClient.query(
2246
- `SELECT * FROM "${this.config.schema || DEFAULT_SCHEMA}"."managed_webhooks" ORDER BY created DESC`
2293
+ `SELECT * FROM "${this.config.schema || DEFAULT_SCHEMA}"."_managed_webhooks" ORDER BY created DESC`
2247
2294
  );
2248
2295
  return result.rows;
2249
2296
  }
@@ -2256,12 +2303,12 @@ var StripeSync = class {
2256
2303
  }
2257
2304
  async deleteManagedWebhook(id) {
2258
2305
  await this.stripe.webhookEndpoints.del(id);
2259
- return this.postgresClient.delete("managed_webhooks", id);
2306
+ return this.postgresClient.delete("_managed_webhooks", id);
2260
2307
  }
2261
2308
  async upsertManagedWebhooks(webhooks, syncTimestamp) {
2262
2309
  return this.postgresClient.upsertManyWithTimestampProtection(
2263
2310
  webhooks,
2264
- "managed_webhooks",
2311
+ "_managed_webhooks",
2265
2312
  managedWebhookSchema,
2266
2313
  syncTimestamp
2267
2314
  );
@@ -2324,5 +2371,6 @@ function chunkArray(array, chunkSize) {
2324
2371
  }
2325
2372
  export {
2326
2373
  PostgresClient,
2327
- StripeAutoSync
2374
+ StripeAutoSync,
2375
+ runMigrations
2328
2376
  };
@@ -0,0 +1,2 @@
1
+ -- Rename managed_webhooks table to _managed_webhooks
2
+ alter table if exists "stripe"."managed_webhooks" rename to "_managed_webhooks";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stripe-experiment-sync",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "private": false,
5
5
  "description": "Stripe Sync Engine to sync Stripe data based on webhooks to Postgres",
6
6
  "type": "module",