stripe-experiment-sync 1.0.3 → 1.0.7

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.
@@ -0,0 +1,57 @@
1
+ -- Rename sync observability tables and create public sync_runs view
2
+ -- Internal tables use _ prefix, public view is sync_runs
3
+
4
+ -- Step 1: Drop the old sync_dashboard view
5
+ DROP VIEW IF EXISTS "stripe"."sync_dashboard";
6
+
7
+ -- Step 2: Rename tables to plural (keep _ prefix for internal tables)
8
+ ALTER TABLE "stripe"."_sync_run" RENAME TO "_sync_runs";
9
+ ALTER TABLE "stripe"."_sync_obj_run" RENAME TO "_sync_obj_runs";
10
+
11
+ -- Step 3: Update foreign key constraint name
12
+ ALTER TABLE "stripe"."_sync_obj_runs"
13
+ DROP CONSTRAINT IF EXISTS fk_sync_obj_run_parent;
14
+
15
+ ALTER TABLE "stripe"."_sync_obj_runs"
16
+ ADD CONSTRAINT fk_sync_obj_runs_parent
17
+ FOREIGN KEY ("_account_id", run_started_at)
18
+ REFERENCES "stripe"."_sync_runs" ("_account_id", started_at);
19
+
20
+ -- Step 4: Recreate indexes with new table names
21
+ DROP INDEX IF EXISTS "stripe"."idx_sync_run_account_status";
22
+ DROP INDEX IF EXISTS "stripe"."idx_sync_obj_run_status";
23
+
24
+ CREATE INDEX idx_sync_runs_account_status
25
+ ON "stripe"."_sync_runs" ("_account_id", closed_at);
26
+
27
+ CREATE INDEX idx_sync_obj_runs_status
28
+ ON "stripe"."_sync_obj_runs" ("_account_id", run_started_at, status);
29
+
30
+ -- Step 5: Create public sync_runs view (one row per run with aggregates)
31
+ CREATE VIEW "stripe"."sync_runs" AS
32
+ SELECT
33
+ r._account_id as account_id,
34
+ r.started_at,
35
+ r.closed_at,
36
+ r.triggered_by,
37
+ r.max_concurrent,
38
+ -- Aggregate metrics from child objects
39
+ COALESCE(SUM(o.processed_count), 0) as total_processed,
40
+ COUNT(o.*) as total_objects,
41
+ COUNT(*) FILTER (WHERE o.status = 'complete') as complete_count,
42
+ COUNT(*) FILTER (WHERE o.status = 'error') as error_count,
43
+ COUNT(*) FILTER (WHERE o.status = 'running') as running_count,
44
+ COUNT(*) FILTER (WHERE o.status = 'pending') as pending_count,
45
+ -- Collect error messages if any
46
+ STRING_AGG(o.error_message, '; ') FILTER (WHERE o.error_message IS NOT NULL) as error_message,
47
+ -- Derive overall status from run state and object states
48
+ CASE
49
+ WHEN r.closed_at IS NULL THEN 'running'
50
+ WHEN COUNT(*) FILTER (WHERE o.status = 'error') > 0 THEN 'error'
51
+ ELSE 'complete'
52
+ END as status
53
+ FROM "stripe"."_sync_runs" r
54
+ LEFT JOIN "stripe"."_sync_obj_runs" o
55
+ ON o._account_id = r._account_id
56
+ AND o.run_started_at = r.started_at
57
+ GROUP BY r._account_id, r.started_at, r.closed_at, r.triggered_by, r.max_concurrent;
@@ -30,7 +30,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/supabase/index.ts
31
31
  var supabase_exports = {};
