stripe-experiment-sync 1.0.6 → 1.0.8
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 +25 -4
- package/dist/{chunk-3UERGK2O.js → chunk-CKWVW2JK.js} +18 -6
- package/dist/chunk-J7BH3XD6.js +3436 -0
- package/dist/chunk-X2OQQCC2.js +409 -0
- package/dist/chunk-YH6KRZDQ.js +634 -0
- package/dist/cli/index.cjs +4592 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +59 -0
- package/dist/cli/lib.cjs +4571 -0
- package/dist/cli/lib.d.cts +69 -0
- package/dist/cli/lib.d.ts +69 -0
- package/dist/cli/lib.js +21 -0
- package/dist/index.cjs +243 -58
- package/dist/index.d.cts +54 -2
- package/dist/index.d.ts +54 -2
- package/dist/index.js +9 -3255
- package/dist/migrations/0057_rename_sync_tables.sql +57 -0
- package/dist/migrations/0058_improve_sync_runs_status.sql +36 -0
- package/dist/supabase/index.cjs +46 -21
- package/dist/supabase/index.d.cts +6 -1
- package/dist/supabase/index.d.ts +6 -1
- package/dist/supabase/index.js +12 -382
- package/package.json +18 -6
|
@@ -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;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Improve sync_runs view status logic
|
|
2
|
+
-- More granular status based on actual object run states
|
|
3
|
+
|
|
4
|
+
DROP VIEW IF EXISTS "stripe"."sync_runs";
|
|
5
|
+
|
|
6
|
+
CREATE VIEW "stripe"."sync_runs" AS
|
|
7
|
+
SELECT
|
|
8
|
+
r._account_id as account_id,
|
|
9
|
+
r.started_at,
|
|
10
|
+
r.closed_at,
|
|
11
|
+
r.triggered_by,
|
|
12
|
+
r.max_concurrent,
|
|
13
|
+
-- Aggregate metrics from child objects
|
|
14
|
+
COALESCE(SUM(o.processed_count), 0) as total_processed,
|
|
15
|
+
COUNT(o.*) as total_objects,
|
|
16
|
+
COUNT(*) FILTER (WHERE o.status = 'complete') as complete_count,
|
|
17
|
+
COUNT(*) FILTER (WHERE o.status = 'error') as error_count,
|
|
18
|
+
COUNT(*) FILTER (WHERE o.status = 'running') as running_count,
|
|
19
|
+
COUNT(*) FILTER (WHERE o.status = 'pending') as pending_count,
|
|
20
|
+
-- Collect error messages if any
|
|
21
|
+
STRING_AGG(o.error_message, '; ') FILTER (WHERE o.error_message IS NOT NULL) as error_message,
|
|
22
|
+
-- Derive overall status from run state and object states
|
|
23
|
+
CASE
|
|
24
|
+
-- Run still open (closed_at IS NULL)
|
|
25
|
+
WHEN r.closed_at IS NULL AND COUNT(*) FILTER (WHERE o.status = 'running') > 0 THEN 'running'
|
|
26
|
+
WHEN r.closed_at IS NULL AND (COUNT(o.*) = 0 OR COUNT(o.*) = COUNT(*) FILTER (WHERE o.status = 'pending')) THEN 'pending'
|
|
27
|
+
WHEN r.closed_at IS NULL THEN 'running'
|
|
28
|
+
-- Run closed (closed_at IS NOT NULL)
|
|
29
|
+
WHEN COUNT(*) FILTER (WHERE o.status = 'error') > 0 THEN 'error'
|
|
30
|
+
ELSE 'complete'
|
|
31
|
+
END as status
|
|
32
|
+
FROM "stripe"."_sync_runs" r
|
|
33
|
+
LEFT JOIN "stripe"."_sync_obj_runs" o
|
|
34
|
+
ON o._account_id = r._account_id
|
|
35
|
+
AND o.run_started_at = r.started_at
|
|
36
|
+
GROUP BY r._account_id, r.started_at, r.closed_at, r.triggered_by, r.max_concurrent;
|
package/dist/supabase/index.cjs
CHANGED
|
@@ -47,14 +47,14 @@ module.exports = __toCommonJS(supabase_exports);
|
|
|
47
47
|
// src/supabase/supabase.ts
|
|
48
48
|
var import_supabase_management_js = require("supabase-management-js");
|
|
49
49
|
|
|
50
|
-
// raw-ts:/
|
|
50
|
+
// raw-ts:/Users/lfdepombo/src/stripe-sync-engine/packages/sync-engine/src/supabase/edge-functions/stripe-setup.ts
|
|
51
51
|
var stripe_setup_default = "import { StripeSync, runMigrations } 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 authHeader = req.headers.get('Authorization')\n if (!authHeader?.startsWith('Bearer ')) {\n return new Response('Unauthorized', { status: 401 })\n }\n\n let stripeSync = null\n try {\n // Get and validate database URL\n const rawDbUrl = Deno.env.get('SUPABASE_DB_URL')\n if (!rawDbUrl) {\n throw new Error('SUPABASE_DB_URL environment variable is not set')\n }\n // Remove sslmode from connection string (not supported by pg in Deno)\n const dbUrl = rawDbUrl.replace(/[?&]sslmode=[^&]*/g, '').replace(/[?&]$/, '')\n\n await runMigrations({ databaseUrl: dbUrl })\n\n stripeSync = new StripeSync({\n poolConfig: { connectionString: dbUrl, max: 2 }, // Need 2 for advisory lock + queries\n stripeSecretKey: Deno.env.get('STRIPE_SECRET_KEY'),\n })\n\n // Release any stale advisory locks from previous timeouts\n await stripeSync.postgresClient.query('SELECT pg_advisory_unlock_all()')\n\n // Construct webhook URL from SUPABASE_URL (available in all Edge Functions)\n const supabaseUrl = Deno.env.get('SUPABASE_URL')\n if (!supabaseUrl) {\n throw new Error('SUPABASE_URL environment variable is not set')\n }\n const webhookUrl = supabaseUrl + '/functions/v1/stripe-webhook'\n\n const webhook = await stripeSync.findOrCreateManagedWebhook(webhookUrl)\n\n await stripeSync.postgresClient.pool.end()\n\n return new Response(\n JSON.stringify({\n success: true,\n message: 'Setup complete',\n webhookId: webhook.id,\n }),\n {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n }\n )\n } catch (error) {\n console.error('Setup error:', error)\n // Cleanup on error\n if (stripeSync) {\n try {\n await stripeSync.postgresClient.query('SELECT pg_advisory_unlock_all()')\n await stripeSync.postgresClient.pool.end()\n } catch (cleanupErr) {\n console.warn('Cleanup failed:', cleanupErr)\n }\n }\n return new Response(JSON.stringify({ success: false, error: error.message }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n})\n";
|
|
52
52
|
|
|
53
|
-
// raw-ts:/
|
|
53
|
+
// raw-ts:/Users/lfdepombo/src/stripe-sync-engine/packages/sync-engine/src/supabase/edge-functions/stripe-webhook.ts
|
|
54
54
|
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";
|
|
55
55
|
|
|
56
|
-
// raw-ts:/
|
|
57
|
-
var stripe_worker_default = "/**\n * Stripe Sync Worker\n *\n * Triggered by pg_cron every 10 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 const objects = stripeSync.
|
|
56
|
+
// raw-ts:/Users/lfdepombo/src/stripe-sync-engine/packages/sync-engine/src/supabase/edge-functions/stripe-worker.ts
|
|
57
|
+
var stripe_worker_default = "/**\n * Stripe Sync Worker\n *\n * Triggered by pg_cron every 10 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";
|
|
58
58
|
|
|
59
59
|
// src/supabase/edge-function-code.ts
|
|
60
60
|
var setupFunctionCode = stripe_setup_default;
|
|
@@ -64,12 +64,12 @@ var workerFunctionCode = stripe_worker_default;
|
|
|
64
64
|
// package.json
|
|
65
65
|
var package_default = {
|
|
66
66
|
name: "stripe-experiment-sync",
|
|
67
|
-
version: "1.0.
|
|
67
|
+
version: "1.0.8-beta.1765856228",
|
|
68
68
|
private: false,
|
|
69
69
|
description: "Stripe Sync Engine to sync Stripe data to Postgres",
|
|
70
70
|
type: "module",
|
|
71
71
|
main: "./dist/index.cjs",
|
|
72
|
-
bin: "./dist/cli/index.
|
|
72
|
+
bin: "./dist/cli/index.js",
|
|
73
73
|
exports: {
|
|
74
74
|
".": {
|
|
75
75
|
types: "./dist/index.d.ts",
|
|
@@ -80,12 +80,17 @@ var package_default = {
|
|
|
80
80
|
types: "./dist/supabase/index.d.ts",
|
|
81
81
|
import: "./dist/supabase/index.js",
|
|
82
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"
|
|
83
88
|
}
|
|
84
89
|
},
|
|
85
90
|
scripts: {
|
|
86
91
|
clean: "rimraf dist",
|
|
87
92
|
prebuild: "npm run clean",
|
|
88
|
-
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",
|
|
89
94
|
lint: "eslint src --ext .ts",
|
|
90
95
|
test: "vitest"
|
|
91
96
|
},
|
|
@@ -93,21 +98,28 @@ var package_default = {
|
|
|
93
98
|
"dist"
|
|
94
99
|
],
|
|
95
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",
|
|
96
107
|
pg: "^8.16.3",
|
|
97
108
|
"pg-node-migrations": "0.0.8",
|
|
109
|
+
stripe: "^17.7.0",
|
|
98
110
|
"supabase-management-js": "^0.1.6",
|
|
99
111
|
ws: "^8.18.0",
|
|
100
112
|
yesql: "^7.0.0"
|
|
101
113
|
},
|
|
102
|
-
peerDependencies: {
|
|
103
|
-
stripe: "> 11"
|
|
104
|
-
},
|
|
105
114
|
devDependencies: {
|
|
115
|
+
"@types/express": "^4.17.21",
|
|
116
|
+
"@types/inquirer": "^9.0.7",
|
|
106
117
|
"@types/node": "^24.10.1",
|
|
107
118
|
"@types/pg": "^8.15.5",
|
|
108
119
|
"@types/ws": "^8.5.13",
|
|
109
120
|
"@types/yesql": "^4.1.4",
|
|
110
121
|
"@vitest/ui": "^4.0.9",
|
|
122
|
+
tsx: "^4.19.2",
|
|
111
123
|
vitest: "^3.2.4"
|
|
112
124
|
},
|
|
113
125
|
repository: {
|
|
@@ -392,7 +404,7 @@ var SupabaseSetupClient = class {
|
|
|
392
404
|
* Removes all Edge Functions, secrets, database resources, and Stripe webhooks
|
|
393
405
|
*/
|
|
394
406
|
async uninstall(stripeSecretKey) {
|
|
395
|
-
const stripe = new import_stripe.default(stripeSecretKey, { apiVersion: "2025-
|
|
407
|
+
const stripe = new import_stripe.default(stripeSecretKey, { apiVersion: "2025-02-24.acacia" });
|
|
396
408
|
try {
|
|
397
409
|
try {
|
|
398
410
|
const webhookResult = await this.runSQL(`
|
|
@@ -436,30 +448,43 @@ var SupabaseSetupClient = class {
|
|
|
436
448
|
throw new Error(`Uninstall failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
437
449
|
}
|
|
438
450
|
}
|
|
439
|
-
|
|
451
|
+
/**
|
|
452
|
+
* Inject package version into Edge Function code
|
|
453
|
+
*/
|
|
454
|
+
injectPackageVersion(code, version) {
|
|
455
|
+
if (version === "latest") {
|
|
456
|
+
return code;
|
|
457
|
+
}
|
|
458
|
+
return code.replace(
|
|
459
|
+
/from ['"]npm:stripe-experiment-sync['"]/g,
|
|
460
|
+
`from 'npm:stripe-experiment-sync@${version}'`
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
async install(stripeKey, packageVersion) {
|
|
440
464
|
const trimmedStripeKey = stripeKey.trim();
|
|
441
465
|
if (!trimmedStripeKey.startsWith("sk_") && !trimmedStripeKey.startsWith("rk_")) {
|
|
442
466
|
throw new Error('Stripe key should start with "sk_" or "rk_"');
|
|
443
467
|
}
|
|
468
|
+
const version = packageVersion || "latest";
|
|
444
469
|
try {
|
|
445
470
|
await this.validateProject();
|
|
446
471
|
await this.runSQL(`CREATE SCHEMA IF NOT EXISTS stripe`);
|
|
447
472
|
await this.updateInstallationComment(
|
|
448
473
|
`${STRIPE_SCHEMA_COMMENT_PREFIX} v${package_default.version} ${INSTALLATION_STARTED_SUFFIX}`
|
|
449
474
|
);
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
475
|
+
const versionedSetup = this.injectPackageVersion(setupFunctionCode, version);
|
|
476
|
+
const versionedWebhook = this.injectPackageVersion(webhookFunctionCode, version);
|
|
477
|
+
const versionedWorker = this.injectPackageVersion(workerFunctionCode, version);
|
|
478
|
+
await this.deployFunction("stripe-setup", versionedSetup);
|
|
479
|
+
await this.deployFunction("stripe-webhook", versionedWebhook);
|
|
480
|
+
await this.deployFunction("stripe-worker", versionedWorker);
|
|
453
481
|
await this.setSecrets([{ name: "STRIPE_SECRET_KEY", value: trimmedStripeKey }]);
|
|
454
482
|
const serviceRoleKey = await this.getServiceRoleKey();
|
|
455
483
|
const setupResult = await this.invokeFunction("stripe-setup", serviceRoleKey);
|
|
456
484
|
if (!setupResult.success) {
|
|
457
485
|
throw new Error(`Setup failed: ${setupResult.error}`);
|
|
458
486
|
}
|
|
459
|
-
|
|
460
|
-
await this.setupPgCronJob();
|
|
461
|
-
} catch {
|
|
462
|
-
}
|
|
487
|
+
await this.setupPgCronJob();
|
|
463
488
|
await this.updateInstallationComment(
|
|
464
489
|
`${STRIPE_SCHEMA_COMMENT_PREFIX} v${package_default.version} ${INSTALLATION_INSTALLED_SUFFIX}`
|
|
465
490
|
);
|
|
@@ -472,14 +497,14 @@ var SupabaseSetupClient = class {
|
|
|
472
497
|
}
|
|
473
498
|
};
|
|
474
499
|
async function install(params) {
|
|
475
|
-
const { supabaseAccessToken, supabaseProjectRef, stripeKey } = params;
|
|
500
|
+
const { supabaseAccessToken, supabaseProjectRef, stripeKey, packageVersion } = params;
|
|
476
501
|
const client = new SupabaseSetupClient({
|
|
477
502
|
accessToken: supabaseAccessToken,
|
|
478
503
|
projectRef: supabaseProjectRef,
|
|
479
504
|
projectBaseUrl: params.baseProjectUrl,
|
|
480
505
|
managementApiBaseUrl: params.baseManagementApiUrl
|
|
481
506
|
});
|
|
482
|
-
await client.install(stripeKey);
|
|
507
|
+
await client.install(stripeKey, packageVersion);
|
|
483
508
|
}
|
|
484
509
|
async function uninstall(params) {
|
|
485
510
|
const { supabaseAccessToken, supabaseProjectRef, stripeKey } = params;
|
|
@@ -93,12 +93,17 @@ declare class SupabaseSetupClient {
|
|
|
93
93
|
* Removes all Edge Functions, secrets, database resources, and Stripe webhooks
|
|
94
94
|
*/
|
|
95
95
|
uninstall(stripeSecretKey: string): Promise<void>;
|
|
96
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Inject package version into Edge Function code
|
|
98
|
+
*/
|
|
99
|
+
private injectPackageVersion;
|
|
100
|
+
install(stripeKey: string, packageVersion?: string): Promise<void>;
|
|
97
101
|
}
|
|
98
102
|
declare function install(params: {
|
|
99
103
|
supabaseAccessToken: string;
|
|
100
104
|
supabaseProjectRef: string;
|
|
101
105
|
stripeKey: string;
|
|
106
|
+
packageVersion?: string;
|
|
102
107
|
baseProjectUrl?: string;
|
|
103
108
|
baseManagementApiUrl?: string;
|
|
104
109
|
}): Promise<void>;
|
package/dist/supabase/index.d.ts
CHANGED
|
@@ -93,12 +93,17 @@ declare class SupabaseSetupClient {
|
|
|
93
93
|
* Removes all Edge Functions, secrets, database resources, and Stripe webhooks
|
|
94
94
|
*/
|
|
95
95
|
uninstall(stripeSecretKey: string): Promise<void>;
|
|
96
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Inject package version into Edge Function code
|
|
98
|
+
*/
|
|
99
|
+
private injectPackageVersion;
|
|
100
|
+
install(stripeKey: string, packageVersion?: string): Promise<void>;
|
|
97
101
|
}
|
|
98
102
|
declare function install(params: {
|
|
99
103
|
supabaseAccessToken: string;
|
|
100
104
|
supabaseProjectRef: string;
|
|
101
105
|
stripeKey: string;
|
|
106
|
+
packageVersion?: string;
|
|
102
107
|
baseProjectUrl?: string;
|
|
103
108
|
baseManagementApiUrl?: string;
|
|
104
109
|
}): Promise<void>;
|