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.
- package/README.md +25 -4
- package/dist/chunk-3P5TZKWU.js +595 -0
- package/dist/chunk-7JWRDXNB.js +3264 -0
- package/dist/chunk-SX3HLE4H.js +406 -0
- package/dist/{chunk-SNGEJHKN.js → chunk-YXQZXR7S.js} +18 -6
- package/dist/cli/index.cjs +4376 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +55 -0
- package/dist/cli/lib.cjs +4359 -0
- package/dist/cli/lib.d.cts +70 -0
- package/dist/cli/lib.d.ts +70 -0
- package/dist/cli/lib.js +21 -0
- package/dist/index.cjs +48 -36
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -3255
- package/dist/migrations/0057_rename_sync_tables.sql +57 -0
- package/dist/supabase/index.cjs +98 -50
- package/dist/supabase/index.d.cts +12 -4
- package/dist/supabase/index.d.ts +12 -4
- package/dist/supabase/index.js +18 -367
- package/package.json +18 -6
package/dist/supabase/index.js
CHANGED
|
@@ -1,372 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// raw-ts:/home/runner/work/stripe-sync-engine/stripe-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 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.getSupportedSyncObjects()\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
|
-
|
|
17
|
-
// src/supabase/edge-function-code.ts
|
|
18
|
-
var setupFunctionCode = stripe_setup_default;
|
|
19
|
-
var webhookFunctionCode = stripe_webhook_default;
|
|
20
|
-
var workerFunctionCode = stripe_worker_default;
|
|
21
|
-
|
|
22
|
-
// src/supabase/supabase.ts
|
|
23
|
-
import Stripe from "stripe";
|
|
24
|
-
var SupabaseDeployClient = class {
|
|
25
|
-
api;
|
|
26
|
-
projectRef;
|
|
27
|
-
baseUrl;
|
|
28
|
-
constructor(options) {
|
|
29
|
-
this.api = new SupabaseManagementAPI({ accessToken: options.accessToken });
|
|
30
|
-
this.projectRef = options.projectRef;
|
|
31
|
-
this.baseUrl = options.baseUrl || process.env.SUPABASE_BASE_URL || "supabase.co";
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Validate that the project exists and we have access
|
|
35
|
-
*/
|
|
36
|
-
async validateProject() {
|
|
37
|
-
const projects = await this.api.getProjects();
|
|
38
|
-
const project = projects?.find((p) => p.id === this.projectRef);
|
|
39
|
-
if (!project) {
|
|
40
|
-
throw new Error(`Project ${this.projectRef} not found or you don't have access`);
|
|
41
|
-
}
|
|
42
|
-
return {
|
|
43
|
-
id: project.id,
|
|
44
|
-
name: project.name,
|
|
45
|
-
region: project.region
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Deploy an Edge Function
|
|
50
|
-
*/
|
|
51
|
-
async deployFunction(name, code) {
|
|
52
|
-
const functions = await this.api.listFunctions(this.projectRef);
|
|
53
|
-
const exists = functions?.some((f) => f.slug === name);
|
|
54
|
-
if (exists) {
|
|
55
|
-
await this.api.updateFunction(this.projectRef, name, {
|
|
56
|
-
body: code,
|
|
57
|
-
verify_jwt: false
|
|
58
|
-
});
|
|
59
|
-
} else {
|
|
60
|
-
await this.api.createFunction(this.projectRef, {
|
|
61
|
-
slug: name,
|
|
62
|
-
name,
|
|
63
|
-
body: code,
|
|
64
|
-
verify_jwt: false
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Set secrets for Edge Functions
|
|
70
|
-
*/
|
|
71
|
-
async setSecrets(secrets) {
|
|
72
|
-
await this.api.createSecrets(this.projectRef, secrets);
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Run SQL against the database
|
|
76
|
-
*/
|
|
77
|
-
async runSQL(sql) {
|
|
78
|
-
return await this.api.runQuery(this.projectRef, sql);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Setup pg_cron job to invoke worker function
|
|
82
|
-
*/
|
|
83
|
-
async setupPgCronJob() {
|
|
84
|
-
const serviceRoleKey = await this.getServiceRoleKey();
|
|
85
|
-
const escapedServiceRoleKey = serviceRoleKey.replace(/'/g, "''");
|
|
86
|
-
const sql = `
|
|
87
|
-
-- Enable extensions
|
|
88
|
-
CREATE EXTENSION IF NOT EXISTS pg_cron;
|
|
89
|
-
CREATE EXTENSION IF NOT EXISTS pg_net;
|
|
90
|
-
CREATE EXTENSION IF NOT EXISTS pgmq;
|
|
91
|
-
|
|
92
|
-
-- Create pgmq queue for sync work (idempotent)
|
|
93
|
-
SELECT pgmq.create('stripe_sync_work')
|
|
94
|
-
WHERE NOT EXISTS (
|
|
95
|
-
SELECT 1 FROM pgmq.list_queues() WHERE queue_name = 'stripe_sync_work'
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
-- Store service role key in vault for pg_cron to use
|
|
99
|
-
-- Delete existing secret if it exists, then create new one
|
|
100
|
-
DELETE FROM vault.secrets WHERE name = 'stripe_sync_service_role_key';
|
|
101
|
-
SELECT vault.create_secret('${escapedServiceRoleKey}', 'stripe_sync_service_role_key');
|
|
102
|
-
|
|
103
|
-
-- Delete existing jobs if they exist
|
|
104
|
-
SELECT cron.unschedule('stripe-sync-worker') WHERE EXISTS (
|
|
105
|
-
SELECT 1 FROM cron.job WHERE jobname = 'stripe-sync-worker'
|
|
106
|
-
);
|
|
107
|
-
SELECT cron.unschedule('stripe-sync-scheduler') WHERE EXISTS (
|
|
108
|
-
SELECT 1 FROM cron.job WHERE jobname = 'stripe-sync-scheduler'
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
-- Create job to invoke worker every 10 seconds
|
|
112
|
-
-- Worker reads from pgmq, enqueues objects if empty, and processes sync work
|
|
113
|
-
SELECT cron.schedule(
|
|
114
|
-
'stripe-sync-worker',
|
|
115
|
-
'10 seconds',
|
|
116
|
-
$$
|
|
117
|
-
SELECT net.http_post(
|
|
118
|
-
url := 'https://${this.projectRef}.${this.baseUrl}/functions/v1/stripe-worker',
|
|
119
|
-
headers := jsonb_build_object(
|
|
120
|
-
'Authorization', 'Bearer ' || (SELECT decrypted_secret FROM vault.decrypted_secrets WHERE name = 'stripe_sync_service_role_key')
|
|
121
|
-
)
|
|
122
|
-
)
|
|
123
|
-
$$
|
|
124
|
-
);
|
|
125
|
-
`;
|
|
126
|
-
await this.runSQL(sql);
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Get the webhook URL for this project
|
|
130
|
-
*/
|
|
131
|
-
getWebhookUrl() {
|
|
132
|
-
return `https://${this.projectRef}.${this.baseUrl}/functions/v1/stripe-webhook`;
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Get the service role key for this project (needed to invoke Edge Functions)
|
|
136
|
-
*/
|
|
137
|
-
async getServiceRoleKey() {
|
|
138
|
-
const apiKeys = await this.api.getProjectApiKeys(this.projectRef);
|
|
139
|
-
const serviceRoleKey = apiKeys?.find((k) => k.name === "service_role");
|
|
140
|
-
if (!serviceRoleKey) {
|
|
141
|
-
throw new Error("Could not find service_role API key");
|
|
142
|
-
}
|
|
143
|
-
return serviceRoleKey.api_key;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Get the anon key for this project (needed for Realtime subscriptions)
|
|
147
|
-
*/
|
|
148
|
-
async getAnonKey() {
|
|
149
|
-
const apiKeys = await this.api.getProjectApiKeys(this.projectRef);
|
|
150
|
-
const anonKey = apiKeys?.find((k) => k.name === "anon");
|
|
151
|
-
if (!anonKey) {
|
|
152
|
-
throw new Error("Could not find anon API key");
|
|
153
|
-
}
|
|
154
|
-
return anonKey.api_key;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Get the project URL
|
|
158
|
-
*/
|
|
159
|
-
getProjectUrl() {
|
|
160
|
-
return `https://${this.projectRef}.${this.baseUrl}`;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Invoke an Edge Function
|
|
164
|
-
*/
|
|
165
|
-
async invokeFunction(name, serviceRoleKey) {
|
|
166
|
-
const url = `https://${this.projectRef}.${this.baseUrl}/functions/v1/${name}`;
|
|
167
|
-
const response = await fetch(url, {
|
|
168
|
-
method: "POST",
|
|
169
|
-
headers: {
|
|
170
|
-
Authorization: `Bearer ${serviceRoleKey}`,
|
|
171
|
-
"Content-Type": "application/json"
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
if (!response.ok) {
|
|
175
|
-
const text = await response.text();
|
|
176
|
-
return { success: false, error: `${response.status}: ${text}` };
|
|
177
|
-
}
|
|
178
|
-
const result = await response.json();
|
|
179
|
-
if (result.success === false) {
|
|
180
|
-
return { success: false, error: result.error };
|
|
181
|
-
}
|
|
182
|
-
return { success: true };
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Check if stripe-sync is installed in the database.
|
|
186
|
-
*
|
|
187
|
-
* Uses the Supabase Management API to run SQL queries.
|
|
188
|
-
* Uses duck typing (schema + migrations table) combined with comment validation.
|
|
189
|
-
* Throws error for legacy installations to prevent accidental corruption.
|
|
190
|
-
*
|
|
191
|
-
* @param schema The schema name to check (defaults to 'stripe')
|
|
192
|
-
* @returns true if properly installed with comment marker, false if not installed
|
|
193
|
-
* @throws Error if legacy installation detected (schema exists without comment)
|
|
194
|
-
*/
|
|
195
|
-
async isInstalled(schema = "stripe") {
|
|
196
|
-
try {
|
|
197
|
-
const schemaCheck = await this.runSQL(
|
|
198
|
-
`SELECT EXISTS (
|
|
199
|
-
SELECT 1 FROM information_schema.schemata
|
|
200
|
-
WHERE schema_name = '${schema}'
|
|
201
|
-
) as schema_exists`
|
|
202
|
-
);
|
|
203
|
-
const schemaExists = schemaCheck[0]?.rows?.[0]?.schema_exists === true;
|
|
204
|
-
if (!schemaExists) {
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
const migrationsCheck = await this.runSQL(
|
|
208
|
-
`SELECT EXISTS (
|
|
209
|
-
SELECT 1 FROM information_schema.tables
|
|
210
|
-
WHERE table_schema = '${schema}' AND table_name IN ('migrations', '_migrations')
|
|
211
|
-
) as table_exists`
|
|
212
|
-
);
|
|
213
|
-
const migrationsTableExists = migrationsCheck[0]?.rows?.[0]?.table_exists === true;
|
|
214
|
-
if (!migrationsTableExists) {
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
const commentCheck = await this.runSQL(
|
|
218
|
-
`SELECT obj_description(oid, 'pg_namespace') as comment
|
|
219
|
-
FROM pg_namespace
|
|
220
|
-
WHERE nspname = '${schema}'`
|
|
221
|
-
);
|
|
222
|
-
const comment = commentCheck[0]?.rows?.[0]?.comment;
|
|
223
|
-
if (!comment || !comment.includes("stripe-sync")) {
|
|
224
|
-
throw new Error(
|
|
225
|
-
`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.`
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
if (comment.includes("installation:started")) {
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
if (comment.includes("installation:error")) {
|
|
232
|
-
throw new Error(
|
|
233
|
-
`Installation failed: Schema '${schema}' exists but installation encountered an error. Comment: ${comment}. Please uninstall and install again.`
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
return true;
|
|
237
|
-
} catch (error) {
|
|
238
|
-
if (error instanceof Error && (error.message.includes("Legacy installation detected") || error.message.includes("Installation failed"))) {
|
|
239
|
-
throw error;
|
|
240
|
-
}
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Update installation progress comment on the stripe schema
|
|
246
|
-
*/
|
|
247
|
-
async updateInstallationComment(message) {
|
|
248
|
-
const escapedMessage = message.replace(/'/g, "''");
|
|
249
|
-
await this.runSQL(`COMMENT ON SCHEMA stripe IS '${escapedMessage}'`);
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Delete an Edge Function
|
|
253
|
-
*/
|
|
254
|
-
async deleteFunction(name) {
|
|
255
|
-
try {
|
|
256
|
-
await this.api.deleteFunction(this.projectRef, name);
|
|
257
|
-
} catch (err) {
|
|
258
|
-
console.warn(`Could not delete function ${name}:`, err);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Delete a secret
|
|
263
|
-
*/
|
|
264
|
-
async deleteSecret(name) {
|
|
265
|
-
try {
|
|
266
|
-
await this.api.deleteSecrets(this.projectRef, [name]);
|
|
267
|
-
} catch (err) {
|
|
268
|
-
console.warn(`Could not delete secret ${name}:`, err);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Uninstall stripe-sync from a Supabase project
|
|
273
|
-
* Removes all Edge Functions, secrets, database resources, and Stripe webhooks
|
|
274
|
-
*/
|
|
275
|
-
async uninstall(stripeSecretKey) {
|
|
276
|
-
const stripe = new Stripe(stripeSecretKey, { apiVersion: "2025-05-28.basil" });
|
|
277
|
-
try {
|
|
278
|
-
try {
|
|
279
|
-
const webhookResult = await this.runSQL(`
|
|
280
|
-
SELECT id FROM stripe._managed_webhooks WHERE id IS NOT NULL
|
|
281
|
-
`);
|
|
282
|
-
const webhookIds = webhookResult[0]?.rows?.map((r) => r.id) || [];
|
|
283
|
-
for (const webhookId of webhookIds) {
|
|
284
|
-
try {
|
|
285
|
-
await stripe.webhookEndpoints.del(webhookId);
|
|
286
|
-
} catch (err) {
|
|
287
|
-
console.warn(`Could not delete Stripe webhook ${webhookId}:`, err);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
} catch (err) {
|
|
291
|
-
console.warn("Could not query/delete webhooks:", err);
|
|
292
|
-
}
|
|
293
|
-
await this.deleteFunction("stripe-setup");
|
|
294
|
-
await this.deleteFunction("stripe-webhook");
|
|
295
|
-
await this.deleteFunction("stripe-worker");
|
|
296
|
-
await this.deleteSecret("STRIPE_SECRET_KEY");
|
|
297
|
-
try {
|
|
298
|
-
await this.runSQL(`
|
|
299
|
-
SELECT cron.unschedule('stripe-sync-worker')
|
|
300
|
-
WHERE EXISTS (
|
|
301
|
-
SELECT 1 FROM cron.job WHERE jobname = 'stripe-sync-worker'
|
|
302
|
-
)
|
|
303
|
-
`);
|
|
304
|
-
} catch (err) {
|
|
305
|
-
console.warn("Could not unschedule pg_cron job:", err);
|
|
306
|
-
}
|
|
307
|
-
try {
|
|
308
|
-
await this.runSQL(`
|
|
309
|
-
DELETE FROM vault.secrets
|
|
310
|
-
WHERE name = 'stripe_sync_service_role_key'
|
|
311
|
-
`);
|
|
312
|
-
} catch (err) {
|
|
313
|
-
console.warn("Could not delete vault secret:", err);
|
|
314
|
-
}
|
|
315
|
-
await this.runSQL(`DROP SCHEMA IF EXISTS stripe CASCADE`);
|
|
316
|
-
} catch (error) {
|
|
317
|
-
throw new Error(`Uninstall failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
async function install(params) {
|
|
322
|
-
const { supabaseAccessToken, supabaseProjectRef, stripeKey } = params;
|
|
323
|
-
const trimmedStripeKey = stripeKey.trim();
|
|
324
|
-
if (!trimmedStripeKey.startsWith("sk_") && !trimmedStripeKey.startsWith("rk_")) {
|
|
325
|
-
throw new Error('Stripe key should start with "sk_" or "rk_"');
|
|
326
|
-
}
|
|
327
|
-
const client = new SupabaseDeployClient({
|
|
328
|
-
accessToken: supabaseAccessToken,
|
|
329
|
-
projectRef: supabaseProjectRef
|
|
330
|
-
});
|
|
331
|
-
try {
|
|
332
|
-
await client.validateProject();
|
|
333
|
-
await client.runSQL(`CREATE SCHEMA IF NOT EXISTS stripe`);
|
|
334
|
-
await client.updateInstallationComment(`stripe-sync v${package_default.version} installation:started`);
|
|
335
|
-
await client.deployFunction("stripe-setup", setupFunctionCode);
|
|
336
|
-
await client.deployFunction("stripe-webhook", webhookFunctionCode);
|
|
337
|
-
await client.deployFunction("stripe-worker", workerFunctionCode);
|
|
338
|
-
await client.setSecrets([{ name: "STRIPE_SECRET_KEY", value: trimmedStripeKey }]);
|
|
339
|
-
const serviceRoleKey = await client.getServiceRoleKey();
|
|
340
|
-
const setupResult = await client.invokeFunction("stripe-setup", serviceRoleKey);
|
|
341
|
-
if (!setupResult.success) {
|
|
342
|
-
throw new Error(`Setup failed: ${setupResult.error}`);
|
|
343
|
-
}
|
|
344
|
-
try {
|
|
345
|
-
await client.setupPgCronJob();
|
|
346
|
-
} catch {
|
|
347
|
-
}
|
|
348
|
-
await client.updateInstallationComment(`stripe-sync v${package_default.version} installed`);
|
|
349
|
-
} catch (error) {
|
|
350
|
-
await client.updateInstallationComment(
|
|
351
|
-
`stripe-sync v${package_default.version} installation:error - ${error instanceof Error ? error.message : String(error)}`
|
|
352
|
-
);
|
|
353
|
-
throw error;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
async function uninstall(params) {
|
|
357
|
-
const { supabaseAccessToken, supabaseProjectRef, stripeKey } = params;
|
|
358
|
-
const trimmedStripeKey = stripeKey.trim();
|
|
359
|
-
if (!trimmedStripeKey.startsWith("sk_") && !trimmedStripeKey.startsWith("rk_")) {
|
|
360
|
-
throw new Error('Stripe key should start with "sk_" or "rk_"');
|
|
361
|
-
}
|
|
362
|
-
const client = new SupabaseDeployClient({
|
|
363
|
-
accessToken: supabaseAccessToken,
|
|
364
|
-
projectRef: supabaseProjectRef
|
|
365
|
-
});
|
|
366
|
-
await client.uninstall(trimmedStripeKey);
|
|
367
|
-
}
|
|
2
|
+
INSTALLATION_ERROR_SUFFIX,
|
|
3
|
+
INSTALLATION_INSTALLED_SUFFIX,
|
|
4
|
+
INSTALLATION_STARTED_SUFFIX,
|
|
5
|
+
STRIPE_SCHEMA_COMMENT_PREFIX,
|
|
6
|
+
SupabaseSetupClient,
|
|
7
|
+
install,
|
|
8
|
+
setupFunctionCode,
|
|
9
|
+
uninstall,
|
|
10
|
+
webhookFunctionCode,
|
|
11
|
+
workerFunctionCode
|
|
12
|
+
} from "../chunk-SX3HLE4H.js";
|
|
13
|
+
import "../chunk-YXQZXR7S.js";
|
|
368
14
|
export {
|
|
369
|
-
|
|
15
|
+
INSTALLATION_ERROR_SUFFIX,
|
|
16
|
+
INSTALLATION_INSTALLED_SUFFIX,
|
|
17
|
+
INSTALLATION_STARTED_SUFFIX,
|
|
18
|
+
STRIPE_SCHEMA_COMMENT_PREFIX,
|
|
19
|
+
SupabaseSetupClient as SupabaseDeployClient,
|
|
20
|
+
SupabaseSetupClient,
|
|
370
21
|
install,
|
|
371
22
|
setupFunctionCode,
|
|
372
23
|
uninstall,
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stripe-experiment-sync",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Stripe Sync Engine to sync Stripe data to Postgres",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.cjs",
|
|
8
|
-
"bin": "./dist/cli/index.
|
|
8
|
+
"bin": "./dist/cli/index.js",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
@@ -16,12 +16,17 @@
|
|
|
16
16
|
"types": "./dist/supabase/index.d.ts",
|
|
17
17
|
"import": "./dist/supabase/index.js",
|
|
18
18
|
"require": "./dist/supabase/index.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./cli": {
|
|
21
|
+
"types": "./dist/cli/lib.d.ts",
|
|
22
|
+
"import": "./dist/cli/lib.js",
|
|
23
|
+
"require": "./dist/cli/lib.cjs"
|
|
19
24
|
}
|
|
20
25
|
},
|
|
21
26
|
"scripts": {
|
|
22
27
|
"clean": "rimraf dist",
|
|
23
28
|
"prebuild": "npm run clean",
|
|
24
|
-
"build": "tsup src/index.ts src/supabase/index.ts --format esm,cjs --dts --shims && cp -r src/database/migrations dist/migrations",
|
|
29
|
+
"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",
|
|
25
30
|
"lint": "eslint src --ext .ts",
|
|
26
31
|
"test": "vitest"
|
|
27
32
|
},
|
|
@@ -29,21 +34,28 @@
|
|
|
29
34
|
"dist"
|
|
30
35
|
],
|
|
31
36
|
"dependencies": {
|
|
37
|
+
"@ngrok/ngrok": "^1.4.1",
|
|
38
|
+
"chalk": "^5.3.0",
|
|
39
|
+
"commander": "^12.1.0",
|
|
40
|
+
"dotenv": "^16.4.7",
|
|
41
|
+
"express": "^4.18.2",
|
|
42
|
+
"inquirer": "^12.3.0",
|
|
32
43
|
"pg": "^8.16.3",
|
|
33
44
|
"pg-node-migrations": "0.0.8",
|
|
45
|
+
"stripe": "^17.7.0",
|
|
34
46
|
"supabase-management-js": "^0.1.6",
|
|
35
47
|
"ws": "^8.18.0",
|
|
36
48
|
"yesql": "^7.0.0"
|
|
37
49
|
},
|
|
38
|
-
"peerDependencies": {
|
|
39
|
-
"stripe": "> 11"
|
|
40
|
-
},
|
|
41
50
|
"devDependencies": {
|
|
51
|
+
"@types/express": "^4.17.21",
|
|
52
|
+
"@types/inquirer": "^9.0.7",
|
|
42
53
|
"@types/node": "^24.10.1",
|
|
43
54
|
"@types/pg": "^8.15.5",
|
|
44
55
|
"@types/ws": "^8.5.13",
|
|
45
56
|
"@types/yesql": "^4.1.4",
|
|
46
57
|
"@vitest/ui": "^4.0.9",
|
|
58
|
+
"tsx": "^4.19.2",
|
|
47
59
|
"vitest": "^3.2.4"
|
|
48
60
|
},
|
|
49
61
|
"repository": {
|