32
32
  __export(supabase_exports, {
33
- SupabaseDeployClient: () => SupabaseDeployClient,
33
+ INSTALLATION_ERROR_SUFFIX: () => INSTALLATION_ERROR_SUFFIX,
34
+ INSTALLATION_INSTALLED_SUFFIX: () => INSTALLATION_INSTALLED_SUFFIX,
35
+ INSTALLATION_STARTED_SUFFIX: () => INSTALLATION_STARTED_SUFFIX,
36
+ STRIPE_SCHEMA_COMMENT_PREFIX: () => STRIPE_SCHEMA_COMMENT_PREFIX,
37
+ SupabaseDeployClient: () => SupabaseSetupClient,
38
+ SupabaseSetupClient: () => SupabaseSetupClient,
34
39
  install: () => install,
35
40
  setupFunctionCode: () => setupFunctionCode,
36
41
  uninstall: () => uninstall,
@@ -59,12 +64,12 @@ var workerFunctionCode = stripe_worker_default;
59
64
  // package.json
60
65
  var package_default = {
61
66
  name: "stripe-experiment-sync",
62
- version: "1.0.3",
67
+ version: "1.0.7",
63
68
  private: false,
64
69
  description: "Stripe Sync Engine to sync Stripe data to Postgres",
65
70
  type: "module",
66
71
  main: "./dist/index.cjs",
67
- bin: "./dist/cli/index.cjs",
72
+ bin: "./dist/cli/index.js",
68
73
  exports: {
69
74
  ".": {
70
75
  types: "./dist/index.d.ts",
@@ -75,12 +80,17 @@ var package_default = {
75
80
  types: "./dist/supabase/index.d.ts",
76
81
  import: "./dist/supabase/index.js",
77
82
  require: "./dist/supabase/index.cjs"
83
+ },
84
+ "./cli": {
85
+ types: "./dist/cli/lib.d.ts",
86
+ import: "./dist/cli/lib.js",
87
+ require: "./dist/cli/lib.cjs"
78
88
  }
79
89
  },
80
90
  scripts: {
81
91
  clean: "rimraf dist",
82
92
  prebuild: "npm run clean",
83
- build: "tsup src/index.ts src/supabase/index.ts --format esm,cjs --dts --shims && cp -r src/database/migrations dist/migrations",
93
+ build: "tsup src/index.ts src/supabase/index.ts src/cli/index.ts src/cli/lib.ts --format esm,cjs --dts --shims && cp -r src/database/migrations dist/migrations",
84
94
  lint: "eslint src --ext .ts",
85
95
  test: "vitest"
86
96
  },
@@ -88,21 +98,28 @@ var package_default = {
88
98
  "dist"
89
99
  ],
90
100
  dependencies: {
101
+ "@ngrok/ngrok": "^1.4.1",
102
+ chalk: "^5.3.0",
103
+ commander: "^12.1.0",
104
+ dotenv: "^16.4.7",
105
+ express: "^4.18.2",
106
+ inquirer: "^12.3.0",
91
107
  pg: "^8.16.3",
92
108
  "pg-node-migrations": "0.0.8",
109
+ stripe: "^17.7.0",
93
110
  "supabase-management-js": "^0.1.6",
94
111
  ws: "^8.18.0",
95
112
  yesql: "^7.0.0"
96
113
  },
97
- peerDependencies: {
98
- stripe: "> 11"
99
- },
100
114
  devDependencies: {
115
+ "@types/express": "^4.17.21",
116
+ "@types/inquirer": "^9.0.7",
101
117
  "@types/node": "^24.10.1",
102
118
  "@types/pg": "^8.15.5",
103
119
  "@types/ws": "^8.5.13",
104
120
  "@types/yesql": "^4.1.4",
105
121
  "@vitest/ui": "^4.0.9",
122
+ tsx: "^4.19.2",
106
123
  vitest: "^3.2.4"
107
124
  },
108
125
  repository: {
@@ -128,14 +145,21 @@ var package_default = {
128
145
 
129
146
  // src/supabase/supabase.ts
130
147
  var import_stripe = __toESM(require("stripe"), 1);
131
- var SupabaseDeployClient = class {
148
+ var STRIPE_SCHEMA_COMMENT_PREFIX = "stripe-sync";
149
+ var INSTALLATION_STARTED_SUFFIX = "installation:started";
150
+ var INSTALLATION_ERROR_SUFFIX = "installation:error";
151
+ var INSTALLATION_INSTALLED_SUFFIX = "installed";
152
+ var SupabaseSetupClient = class {
132
153
  api;
133
154
  projectRef;
134
- baseUrl;
155
+ projectBaseUrl;
135
156
  constructor(options) {
136
- this.api = new import_supabase_management_js.SupabaseManagementAPI({ accessToken: options.accessToken });
157
+ this.api = new import_supabase_management_js.SupabaseManagementAPI({
158
+ accessToken: options.accessToken,
159
+ baseUrl: options.managementApiBaseUrl
160
+ });
137
161
  this.projectRef = options.projectRef;
138
- this.baseUrl = options.baseUrl || process.env.SUPABASE_BASE_URL || "supabase.co";
162
+ this.projectBaseUrl = options.projectBaseUrl || process.env.SUPABASE_BASE_URL || "supabase.co";
139
163
  }
140
164
  /**
141
165
  * Validate that the project exists and we have access
@@ -222,7 +246,7 @@ var SupabaseDeployClient = class {
222
246
  '10 seconds',
223
247
  $$
224
248
  SELECT net.http_post(
225
- url := 'https://${this.projectRef}.${this.baseUrl}/functions/v1/stripe-worker',
249
+ url := 'https://${this.projectRef}.${this.projectBaseUrl}/functions/v1/stripe-worker',
226
250
  headers := jsonb_build_object(
227
251
  'Authorization', 'Bearer ' || (SELECT decrypted_secret FROM vault.decrypted_secrets WHERE name = 'stripe_sync_service_role_key')
228
252
  )
@@ -236,7 +260,7 @@ var SupabaseDeployClient = class {
236
260
  * Get the webhook URL for this project
237
261
  */
238
262
  getWebhookUrl() {
239
- return `https://${this.projectRef}.${this.baseUrl}/functions/v1/stripe-webhook`;
263
+ return `https://${this.projectRef}.${this.projectBaseUrl}/functions/v1/stripe-webhook`;
240
264
  }
241
265
  /**
242
266
  * Get the service role key for this project (needed to invoke Edge Functions)
@@ -264,13 +288,13 @@ var SupabaseDeployClient = class {
264
288
  * Get the project URL
265
289
  */
266
290
  getProjectUrl() {
267
- return `https://${this.projectRef}.${this.baseUrl}`;
291
+ return `https://${this.projectRef}.${this.projectBaseUrl}`;
268
292
  }
269
293
  /**
270
294
  * Invoke an Edge Function
271
295
  */
272
296
  async invokeFunction(name, serviceRoleKey) {
273
- const url = `https://${this.projectRef}.${this.baseUrl}/functions/v1/${name}`;
297
+ const url = `https://${this.projectRef}.${this.projectBaseUrl}/functions/v1/${name}`;
274
298
  const response = await fetch(url, {
275
299
  method: "POST",
276
300
  headers: {
@@ -327,15 +351,15 @@ var SupabaseDeployClient = class {
327
351
  WHERE nspname = '${schema}'`
328
352
  );
329
353
  const comment = commentCheck[0]?.rows?.[0]?.comment;
330
- if (!comment || !comment.includes("stripe-sync")) {
354
+ if (!comment || !comment.includes(STRIPE_SCHEMA_COMMENT_PREFIX)) {
331
355
  throw new Error(
332
356
  `Legacy installation detected: Schema '${schema}' and migrations table exist, but missing stripe-sync comment marker. This may be a legacy installation or manually created schema. Please contact support or manually drop the schema before proceeding.`
333
357
  );
334
358
  }
335
- if (comment.includes("installation:started")) {
359
+ if (comment.includes(INSTALLATION_STARTED_SUFFIX)) {
336
360
  return false;
337
361
  }
338
- if (comment.includes("installation:error")) {
362
+ if (comment.includes(INSTALLATION_ERROR_SUFFIX)) {
339
363
  throw new Error(
340
364
  `Installation failed: Schema '${schema}' exists but installation encountered an error. Comment: ${comment}. Please uninstall and install again.`
341
365
  );
@@ -380,7 +404,7 @@ var SupabaseDeployClient = class {
380
404
  * Removes all Edge Functions, secrets, database resources, and Stripe webhooks
381
405
  */
382
406
  async uninstall(stripeSecretKey) {
383
- const stripe = new import_stripe.default(stripeSecretKey, { apiVersion: "2025-05-28.basil" });
407
+ const stripe = new import_stripe.default(stripeSecretKey, { apiVersion: "2025-02-24.acacia" });
384
408
  try {
385
409
  try {
386
410
  const webhookResult = await this.runSQL(`
@@ -424,41 +448,60 @@ var SupabaseDeployClient = class {
424
448
  throw new Error(`Uninstall failed: ${error instanceof Error ? error.message : String(error)}`);
425
449
  }
426
450
  }
451
+ async install(stripeKey) {
452
+ const trimmedStripeKey = stripeKey.trim();
453
+ if (!trimmedStripeKey.startsWith("sk_") && !trimmedStripeKey.startsWith("rk_")) {
454
+ throw new Error('Stripe key should start with "sk_" or "rk_"');
455
+ }
456
+ try {
457
+ await this.validateProject();
458
+ await this.runSQL(`CREATE SCHEMA IF NOT EXISTS stripe`);
459
+ await this.updateInstallationComment(
460
+ `${STRIPE_SCHEMA_COMMENT_PREFIX} v${package_default.version} ${INSTALLATION_STARTED_SUFFIX}`
461
+ );
462
+ await this.deployFunction("stripe-setup", setupFunctionCode);
463
+ await this.deployFunction("stripe-webhook", webhookFunctionCode);
464
+ await this.deployFunction("stripe-worker", workerFunctionCode);
465
+ await this.setSecrets([{ name: "STRIPE_SECRET_KEY", value: trimmedStripeKey }]);
466
+ const serviceRoleKey = await this.getServiceRoleKey();
467
+ const setupResult = await this.invokeFunction("stripe-setup", serviceRoleKey);
468
+ if (!setupResult.success) {
469
+ throw new Error(`Setup failed: ${setupResult.error}`);
470
+ }
471
+ let pgCronEnabled = false;
472
+ try {
473
+ await this.setupPgCronJob();
474
+ pgCronEnabled = true;
475
+ } catch {
476
+ console.warn("pg_cron setup failed - falling back to manual worker invocation");
477
+ }
478
+ if (!pgCronEnabled) {
479
+ try {
480
+ await this.invokeFunction("stripe-worker", serviceRoleKey);
481
+ } catch (err) {
482
+ console.warn("Failed to trigger initial worker invocation:", err);
483
+ }
484
+ }
485
+ await this.updateInstallationComment(
486
+ `${STRIPE_SCHEMA_COMMENT_PREFIX} v${package_default.version} ${INSTALLATION_INSTALLED_SUFFIX}`
487
+ );
488
+ } catch (error) {
489
+ await this.updateInstallationComment(
490
+ `${STRIPE_SCHEMA_COMMENT_PREFIX} v${package_default.version} ${INSTALLATION_ERROR_SUFFIX} - ${error instanceof Error ? error.message : String(error)}`
491
+ );
492
+ throw error;
493
+ }
494
+ }
427
495
  };
428
496
  async function install(params) {
429
497
  const { supabaseAccessToken, supabaseProjectRef, stripeKey } = params;
430
- const trimmedStripeKey = stripeKey.trim();
431
- if (!trimmedStripeKey.startsWith("sk_") && !trimmedStripeKey.startsWith("rk_")) {
432
- throw new Error('Stripe key should start with "sk_" or "rk_"');
433
- }
434
- const client = new SupabaseDeployClient({
498
+ const client = new SupabaseSetupClient({
435
499
  accessToken: supabaseAccessToken,
436
- projectRef: supabaseProjectRef
500
+ projectRef: supabaseProjectRef,
501
+ projectBaseUrl: params.baseProjectUrl,
502
+ managementApiBaseUrl: params.baseManagementApiUrl
437
503
  });
438
- try {
439
- await client.validateProject();
440
- await client.runSQL(`CREATE SCHEMA IF NOT EXISTS stripe`);
441
- await client.updateInstallationComment(`stripe-sync v${package_default.version} installation:started`);
442
- await client.deployFunction("stripe-setup", setupFunctionCode);
443
- await client.deployFunction("stripe-webhook", webhookFunctionCode);
444
- await client.deployFunction("stripe-worker", workerFunctionCode);
445
- await client.setSecrets([{ name: "STRIPE_SECRET_KEY", value: trimmedStripeKey }]);
446
- const serviceRoleKey = await client.getServiceRoleKey();
447
- const setupResult = await client.invokeFunction("stripe-setup", serviceRoleKey);
448
- if (!setupResult.success) {
449
- throw new Error(`Setup failed: ${setupResult.error}`);
450
- }
451
- try {
452
- await client.setupPgCronJob();
453
- } catch {
454
- }
455
- await client.updateInstallationComment(`stripe-sync v${package_default.version} installed`);
456
- } catch (error) {
457
- await client.updateInstallationComment(
458
- `stripe-sync v${package_default.version} installation:error - ${error instanceof Error ? error.message : String(error)}`
459
- );
460
- throw error;
461
- }
504
+ await client.install(stripeKey);
462
505
  }
463
506
  async function uninstall(params) {
464
507
  const { supabaseAccessToken, supabaseProjectRef, stripeKey } = params;
@@ -466,7 +509,7 @@ async function uninstall(params) {
466
509
  if (!trimmedStripeKey.startsWith("sk_") && !trimmedStripeKey.startsWith("rk_")) {
467
510
  throw new Error('Stripe key should start with "sk_" or "rk_"');
468
511
  }
469
- const client = new SupabaseDeployClient({
512
+ const client = new SupabaseSetupClient({
470
513
  accessToken: supabaseAccessToken,
471
514
  projectRef: supabaseProjectRef
472
515
  });
@@ -474,7 +517,12 @@ async function uninstall(params) {
474
517
  }
475
518
  // Annotate the CommonJS export names for ESM import in node:
476
519
  0 && (module.exports = {
520
+ INSTALLATION_ERROR_SUFFIX,
521
+ INSTALLATION_INSTALLED_SUFFIX,
522
+ INSTALLATION_STARTED_SUFFIX,
523
+ STRIPE_SCHEMA_COMMENT_PREFIX,
477
524
  SupabaseDeployClient,
525
+ SupabaseSetupClient,
478
526
  install,
479
527
  setupFunctionCode,
480
528
  uninstall,
@@ -1,17 +1,22 @@
1
+ declare const STRIPE_SCHEMA_COMMENT_PREFIX = "stripe-sync";
2
+ declare const INSTALLATION_STARTED_SUFFIX = "installation:started";
3
+ declare const INSTALLATION_ERROR_SUFFIX = "installation:error";
4
+ declare const INSTALLATION_INSTALLED_SUFFIX = "installed";
1
5
  interface DeployClientOptions {
2
6
  accessToken: string;
3
7
  projectRef: string;
4
- baseUrl?: string;
8
+ projectBaseUrl?: string;
9
+ managementApiBaseUrl?: string;
5
10
  }
6
11
  interface ProjectInfo {
7
12
  id: string;
8
13
  name: string;
9
14
  region: string;
10
15
  }
11
- declare class SupabaseDeployClient {
16
+ declare class SupabaseSetupClient {
12
17
  private api;
13
18
  private projectRef;
14
- private baseUrl;
19
+ private projectBaseUrl;
15
20
  constructor(options: DeployClientOptions);
16
21
  /**
17
22
  * Validate that the project exists and we have access
@@ -88,11 +93,14 @@ declare class SupabaseDeployClient {
88
93
  * Removes all Edge Functions, secrets, database resources, and Stripe webhooks
89
94
  */
90
95
  uninstall(stripeSecretKey: string): Promise<void>;
96
+ install(stripeKey: string): Promise<void>;
91
97
  }
92
98
  declare function install(params: {
93
99
  supabaseAccessToken: string;
94
100
  supabaseProjectRef: string;
95
101
  stripeKey: string;
102
+ baseProjectUrl?: string;
103
+ baseManagementApiUrl?: string;
96
104
  }): Promise<void>;
97
105
  declare function uninstall(params: {
98
106
  supabaseAccessToken: string;
@@ -104,4 +112,4 @@ declare const setupFunctionCode: string;
104
112
  declare const webhookFunctionCode: string;
105
113
  declare const workerFunctionCode: string;
106
114
 
107
- export { type DeployClientOptions, type ProjectInfo, SupabaseDeployClient, install, setupFunctionCode, uninstall, webhookFunctionCode, workerFunctionCode };
115
+ export { type DeployClientOptions, INSTALLATION_ERROR_SUFFIX, INSTALLATION_INSTALLED_SUFFIX, INSTALLATION_STARTED_SUFFIX, type ProjectInfo, STRIPE_SCHEMA_COMMENT_PREFIX, SupabaseSetupClient as SupabaseDeployClient, SupabaseSetupClient, install, setupFunctionCode, uninstall, webhookFunctionCode, workerFunctionCode };
@@ -1,17 +1,22 @@
1
+ declare const STRIPE_SCHEMA_COMMENT_PREFIX = "stripe-sync";
2
+ declare const INSTALLATION_STARTED_SUFFIX = "installation:started";
3
+ declare const INSTALLATION_ERROR_SUFFIX = "installation:error";
4
+ declare const INSTALLATION_INSTALLED_SUFFIX = "installed";
1
5
  interface DeployClientOptions {
2
6
  accessToken: string;
3
7
  projectRef: string;
4
- baseUrl?: string;
8
+ projectBaseUrl?: string;
9
+ managementApiBaseUrl?: string;
5
10
  }
6
11
  interface ProjectInfo {
7
12
  id: string;
8
13
  name: string;
9
14
  region: string;
10
15
  }
11
- declare class SupabaseDeployClient {
16
+ declare class SupabaseSetupClient {
12
17
  private api;
13
18
  private projectRef;
14
- private baseUrl;
19
+ private projectBaseUrl;
15
20
  constructor(options: DeployClientOptions);
16
21
  /**
17
22
  * Validate that the project exists and we have access
@@ -88,11 +93,14 @@ declare class SupabaseDeployClient {
88
93
  * Removes all Edge Functions, secrets, database resources, and Stripe webhooks
89
94
  */
90
95
  uninstall(stripeSecretKey: string): Promise<void>;
96
+ install(stripeKey: string): Promise<void>;
91
97
  }
92
98
  declare function install(params: {
93
99
  supabaseAccessToken: string;
94
100
  supabaseProjectRef: string;
95
101
  stripeKey: string;
102
+ baseProjectUrl?: string;
103
+ baseManagementApiUrl?: string;
96
104
  }): Promise<void>;
97
105
  declare function uninstall(params: {
98
106
  supabaseAccessToken: string;
@@ -104,4 +112,4 @@ declare const setupFunctionCode: string;
104
112
  declare const webhookFunctionCode: string;
105
113
  declare const workerFunctionCode: string;
106
114
 
107
- export { type DeployClientOptions, type ProjectInfo, SupabaseDeployClient, install, setupFunctionCode, uninstall, webhookFunctionCode, workerFunctionCode };
115
+ export { type DeployClientOptions, INSTALLATION_ERROR_SUFFIX, INSTALLATION_INSTALLED_SUFFIX, INSTALLATION_STARTED_SUFFIX, type ProjectInfo, STRIPE_SCHEMA_COMMENT_PREFIX, SupabaseSetupClient as SupabaseDeployClient, SupabaseSetupClient, install, setupFunctionCode, uninstall, webhookFunctionCode, workerFunctionCode };