stripe-experiment-sync 1.0.9-beta.1765909347 → 1.0.10
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/{chunk-2CYYWBTD.js → chunk-62FKHVHJ.js} +27 -486
- package/dist/{chunk-SI3VFP3M.js → chunk-AHNO3EMD.js} +36 -9
- package/dist/{chunk-DBJCCGXP.js → chunk-FZQ4B7VZ.js} +12 -29
- package/dist/{chunk-3W3CERIG.js → chunk-VEEV6P4R.js} +1 -3
- package/dist/cli/index.cjs +82 -527
- package/dist/cli/index.js +12 -8
- package/dist/cli/lib.cjs +74 -523
- package/dist/cli/lib.d.cts +1 -2
- package/dist/cli/lib.d.ts +1 -2
- package/dist/cli/lib.js +4 -4
- package/dist/index.cjs +27 -490
- package/dist/index.d.cts +4 -107
- package/dist/index.d.ts +4 -107
- package/dist/index.js +2 -6
- package/dist/supabase/index.cjs +36 -11
- package/dist/supabase/index.d.cts +4 -2
- package/dist/supabase/index.d.ts +4 -2
- package/dist/supabase/index.js +2 -2
- package/package.json +1 -3
- package/dist/migrations/0059_sigma_subscription_item_change_events_v2_beta.sql +0 -61
- package/dist/migrations/0060_sigma_exchange_rates_from_usd.sql +0 -38
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
package_default
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VEEV6P4R.js";
|
|
4
4
|
|
|
5
5
|
// src/supabase/supabase.ts
|
|
6
6
|
import { SupabaseManagementAPI } from "supabase-management-js";
|
|
@@ -12,7 +12,7 @@ var stripe_setup_default = "import { StripeSync, runMigrations } from 'npm:strip
|
|
|
12
12
|
var stripe_webhook_default = "import { StripeSync } from 'npm:stripe-experiment-sync'\n\nDeno.serve(async (req) => {\n if (req.method !== 'POST') {\n return new Response('Method not allowed', { status: 405 })\n }\n\n const sig = req.headers.get('stripe-signature')\n if (!sig) {\n return new Response('Missing stripe-signature header', { status: 400 })\n }\n\n const rawDbUrl = Deno.env.get('SUPABASE_DB_URL')\n if (!rawDbUrl) {\n return new Response(JSON.stringify({ error: 'SUPABASE_DB_URL not set' }), { status: 500 })\n }\n const dbUrl = rawDbUrl.replace(/[?&]sslmode=[^&]*/g, '').replace(/[?&]$/, '')\n\n const stripeSync = new StripeSync({\n poolConfig: { connectionString: dbUrl, max: 1 },\n stripeSecretKey: Deno.env.get('STRIPE_SECRET_KEY')!,\n })\n\n try {\n const rawBody = new Uint8Array(await req.arrayBuffer())\n await stripeSync.processWebhook(rawBody, sig)\n return new Response(JSON.stringify({ received: true }), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n console.error('Webhook processing error:', error)\n const isSignatureError =\n error.message?.includes('signature') || error.type === 'StripeSignatureVerificationError'\n const status = isSignatureError ? 400 : 500\n return new Response(JSON.stringify({ error: error.message }), {\n status,\n headers: { 'Content-Type': 'application/json' },\n })\n } finally {\n await stripeSync.postgresClient.pool.end()\n }\n})\n";
|
|
13
13
|
|
|
14
14
|
// raw-ts:/home/runner/work/sync-engine/sync-engine/packages/sync-engine/src/supabase/edge-functions/stripe-worker.ts
|
|
15
|
-
var stripe_worker_default = "/**\n * Stripe Sync Worker\n *\n * Triggered by pg_cron
|
|
15
|
+
var stripe_worker_default = "/**\n * Stripe Sync Worker\n *\n * Triggered by pg_cron at a configurable interval (default: 60 seconds). Uses pgmq for durable work queue.\n *\n * Flow:\n * 1. Read batch of messages from pgmq (qty=10, vt=60s)\n * 2. If queue empty: enqueue all objects (continuous sync)\n * 3. Process messages in parallel (Promise.all):\n * - processNext(object)\n * - Delete message on success\n * - Re-enqueue if hasMore\n * 4. Return results summary\n *\n * Concurrency:\n * - Multiple workers can run concurrently via overlapping pg_cron triggers.\n * - Each worker processes its batch of messages in parallel (Promise.all).\n * - pgmq visibility timeout prevents duplicate message reads across workers.\n * - processNext() is idempotent (uses internal cursor tracking), so duplicate\n * processing on timeout/crash is safe.\n */\n\nimport { StripeSync } from 'npm:stripe-experiment-sync'\nimport postgres from 'npm:postgres'\n\nconst QUEUE_NAME = 'stripe_sync_work'\nconst VISIBILITY_TIMEOUT = 60 // seconds\nconst BATCH_SIZE = 10\n\nDeno.serve(async (req) => {\n const authHeader = req.headers.get('Authorization')\n if (!authHeader?.startsWith('Bearer ')) {\n return new Response('Unauthorized', { status: 401 })\n }\n\n const rawDbUrl = Deno.env.get('SUPABASE_DB_URL')\n if (!rawDbUrl) {\n return new Response(JSON.stringify({ error: 'SUPABASE_DB_URL not set' }), { status: 500 })\n }\n const dbUrl = rawDbUrl.replace(/[?&]sslmode=[^&]*/g, '').replace(/[?&]$/, '')\n\n let sql\n let stripeSync\n\n try {\n sql = postgres(dbUrl, { max: 1, prepare: false })\n } catch (error) {\n return new Response(\n JSON.stringify({\n error: 'Failed to create postgres connection',\n details: error.message,\n stack: error.stack,\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n )\n }\n\n try {\n stripeSync = new StripeSync({\n poolConfig: { connectionString: dbUrl, max: 1 },\n stripeSecretKey: Deno.env.get('STRIPE_SECRET_KEY')!,\n })\n } catch (error) {\n await sql.end()\n return new Response(\n JSON.stringify({\n error: 'Failed to create StripeSync',\n details: error.message,\n stack: error.stack,\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n )\n }\n\n try {\n // Read batch of messages from queue\n const messages = await sql`\n SELECT * FROM pgmq.read(${QUEUE_NAME}::text, ${VISIBILITY_TIMEOUT}::int, ${BATCH_SIZE}::int)\n `\n\n // If queue empty, enqueue all objects for continuous sync\n if (messages.length === 0) {\n // Create sync run to make enqueued work visible (status='pending')\n const { objects } = await stripeSync.joinOrCreateSyncRun('worker')\n const msgs = objects.map((object) => JSON.stringify({ object }))\n\n await sql`\n SELECT pgmq.send_batch(\n ${QUEUE_NAME}::text,\n ${sql.array(msgs)}::jsonb[]\n )\n `\n\n return new Response(JSON.stringify({ enqueued: objects.length, objects }), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n\n // Process messages in parallel\n const results = await Promise.all(\n messages.map(async (msg) => {\n const { object } = msg.message as { object: string }\n\n try {\n const result = await stripeSync.processNext(object)\n\n // Delete message on success (cast to bigint to disambiguate overloaded function)\n await sql`SELECT pgmq.delete(${QUEUE_NAME}::text, ${msg.msg_id}::bigint)`\n\n // Re-enqueue if more pages\n if (result.hasMore) {\n await sql`SELECT pgmq.send(${QUEUE_NAME}::text, ${sql.json({ object })}::jsonb)`\n }\n\n return { object, ...result }\n } catch (error) {\n // Log error but continue to next message\n // Message will become visible again after visibility timeout\n console.error(`Error processing ${object}:`, error)\n return {\n object,\n processed: 0,\n hasMore: false,\n error: error.message,\n stack: error.stack,\n }\n }\n })\n )\n\n return new Response(JSON.stringify({ results }), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n console.error('Worker error:', error)\n return new Response(JSON.stringify({ error: error.message, stack: error.stack }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n })\n } finally {\n if (sql) await sql.end()\n if (stripeSync) await stripeSync.postgresClient.pool.end()\n }\n})\n";
|
|
16
16
|
|
|
17
17
|
// src/supabase/edge-function-code.ts
|
|
18
18
|
var setupFunctionCode = stripe_setup_default;
|
|
@@ -86,8 +86,29 @@ var SupabaseSetupClient = class {
|
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
88
|
* Setup pg_cron job to invoke worker function
|
|
89
|
+
* @param intervalSeconds - How often to run the worker (default: 60 seconds)
|
|
89
90
|
*/
|
|
90
|
-
async setupPgCronJob() {
|
|
91
|
+
async setupPgCronJob(intervalSeconds = 60) {
|
|
92
|
+
if (!Number.isInteger(intervalSeconds) || intervalSeconds < 1) {
|
|
93
|
+
throw new Error(`Invalid interval: ${intervalSeconds}. Must be a positive integer.`);
|
|
94
|
+
}
|
|
95
|
+
let schedule;
|
|
96
|
+
if (intervalSeconds < 60) {
|
|
97
|
+
schedule = `${intervalSeconds} seconds`;
|
|
98
|
+
} else if (intervalSeconds % 60 === 0) {
|
|
99
|
+
const minutes = intervalSeconds / 60;
|
|
100
|
+
if (minutes < 60) {
|
|
101
|
+
schedule = `*/${minutes} * * * *`;
|
|
102
|
+
} else {
|
|
103
|
+
throw new Error(
|
|
104
|
+
`Invalid interval: ${intervalSeconds}. Intervals >= 3600 seconds (1 hour) are not supported. Use a value between 1-3599 seconds.`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Invalid interval: ${intervalSeconds}. Must be either 1-59 seconds or a multiple of 60 (e.g., 60, 120, 180).`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
91
112
|
const serviceRoleKey = await this.getServiceRoleKey();
|
|
92
113
|
const escapedServiceRoleKey = serviceRoleKey.replace(/'/g, "''");
|
|
93
114
|
const sql = `
|
|
@@ -115,11 +136,11 @@ var SupabaseSetupClient = class {
|
|
|
115
136
|
SELECT 1 FROM cron.job WHERE jobname = 'stripe-sync-scheduler'
|
|
116
137
|
);
|
|
117
138
|
|
|
118
|
-
-- Create job to invoke worker
|
|
139
|
+
-- Create job to invoke worker at configured interval
|
|
119
140
|
-- Worker reads from pgmq, enqueues objects if empty, and processes sync work
|
|
120
141
|
SELECT cron.schedule(
|
|
121
142
|
'stripe-sync-worker',
|
|
122
|
-
'
|
|
143
|
+
'${schedule}',
|
|
123
144
|
$$
|
|
124
145
|
SELECT net.http_post(
|
|
125
146
|
url := 'https://${this.projectRef}.${this.projectBaseUrl}/functions/v1/stripe-worker',
|
|
@@ -347,7 +368,7 @@ var SupabaseSetupClient = class {
|
|
|
347
368
|
`from 'npm:stripe-experiment-sync@${version}'`
|
|
348
369
|
);
|
|
349
370
|
}
|
|
350
|
-
async install(stripeKey, packageVersion) {
|
|
371
|
+
async install(stripeKey, packageVersion, workerIntervalSeconds) {
|
|
351
372
|
const trimmedStripeKey = stripeKey.trim();
|
|
352
373
|
if (!trimmedStripeKey.startsWith("sk_") && !trimmedStripeKey.startsWith("rk_")) {
|
|
353
374
|
throw new Error('Stripe key should start with "sk_" or "rk_"');
|
|
@@ -371,7 +392,7 @@ var SupabaseSetupClient = class {
|
|
|
371
392
|
if (!setupResult.success) {
|
|
372
393
|
throw new Error(`Setup failed: ${setupResult.error}`);
|
|
373
394
|
}
|
|
374
|
-
await this.setupPgCronJob();
|
|
395
|
+
await this.setupPgCronJob(workerIntervalSeconds);
|
|
375
396
|
await this.updateInstallationComment(
|
|
376
397
|
`${STRIPE_SCHEMA_COMMENT_PREFIX} v${package_default.version} ${INSTALLATION_INSTALLED_SUFFIX}`
|
|
377
398
|
);
|
|
@@ -384,14 +405,20 @@ var SupabaseSetupClient = class {
|
|
|
384
405
|
}
|
|
385
406
|
};
|
|
386
407
|
async function install(params) {
|
|
387
|
-
const {
|
|
408
|
+
const {
|
|
409
|
+
supabaseAccessToken,
|
|
410
|
+
supabaseProjectRef,
|
|
411
|
+
stripeKey,
|
|
412
|
+
packageVersion,
|
|
413
|
+
workerIntervalSeconds
|
|
414
|
+
} = params;
|
|
388
415
|
const client = new SupabaseSetupClient({
|
|
389
416
|
accessToken: supabaseAccessToken,
|
|
390
417
|
projectRef: supabaseProjectRef,
|
|
391
418
|
projectBaseUrl: params.baseProjectUrl,
|
|
392
419
|
managementApiBaseUrl: params.baseManagementApiUrl
|
|
393
420
|
});
|
|
394
|
-
await client.install(stripeKey, packageVersion);
|
|
421
|
+
await client.install(stripeKey, packageVersion, workerIntervalSeconds);
|
|
395
422
|
}
|
|
396
423
|
async function uninstall(params) {
|
|
397
424
|
const { supabaseAccessToken, supabaseProjectRef, stripeKey } = params;
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
StripeSync,
|
|
3
3
|
createStripeWebSocketClient,
|
|
4
4
|
runMigrations
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-62FKHVHJ.js";
|
|
6
6
|
import {
|
|
7
7
|
install,
|
|
8
8
|
uninstall
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-AHNO3EMD.js";
|
|
10
10
|
|
|
11
11
|
// src/cli/config.ts
|
|
12
12
|
import dotenv from "dotenv";
|
|
@@ -18,7 +18,6 @@ async function loadConfig(options) {
|
|
|
18
18
|
config.stripeApiKey = options.stripeKey || process.env.STRIPE_API_KEY || "";
|
|
19
19
|
config.ngrokAuthToken = options.ngrokToken || process.env.NGROK_AUTH_TOKEN || "";
|
|
20
20
|
config.databaseUrl = options.databaseUrl || process.env.DATABASE_URL || "";
|
|
21
|
-
config.enableSigmaSync = options.enableSigmaSync ?? (process.env.ENABLE_SIGMA_SYNC !== void 0 ? process.env.ENABLE_SIGMA_SYNC === "true" : void 0);
|
|
22
21
|
const questions = [];
|
|
23
22
|
if (!config.stripeApiKey) {
|
|
24
23
|
questions.push({
|
|
@@ -30,8 +29,8 @@ async function loadConfig(options) {
|
|
|
30
29
|
if (!input || input.trim() === "") {
|
|
31
30
|
return "Stripe API key is required";
|
|
32
31
|
}
|
|
33
|
-
if (!input.startsWith("sk_")
|
|
34
|
-
return 'Stripe API key should start with "sk_"
|
|
32
|
+
if (!input.startsWith("sk_")) {
|
|
33
|
+
return 'Stripe API key should start with "sk_"';
|
|
35
34
|
}
|
|
36
35
|
return true;
|
|
37
36
|
}
|
|
@@ -54,22 +53,11 @@ async function loadConfig(options) {
|
|
|
54
53
|
}
|
|
55
54
|
});
|
|
56
55
|
}
|
|
57
|
-
if (config.enableSigmaSync === void 0) {
|
|
58
|
-
questions.push({
|
|
59
|
-
type: "confirm",
|
|
60
|
-
name: "enableSigmaSync",
|
|
61
|
-
message: "Enable Sigma sync? (Requires Sigma access in Stripe API key)",
|
|
62
|
-
default: false
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
56
|
if (questions.length > 0) {
|
|
66
|
-
console.log(chalk.yellow("\nMissing configuration. Please provide:"));
|
|
57
|
+
console.log(chalk.yellow("\nMissing required configuration. Please provide:"));
|
|
67
58
|
const answers = await inquirer.prompt(questions);
|
|
68
59
|
Object.assign(config, answers);
|
|
69
60
|
}
|
|
70
|
-
if (config.enableSigmaSync === void 0) {
|
|
71
|
-
config.enableSigmaSync = false;
|
|
72
|
-
}
|
|
73
61
|
return config;
|
|
74
62
|
}
|
|
75
63
|
|
|
@@ -129,9 +117,7 @@ var VALID_SYNC_OBJECTS = [
|
|
|
129
117
|
"credit_note",
|
|
130
118
|
"early_fraud_warning",
|
|
131
119
|
"refund",
|
|
132
|
-
"checkout_sessions"
|
|
133
|
-
"subscription_item_change_events_v2_beta",
|
|
134
|
-
"exchange_rates_from_usd"
|
|
120
|
+
"checkout_sessions"
|
|
135
121
|
];
|
|
136
122
|
async function backfillCommand(options, entityName) {
|
|
137
123
|
let stripeSync = null;
|
|
@@ -160,8 +146,8 @@ async function backfillCommand(options, entityName) {
|
|
|
160
146
|
if (!input || input.trim() === "") {
|
|
161
147
|
return "Stripe API key is required";
|
|
162
148
|
}
|
|
163
|
-
if (!input.startsWith("sk_")
|
|
164
|
-
return 'Stripe API key should start with "sk_"
|
|
149
|
+
if (!input.startsWith("sk_")) {
|
|
150
|
+
return 'Stripe API key should start with "sk_"';
|
|
165
151
|
}
|
|
166
152
|
return true;
|
|
167
153
|
}
|
|
@@ -218,7 +204,6 @@ async function backfillCommand(options, entityName) {
|
|
|
218
204
|
stripeSync = new StripeSync({
|
|
219
205
|
databaseUrl: config.databaseUrl,
|
|
220
206
|
stripeSecretKey: config.stripeApiKey,
|
|
221
|
-
enableSigmaSync: process.env.ENABLE_SIGMA_SYNC === "true",
|
|
222
207
|
stripeApiVersion: process.env.STRIPE_API_VERSION || "2020-08-27",
|
|
223
208
|
autoExpandLists: process.env.AUTO_EXPAND_LISTS === "true",
|
|
224
209
|
backfillRelatedEntities: process.env.BACKFILL_RELATED_ENTITIES !== "false",
|
|
@@ -382,7 +367,6 @@ Mode: ${modeLabel}`));
|
|
|
382
367
|
stripeSync = new StripeSync({
|
|
383
368
|
databaseUrl: config.databaseUrl,
|
|
384
369
|
stripeSecretKey: config.stripeApiKey,
|
|
385
|
-
enableSigmaSync: config.enableSigmaSync,
|
|
386
370
|
stripeApiVersion: process.env.STRIPE_API_VERSION || "2020-08-27",
|
|
387
371
|
autoExpandLists: process.env.AUTO_EXPAND_LISTS === "true",
|
|
388
372
|
backfillRelatedEntities: process.env.BACKFILL_RELATED_ENTITIES !== "false",
|
|
@@ -530,8 +514,7 @@ async function installCommand(options) {
|
|
|
530
514
|
mask: "*",
|
|
531
515
|
validate: (input) => {
|
|
532
516
|
if (!input.trim()) return "Stripe key is required";
|
|
533
|
-
if (!input.startsWith("sk_")
|
|
534
|
-
return 'Stripe key should start with "sk_" or "rk_"';
|
|
517
|
+
if (!input.startsWith("sk_")) return 'Stripe key should start with "sk_"';
|
|
535
518
|
return true;
|
|
536
519
|
}
|
|
537
520
|
});
|
|
@@ -550,7 +533,8 @@ async function installCommand(options) {
|
|
|
550
533
|
supabaseAccessToken: accessToken,
|
|
551
534
|
supabaseProjectRef: projectRef,
|
|
552
535
|
stripeKey,
|
|
553
|
-
packageVersion: options.packageVersion
|
|
536
|
+
packageVersion: options.packageVersion,
|
|
537
|
+
workerIntervalSeconds: options.workerInterval
|
|
554
538
|
});
|
|
555
539
|
console.log(chalk3.cyan("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
556
540
|
console.log(chalk3.cyan.bold(" Installation Complete!"));
|
|
@@ -601,8 +585,7 @@ async function uninstallCommand(options) {
|
|
|
601
585
|
mask: "*",
|
|
602
586
|
validate: (input) => {
|
|
603
587
|
if (!input.trim()) return "Stripe key is required";
|
|
604
|
-
if (!input.startsWith("sk_")
|
|
605
|
-
return 'Stripe key should start with "sk_" or "rk_"';
|
|
588
|
+
if (!input.startsWith("sk_")) return 'Stripe key should start with "sk_"';
|
|
606
589
|
return true;
|
|
607
590
|
}
|
|
608
591
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// package.json
|
|
2
2
|
var package_default = {
|
|
3
3
|
name: "stripe-experiment-sync",
|
|
4
|
-
version: "1.0.
|
|
4
|
+
version: "1.0.10",
|
|
5
5
|
private: false,
|
|
6
6
|
description: "Stripe Sync Engine to sync Stripe data to Postgres",
|
|
7
7
|
type: "module",
|
|
@@ -41,7 +41,6 @@ var package_default = {
|
|
|
41
41
|
dotenv: "^16.4.7",
|
|
42
42
|
express: "^4.18.2",
|
|
43
43
|
inquirer: "^12.3.0",
|
|
44
|
-
papaparse: "5.4.1",
|
|
45
44
|
pg: "^8.16.3",
|
|
46
45
|
"pg-node-migrations": "0.0.8",
|
|
47
46
|
stripe: "^17.7.0",
|
|
@@ -53,7 +52,6 @@ var package_default = {
|
|
|
53
52
|
"@types/express": "^4.17.21",
|
|
54
53
|
"@types/inquirer": "^9.0.7",
|
|
55
54
|
"@types/node": "^24.10.1",
|
|
56
|
-
"@types/papaparse": "5.3.16",
|
|
57
55
|
"@types/pg": "^8.15.5",
|
|
58
56
|
"@types/ws": "^8.5.13",
|
|
59
57
|
"@types/yesql": "^4.1.4",
|