stripe-experiment-sync 1.0.7 → 1.0.8-beta.1765872477
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-SX3HLE4H.js → chunk-E2HSDYSR.js} +39 -25
- package/dist/{chunk-7JWRDXNB.js → chunk-E7LIOBPP.js} +195 -23
- package/dist/{chunk-YXQZXR7S.js → chunk-M3MYAMG2.js} +1 -1
- package/dist/{chunk-3P5TZKWU.js → chunk-XDVV7YHP.js} +102 -63
- package/dist/cli/index.cjs +333 -106
- package/dist/cli/index.js +10 -6
- package/dist/cli/lib.cjs +327 -104
- package/dist/cli/lib.d.cts +5 -6
- package/dist/cli/lib.d.ts +5 -6
- package/dist/cli/lib.js +4 -4
- package/dist/index.cjs +196 -23
- package/dist/index.d.cts +53 -1
- package/dist/index.d.ts +53 -1
- package/dist/index.js +4 -2
- package/dist/migrations/0058_improve_sync_runs_status.sql +36 -0
- package/dist/supabase/index.cjs +39 -25
- package/dist/supabase/index.d.cts +6 -1
- package/dist/supabase/index.d.ts +6 -1
- package/dist/supabase/index.js +2 -2
- package/package.json +1 -1
package/dist/cli/lib.cjs
CHANGED
|
@@ -77,20 +77,6 @@ async function loadConfig(options) {
|
|
|
77
77
|
}
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
|
-
if (!config.ngrokAuthToken) {
|
|
81
|
-
questions.push({
|
|
82
|
-
type: "password",
|
|
83
|
-
name: "ngrokAuthToken",
|
|
84
|
-
message: "Enter your ngrok auth token:",
|
|
85
|
-
mask: "*",
|
|
86
|
-
validate: (input) => {
|
|
87
|
-
if (!input || input.trim() === "") {
|
|
88
|
-
return "Ngrok auth token is required";
|
|
89
|
-
}
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
80
|
if (!config.databaseUrl) {
|
|
95
81
|
questions.push({
|
|
96
82
|
type: "password",
|
|
@@ -119,7 +105,7 @@ async function loadConfig(options) {
|
|
|
119
105
|
// package.json
|
|
120
106
|
var package_default = {
|
|
121
107
|
name: "stripe-experiment-sync",
|
|
122
|
-
version: "1.0.
|
|
108
|
+
version: "1.0.8-beta.1765872477",
|
|
123
109
|
private: false,
|
|
124
110
|
description: "Stripe Sync Engine to sync Stripe data to Postgres",
|
|
125
111
|
type: "module",
|
|
@@ -1803,19 +1789,8 @@ var StripeSync = class {
|
|
|
1803
1789
|
if (params?.runStartedAt) {
|
|
1804
1790
|
runStartedAt = params.runStartedAt;
|
|
1805
1791
|
} else {
|
|
1806
|
-
const runKey = await this.
|
|
1807
|
-
|
|
1808
|
-
params?.triggeredBy ?? "processNext"
|
|
1809
|
-
);
|
|
1810
|
-
if (!runKey) {
|
|
1811
|
-
const activeRun = await this.postgresClient.getActiveSyncRun(accountId);
|
|
1812
|
-
if (!activeRun) {
|
|
1813
|
-
throw new Error("Failed to get or create sync run");
|
|
1814
|
-
}
|
|
1815
|
-
runStartedAt = activeRun.runStartedAt;
|
|
1816
|
-
} else {
|
|
1817
|
-
runStartedAt = runKey.runStartedAt;
|
|
1818
|
-
}
|
|
1792
|
+
const { runKey } = await this.joinOrCreateSyncRun(params?.triggeredBy ?? "processNext");
|
|
1793
|
+
runStartedAt = runKey.runStartedAt;
|
|
1819
1794
|
}
|
|
1820
1795
|
await this.postgresClient.createObjectRuns(accountId, runStartedAt, [resourceName]);
|
|
1821
1796
|
const objRun = await this.postgresClient.getObjectRun(accountId, runStartedAt, resourceName);
|
|
@@ -1978,18 +1953,39 @@ var StripeSync = class {
|
|
|
1978
1953
|
}
|
|
1979
1954
|
return { synced: totalSynced };
|
|
1980
1955
|
}
|
|
1981
|
-
|
|
1982
|
-
|
|
1956
|
+
/**
|
|
1957
|
+
* Join existing sync run or create a new one.
|
|
1958
|
+
* Returns sync run key and list of supported objects to sync.
|
|
1959
|
+
*
|
|
1960
|
+
* Cooperative behavior: If a sync run already exists, joins it instead of failing.
|
|
1961
|
+
* This is used by workers and background processes that should cooperate.
|
|
1962
|
+
*
|
|
1963
|
+
* @param triggeredBy - What triggered this sync (for observability)
|
|
1964
|
+
* @returns Run key and list of objects to sync
|
|
1965
|
+
*/
|
|
1966
|
+
async joinOrCreateSyncRun(triggeredBy = "worker") {
|
|
1983
1967
|
await this.getCurrentAccount();
|
|
1984
1968
|
const accountId = await this.getAccountId();
|
|
1985
|
-
const
|
|
1986
|
-
if (!
|
|
1969
|
+
const result = await this.postgresClient.getOrCreateSyncRun(accountId, triggeredBy);
|
|
1970
|
+
if (!result) {
|
|
1987
1971
|
const activeRun = await this.postgresClient.getActiveSyncRun(accountId);
|
|
1988
1972
|
if (!activeRun) {
|
|
1989
1973
|
throw new Error("Failed to get or create sync run");
|
|
1990
1974
|
}
|
|
1991
|
-
return
|
|
1975
|
+
return {
|
|
1976
|
+
runKey: { accountId: activeRun.accountId, runStartedAt: activeRun.runStartedAt },
|
|
1977
|
+
objects: this.getSupportedSyncObjects()
|
|
1978
|
+
};
|
|
1992
1979
|
}
|
|
1980
|
+
const { accountId: runAccountId, runStartedAt } = result;
|
|
1981
|
+
return {
|
|
1982
|
+
runKey: { accountId: runAccountId, runStartedAt },
|
|
1983
|
+
objects: this.getSupportedSyncObjects()
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
async processUntilDone(params) {
|
|
1987
|
+
const { object } = params ?? { object: "all" };
|
|
1988
|
+
const { runKey } = await this.joinOrCreateSyncRun("processUntilDone");
|
|
1993
1989
|
return this.processUntilDoneWithRun(runKey.runStartedAt, object, params);
|
|
1994
1990
|
}
|
|
1995
1991
|
/**
|
|
@@ -3448,6 +3444,167 @@ async function runMigrations(config) {
|
|
|
3448
3444
|
}
|
|
3449
3445
|
}
|
|
3450
3446
|
|
|
3447
|
+
// src/websocket-client.ts
|
|
3448
|
+
var import_ws = __toESM(require("ws"), 1);
|
|
3449
|
+
var CLI_VERSION = "1.33.0";
|
|
3450
|
+
var PONG_WAIT = 10 * 1e3;
|
|
3451
|
+
var PING_PERIOD = PONG_WAIT * 2 / 10;
|
|
3452
|
+
function getClientUserAgent() {
|
|
3453
|
+
return JSON.stringify({
|
|
3454
|
+
name: "stripe-cli",
|
|
3455
|
+
version: CLI_VERSION,
|
|
3456
|
+
publisher: "stripe",
|
|
3457
|
+
os: process.platform
|
|
3458
|
+
});
|
|
3459
|
+
}
|
|
3460
|
+
async function createCliSession(stripeApiKey) {
|
|
3461
|
+
const params = new URLSearchParams();
|
|
3462
|
+
params.append("device_name", "stripe-sync-engine");
|
|
3463
|
+
params.append("websocket_features[]", "webhooks");
|
|
3464
|
+
const response = await fetch("https://api.stripe.com/v1/stripecli/sessions", {
|
|
3465
|
+
method: "POST",
|
|
3466
|
+
headers: {
|
|
3467
|
+
Authorization: `Bearer ${stripeApiKey}`,
|
|
3468
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
3469
|
+
"User-Agent": `Stripe/v1 stripe-cli/${CLI_VERSION}`,
|
|
3470
|
+
"X-Stripe-Client-User-Agent": getClientUserAgent()
|
|
3471
|
+
},
|
|
3472
|
+
body: params.toString()
|
|
3473
|
+
});
|
|
3474
|
+
if (!response.ok) {
|
|
3475
|
+
const error = await response.text();
|
|
3476
|
+
throw new Error(`Failed to create CLI session: ${error}`);
|
|
3477
|
+
}
|
|
3478
|
+
return await response.json();
|
|
3479
|
+
}
|
|
3480
|
+
async function createStripeWebSocketClient(options) {
|
|
3481
|
+
const { stripeApiKey, onEvent, onReady, onError, onClose } = options;
|
|
3482
|
+
const session = await createCliSession(stripeApiKey);
|
|
3483
|
+
let ws = null;
|
|
3484
|
+
let pingInterval = null;
|
|
3485
|
+
let connected = false;
|
|
3486
|
+
let shouldReconnect = true;
|
|
3487
|
+
function connect() {
|
|
3488
|
+
const wsUrl = `${session.websocket_url}?websocket_feature=${encodeURIComponent(session.websocket_authorized_feature)}`;
|
|
3489
|
+
ws = new import_ws.default(wsUrl, {
|
|
3490
|
+
headers: {
|
|
3491
|
+
"Accept-Encoding": "identity",
|
|
3492
|
+
"User-Agent": `Stripe/v1 stripe-cli/${CLI_VERSION}`,
|
|
3493
|
+
"X-Stripe-Client-User-Agent": getClientUserAgent(),
|
|
3494
|
+
"Websocket-Id": session.websocket_id
|
|
3495
|
+
}
|
|
3496
|
+
});
|
|
3497
|
+
ws.on("pong", () => {
|
|
3498
|
+
});
|
|
3499
|
+
ws.on("open", () => {
|
|
3500
|
+
connected = true;
|
|
3501
|
+
pingInterval = setInterval(() => {
|
|
3502
|
+
if (ws && ws.readyState === import_ws.default.OPEN) {
|
|
3503
|
+
ws.ping();
|
|
3504
|
+
}
|
|
3505
|
+
}, PING_PERIOD);
|
|
3506
|
+
if (onReady) {
|
|
3507
|
+
onReady(session.secret);
|
|
3508
|
+
}
|
|
3509
|
+
});
|
|
3510
|
+
ws.on("message", async (data) => {
|
|
3511
|
+
try {
|
|
3512
|
+
const message = JSON.parse(data.toString());
|
|
3513
|
+
const ack = {
|
|
3514
|
+
type: "event_ack",
|
|
3515
|
+
event_id: message.webhook_id,
|
|
3516
|
+
webhook_conversation_id: message.webhook_conversation_id,
|
|
3517
|
+
webhook_id: message.webhook_id
|
|
3518
|
+
};
|
|
3519
|
+
if (ws && ws.readyState === import_ws.default.OPEN) {
|
|
3520
|
+
ws.send(JSON.stringify(ack));
|
|
3521
|
+
}
|
|
3522
|
+
let response;
|
|
3523
|
+
try {
|
|
3524
|
+
const result = await onEvent(message);
|
|
3525
|
+
response = {
|
|
3526
|
+
type: "webhook_response",
|
|
3527
|
+
webhook_id: message.webhook_id,
|
|
3528
|
+
webhook_conversation_id: message.webhook_conversation_id,
|
|
3529
|
+
forward_url: "stripe-sync-engine",
|
|
3530
|
+
status: result?.status ?? 200,
|
|
3531
|
+
http_headers: {},
|
|
3532
|
+
body: JSON.stringify({
|
|
3533
|
+
event_type: result?.event_type,
|
|
3534
|
+
event_id: result?.event_id,
|
|
3535
|
+
database_url: result?.databaseUrl,
|
|
3536
|
+
error: result?.error
|
|
3537
|
+
}),
|
|
3538
|
+
request_headers: message.http_headers,
|
|
3539
|
+
request_body: message.event_payload,
|
|
3540
|
+
notification_id: message.webhook_id
|
|
3541
|
+
};
|
|
3542
|
+
} catch (err) {
|
|
3543
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
3544
|
+
response = {
|
|
3545
|
+
type: "webhook_response",
|
|
3546
|
+
webhook_id: message.webhook_id,
|
|
3547
|
+
webhook_conversation_id: message.webhook_conversation_id,
|
|
3548
|
+
forward_url: "stripe-sync-engine",
|
|
3549
|
+
status: 500,
|
|
3550
|
+
http_headers: {},
|
|
3551
|
+
body: JSON.stringify({ error: errorMessage }),
|
|
3552
|
+
request_headers: message.http_headers,
|
|
3553
|
+
request_body: message.event_payload,
|
|
3554
|
+
notification_id: message.webhook_id
|
|
3555
|
+
};
|
|
3556
|
+
if (onError) {
|
|
3557
|
+
onError(err instanceof Error ? err : new Error(errorMessage));
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
if (ws && ws.readyState === import_ws.default.OPEN) {
|
|
3561
|
+
ws.send(JSON.stringify(response));
|
|
3562
|
+
}
|
|
3563
|
+
} catch (err) {
|
|
3564
|
+
if (onError) {
|
|
3565
|
+
onError(err instanceof Error ? err : new Error(String(err)));
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
});
|
|
3569
|
+
ws.on("error", (error) => {
|
|
3570
|
+
if (onError) {
|
|
3571
|
+
onError(error);
|
|
3572
|
+
}
|
|
3573
|
+
});
|
|
3574
|
+
ws.on("close", (code, reason) => {
|
|
3575
|
+
connected = false;
|
|
3576
|
+
if (pingInterval) {
|
|
3577
|
+
clearInterval(pingInterval);
|
|
3578
|
+
pingInterval = null;
|
|
3579
|
+
}
|
|
3580
|
+
if (onClose) {
|
|
3581
|
+
onClose(code, reason.toString());
|
|
3582
|
+
}
|
|
3583
|
+
if (shouldReconnect) {
|
|
3584
|
+
const delay = (session.reconnect_delay || 5) * 1e3;
|
|
3585
|
+
setTimeout(() => {
|
|
3586
|
+
connect();
|
|
3587
|
+
}, delay);
|
|
3588
|
+
}
|
|
3589
|
+
});
|
|
3590
|
+
}
|
|
3591
|
+
connect();
|
|
3592
|
+
return {
|
|
3593
|
+
close: () => {
|
|
3594
|
+
shouldReconnect = false;
|
|
3595
|
+
if (pingInterval) {
|
|
3596
|
+
clearInterval(pingInterval);
|
|
3597
|
+
pingInterval = null;
|
|
3598
|
+
}
|
|
3599
|
+
if (ws) {
|
|
3600
|
+
ws.close(1e3, "Connection Done");
|
|
3601
|
+
ws = null;
|
|
3602
|
+
}
|
|
3603
|
+
},
|
|
3604
|
+
isConnected: () => connected
|
|
3605
|
+
};
|
|
3606
|
+
}
|
|
3607
|
+
|
|
3451
3608
|
// src/index.ts
|
|
3452
3609
|
var VERSION = package_default.version;
|
|
3453
3610
|
|
|
@@ -3487,14 +3644,14 @@ Creating ngrok tunnel for port ${port}...`));
|
|
|
3487
3644
|
// src/supabase/supabase.ts
|
|
3488
3645
|
var import_supabase_management_js = require("supabase-management-js");
|
|
3489
3646
|
|
|
3490
|
-
// raw-ts:/
|
|
3647
|
+
// raw-ts:/Users/lfdepombo/src/stripe-sync-engine/packages/sync-engine/src/supabase/edge-functions/stripe-setup.ts
|
|
3491
3648
|
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";
|
|
3492
3649
|
|
|
3493
|
-
// raw-ts:/
|
|
3650
|
+
// raw-ts:/Users/lfdepombo/src/stripe-sync-engine/packages/sync-engine/src/supabase/edge-functions/stripe-webhook.ts
|
|
3494
3651
|
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";
|
|
3495
3652
|
|
|
3496
|
-
// raw-ts:/
|
|
3497
|
-
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.
|
|
3653
|
+
// raw-ts:/Users/lfdepombo/src/stripe-sync-engine/packages/sync-engine/src/supabase/edge-functions/stripe-worker.ts
|
|
3654
|
+
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";
|
|
3498
3655
|
|
|
3499
3656
|
// src/supabase/edge-function-code.ts
|
|
3500
3657
|
var setupFunctionCode = stripe_setup_default;
|
|
@@ -3801,45 +3958,59 @@ var SupabaseSetupClient = class {
|
|
|
3801
3958
|
} catch (err) {
|
|
3802
3959
|
console.warn("Could not delete vault secret:", err);
|
|
3803
3960
|
}
|
|
3961
|
+
try {
|
|
3962
|
+
await this.runSQL(`
|
|
3963
|
+
SELECT pg_terminate_backend(pid)
|
|
3964
|
+
FROM pg_stat_activity
|
|
3965
|
+
WHERE datname = current_database()
|
|
3966
|
+
AND pid != pg_backend_pid()
|
|
3967
|
+
AND query ILIKE '%stripe.%'
|
|
3968
|
+
`);
|
|
3969
|
+
} catch (err) {
|
|
3970
|
+
console.warn("Could not terminate connections:", err);
|
|
3971
|
+
}
|
|
3804
3972
|
await this.runSQL(`DROP SCHEMA IF EXISTS stripe CASCADE`);
|
|
3805
3973
|
} catch (error) {
|
|
3806
3974
|
throw new Error(`Uninstall failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3807
3975
|
}
|
|
3808
3976
|
}
|
|
3809
|
-
|
|
3977
|
+
/**
|
|
3978
|
+
* Inject package version into Edge Function code
|
|
3979
|
+
*/
|
|
3980
|
+
injectPackageVersion(code, version) {
|
|
3981
|
+
if (version === "latest") {
|
|
3982
|
+
return code;
|
|
3983
|
+
}
|
|
3984
|
+
return code.replace(
|
|
3985
|
+
/from ['"]npm:stripe-experiment-sync['"]/g,
|
|
3986
|
+
`from 'npm:stripe-experiment-sync@${version}'`
|
|
3987
|
+
);
|
|
3988
|
+
}
|
|
3989
|
+
async install(stripeKey, packageVersion) {
|
|
3810
3990
|
const trimmedStripeKey = stripeKey.trim();
|
|
3811
3991
|
if (!trimmedStripeKey.startsWith("sk_") && !trimmedStripeKey.startsWith("rk_")) {
|
|
3812
3992
|
throw new Error('Stripe key should start with "sk_" or "rk_"');
|
|
3813
3993
|
}
|
|
3994
|
+
const version = packageVersion || "latest";
|
|
3814
3995
|
try {
|
|
3815
3996
|
await this.validateProject();
|
|
3816
3997
|
await this.runSQL(`CREATE SCHEMA IF NOT EXISTS stripe`);
|
|
3817
3998
|
await this.updateInstallationComment(
|
|
3818
3999
|
`${STRIPE_SCHEMA_COMMENT_PREFIX} v${package_default.version} ${INSTALLATION_STARTED_SUFFIX}`
|
|
3819
4000
|
);
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
4001
|
+
const versionedSetup = this.injectPackageVersion(setupFunctionCode, version);
|
|
4002
|
+
const versionedWebhook = this.injectPackageVersion(webhookFunctionCode, version);
|
|
4003
|
+
const versionedWorker = this.injectPackageVersion(workerFunctionCode, version);
|
|
4004
|
+
await this.deployFunction("stripe-setup", versionedSetup);
|
|
4005
|
+
await this.deployFunction("stripe-webhook", versionedWebhook);
|
|
4006
|
+
await this.deployFunction("stripe-worker", versionedWorker);
|
|
3823
4007
|
await this.setSecrets([{ name: "STRIPE_SECRET_KEY", value: trimmedStripeKey }]);
|
|
3824
4008
|
const serviceRoleKey = await this.getServiceRoleKey();
|
|
3825
4009
|
const setupResult = await this.invokeFunction("stripe-setup", serviceRoleKey);
|
|
3826
4010
|
if (!setupResult.success) {
|
|
3827
4011
|
throw new Error(`Setup failed: ${setupResult.error}`);
|
|
3828
4012
|
}
|
|
3829
|
-
|
|
3830
|
-
try {
|
|
3831
|
-
await this.setupPgCronJob();
|
|
3832
|
-
pgCronEnabled = true;
|
|
3833
|
-
} catch {
|
|
3834
|
-
console.warn("pg_cron setup failed - falling back to manual worker invocation");
|
|
3835
|
-
}
|
|
3836
|
-
if (!pgCronEnabled) {
|
|
3837
|
-
try {
|
|
3838
|
-
await this.invokeFunction("stripe-worker", serviceRoleKey);
|
|
3839
|
-
} catch (err) {
|
|
3840
|
-
console.warn("Failed to trigger initial worker invocation:", err);
|
|
3841
|
-
}
|
|
3842
|
-
}
|
|
4013
|
+
await this.setupPgCronJob();
|
|
3843
4014
|
await this.updateInstallationComment(
|
|
3844
4015
|
`${STRIPE_SCHEMA_COMMENT_PREFIX} v${package_default.version} ${INSTALLATION_INSTALLED_SUFFIX}`
|
|
3845
4016
|
);
|
|
@@ -3852,14 +4023,14 @@ var SupabaseSetupClient = class {
|
|
|
3852
4023
|
}
|
|
3853
4024
|
};
|
|
3854
4025
|
async function install(params) {
|
|
3855
|
-
const { supabaseAccessToken, supabaseProjectRef, stripeKey } = params;
|
|
4026
|
+
const { supabaseAccessToken, supabaseProjectRef, stripeKey, packageVersion } = params;
|
|
3856
4027
|
const client = new SupabaseSetupClient({
|
|
3857
4028
|
accessToken: supabaseAccessToken,
|
|
3858
4029
|
projectRef: supabaseProjectRef,
|
|
3859
4030
|
projectBaseUrl: params.baseProjectUrl,
|
|
3860
4031
|
managementApiBaseUrl: params.baseManagementApiUrl
|
|
3861
4032
|
});
|
|
3862
|
-
await client.install(stripeKey);
|
|
4033
|
+
await client.install(stripeKey, packageVersion);
|
|
3863
4034
|
}
|
|
3864
4035
|
async function uninstall(params) {
|
|
3865
4036
|
const { supabaseAccessToken, supabaseProjectRef, stripeKey } = params;
|
|
@@ -4063,10 +4234,19 @@ async function syncCommand(options) {
|
|
|
4063
4234
|
let tunnel = null;
|
|
4064
4235
|
let server = null;
|
|
4065
4236
|
let webhookId = null;
|
|
4237
|
+
let wsClient = null;
|
|
4066
4238
|
const cleanup = async (signal) => {
|
|
4067
4239
|
console.log(import_chalk3.default.blue(`
|
|
4068
4240
|
|
|
4069
4241
|
Cleaning up... (signal: ${signal || "manual"})`));
|
|
4242
|
+
if (wsClient) {
|
|
4243
|
+
try {
|
|
4244
|
+
wsClient.close();
|
|
4245
|
+
console.log(import_chalk3.default.green("\u2713 WebSocket closed"));
|
|
4246
|
+
} catch {
|
|
4247
|
+
console.log(import_chalk3.default.yellow("\u26A0 Could not close WebSocket"));
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4070
4250
|
const keepWebhooksOnShutdown = process.env.KEEP_WEBHOOKS_ON_SHUTDOWN === "true";
|
|
4071
4251
|
if (webhookId && stripeSync && !keepWebhooksOnShutdown) {
|
|
4072
4252
|
try {
|
|
@@ -4110,7 +4290,12 @@ Cleaning up... (signal: ${signal || "manual"})`));
|
|
|
4110
4290
|
process.on("SIGTERM", () => cleanup("SIGTERM"));
|
|
4111
4291
|
try {
|
|
4112
4292
|
const config = await loadConfig(options);
|
|
4113
|
-
|
|
4293
|
+
const useWebSocketMode = !config.ngrokAuthToken;
|
|
4294
|
+
const modeLabel = useWebSocketMode ? "WebSocket" : "Webhook (ngrok)";
|
|
4295
|
+
console.log(import_chalk3.default.blue(`
|
|
4296
|
+
Mode: ${modeLabel}`));
|
|
4297
|
+
const maskedDbUrl = config.databaseUrl.replace(/:[^:@]+@/, ":****@");
|
|
4298
|
+
console.log(import_chalk3.default.gray(`Database: ${maskedDbUrl}`));
|
|
4114
4299
|
try {
|
|
4115
4300
|
await runMigrations({
|
|
4116
4301
|
databaseUrl: config.databaseUrl
|
|
@@ -4135,53 +4320,90 @@ Cleaning up... (signal: ${signal || "manual"})`));
|
|
|
4135
4320
|
backfillRelatedEntities: process.env.BACKFILL_RELATED_ENTITIES !== "false",
|
|
4136
4321
|
poolConfig
|
|
4137
4322
|
});
|
|
4138
|
-
const
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4323
|
+
const databaseUrlWithoutPassword = config.databaseUrl.replace(/:[^:@]+@/, ":****@");
|
|
4324
|
+
if (useWebSocketMode) {
|
|
4325
|
+
console.log(import_chalk3.default.blue("\nConnecting to Stripe WebSocket..."));
|
|
4326
|
+
wsClient = await createStripeWebSocketClient({
|
|
4327
|
+
stripeApiKey: config.stripeApiKey,
|
|
4328
|
+
onEvent: async (event) => {
|
|
4329
|
+
try {
|
|
4330
|
+
const payload = JSON.parse(event.event_payload);
|
|
4331
|
+
console.log(import_chalk3.default.cyan(`\u2190 ${payload.type}`) + import_chalk3.default.gray(` (${payload.id})`));
|
|
4332
|
+
if (stripeSync) {
|
|
4333
|
+
await stripeSync.processEvent(payload);
|
|
4334
|
+
return {
|
|
4335
|
+
status: 200,
|
|
4336
|
+
event_type: payload.type,
|
|
4337
|
+
event_id: payload.id,
|
|
4338
|
+
databaseUrl: databaseUrlWithoutPassword
|
|
4339
|
+
};
|
|
4340
|
+
}
|
|
4341
|
+
} catch (err) {
|
|
4342
|
+
console.error(import_chalk3.default.red("Error processing event:"), err);
|
|
4343
|
+
return {
|
|
4344
|
+
status: 500,
|
|
4345
|
+
databaseUrl: databaseUrlWithoutPassword,
|
|
4346
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4347
|
+
};
|
|
4348
|
+
}
|
|
4349
|
+
},
|
|
4350
|
+
onReady: (secret) => {
|
|
4351
|
+
console.log(import_chalk3.default.green("\u2713 Connected to Stripe WebSocket"));
|
|
4352
|
+
const maskedSecret = secret.length > 14 ? `${secret.slice(0, 10)}...${secret.slice(-4)}` : "****";
|
|
4353
|
+
console.log(import_chalk3.default.gray(` Webhook secret: ${maskedSecret}`));
|
|
4354
|
+
},
|
|
4355
|
+
onError: (error) => {
|
|
4356
|
+
console.error(import_chalk3.default.red("WebSocket error:"), error.message);
|
|
4357
|
+
},
|
|
4358
|
+
onClose: (code, reason) => {
|
|
4359
|
+
console.log(import_chalk3.default.yellow(`WebSocket closed: ${code} - ${reason}`));
|
|
4360
|
+
}
|
|
4361
|
+
});
|
|
4362
|
+
} else {
|
|
4363
|
+
const port = 3e3;
|
|
4364
|
+
tunnel = await createTunnel(port, config.ngrokAuthToken);
|
|
4365
|
+
const webhookPath = process.env.WEBHOOK_PATH || "/stripe-webhooks";
|
|
4366
|
+
console.log(import_chalk3.default.blue("\nCreating Stripe webhook endpoint..."));
|
|
4367
|
+
const webhook = await stripeSync.findOrCreateManagedWebhook(`${tunnel.url}${webhookPath}`);
|
|
4368
|
+
webhookId = webhook.id;
|
|
4369
|
+
const eventCount = webhook.enabled_events?.length || 0;
|
|
4370
|
+
console.log(import_chalk3.default.green(`\u2713 Webhook created: ${webhook.id}`));
|
|
4371
|
+
console.log(import_chalk3.default.cyan(` URL: ${webhook.url}`));
|
|
4372
|
+
console.log(import_chalk3.default.cyan(` Events: ${eventCount} supported events`));
|
|
4373
|
+
const app = (0, import_express.default)();
|
|
4374
|
+
const webhookRoute = webhookPath;
|
|
4375
|
+
app.use(webhookRoute, import_express.default.raw({ type: "application/json" }));
|
|
4376
|
+
app.post(webhookRoute, async (req, res) => {
|
|
4377
|
+
const sig = req.headers["stripe-signature"];
|
|
4378
|
+
if (!sig || typeof sig !== "string") {
|
|
4379
|
+
console.error("[Webhook] Missing stripe-signature header");
|
|
4380
|
+
return res.status(400).send({ error: "Missing stripe-signature header" });
|
|
4381
|
+
}
|
|
4382
|
+
const rawBody = req.body;
|
|
4383
|
+
if (!rawBody || !Buffer.isBuffer(rawBody)) {
|
|
4384
|
+
console.error("[Webhook] Body is not a Buffer!");
|
|
4385
|
+
return res.status(400).send({ error: "Missing raw body for signature verification" });
|
|
4386
|
+
}
|
|
4387
|
+
try {
|
|
4388
|
+
await stripeSync.processWebhook(rawBody, sig);
|
|
4389
|
+
return res.status(200).send({ received: true });
|
|
4390
|
+
} catch (error) {
|
|
4391
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4392
|
+
console.error("[Webhook] Processing error:", errorMessage);
|
|
4393
|
+
return res.status(400).send({ error: errorMessage });
|
|
4394
|
+
}
|
|
4395
|
+
});
|
|
4396
|
+
app.use(import_express.default.json());
|
|
4397
|
+
app.use(import_express.default.urlencoded({ extended: false }));
|
|
4398
|
+
app.get("/health", async (req, res) => res.status(200).json({ status: "ok" }));
|
|
4399
|
+
console.log(import_chalk3.default.blue(`
|
|
4177
4400
|
Starting server on port ${port}...`));
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4401
|
+
await new Promise((resolve, reject) => {
|
|
4402
|
+
server = app.listen(port, "0.0.0.0", () => resolve());
|
|
4403
|
+
server.on("error", reject);
|
|
4181
4404
|
});
|
|
4182
|
-
|
|
4183
|
-
}
|
|
4184
|
-
console.log(import_chalk3.default.green(`\u2713 Server started on port ${port}`));
|
|
4405
|
+
console.log(import_chalk3.default.green(`\u2713 Server started on port ${port}`));
|
|
4406
|
+
}
|
|
4185
4407
|
if (process.env.SKIP_BACKFILL !== "true") {
|
|
4186
4408
|
console.log(import_chalk3.default.blue("\nStarting initial sync of all Stripe data..."));
|
|
4187
4409
|
const syncResult = await stripeSync.processUntilDone();
|
|
@@ -4258,7 +4480,8 @@ async function installCommand(options) {
|
|
|
4258
4480
|
await install({
|
|
4259
4481
|
supabaseAccessToken: accessToken,
|
|
4260
4482
|
supabaseProjectRef: projectRef,
|
|
4261
|
-
stripeKey
|
|
4483
|
+
stripeKey,
|
|
4484
|
+
packageVersion: options.packageVersion
|
|
4262
4485
|
});
|
|
4263
4486
|
console.log(import_chalk3.default.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"));
|
|
4264
4487
|
console.log(import_chalk3.default.cyan.bold(" Installation Complete!"));
|
package/dist/cli/lib.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
interface Config {
|
|
2
2
|
stripeApiKey: string;
|
|
3
|
-
ngrokAuthToken
|
|
3
|
+
ngrokAuthToken?: string;
|
|
4
4
|
databaseUrl: string;
|
|
5
5
|
}
|
|
6
6
|
interface CliOptions {
|
|
@@ -18,6 +18,7 @@ interface DeployOptions {
|
|
|
18
18
|
supabaseAccessToken?: string;
|
|
19
19
|
supabaseProjectRef?: string;
|
|
20
20
|
stripeKey?: string;
|
|
21
|
+
packageVersion?: string;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -30,11 +31,9 @@ declare function backfillCommand(options: CliOptions, entityName: string): Promi
|
|
|
30
31
|
declare function migrateCommand(options: CliOptions): Promise<void>;
|
|
31
32
|
/**
|
|
32
33
|
* Main sync command - syncs Stripe data to PostgreSQL using webhooks for real-time updates.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* 4. Runs initial backfill of all Stripe data
|
|
37
|
-
* 5. Keeps running to process live webhook events (Ctrl+C to stop)
|
|
34
|
+
* Supports two modes:
|
|
35
|
+
* - WebSocket mode (default): Direct connection to Stripe via WebSocket, no ngrok needed
|
|
36
|
+
* - Webhook mode: Uses ngrok tunnel + Express server (when NGROK_AUTH_TOKEN is provided)
|
|
38
37
|
*/
|
|
39
38
|
declare function syncCommand(options: CliOptions): Promise<void>;
|
|
40
39
|
/**
|
package/dist/cli/lib.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
interface Config {
|
|
2
2
|
stripeApiKey: string;
|
|
3
|
-
ngrokAuthToken
|
|
3
|
+
ngrokAuthToken?: string;
|
|
4
4
|
databaseUrl: string;
|
|
5
5
|
}
|
|
6
6
|
interface CliOptions {
|
|
@@ -18,6 +18,7 @@ interface DeployOptions {
|
|
|
18
18
|
supabaseAccessToken?: string;
|
|
19
19
|
supabaseProjectRef?: string;
|
|
20
20
|
stripeKey?: string;
|
|
21
|
+
packageVersion?: string;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -30,11 +31,9 @@ declare function backfillCommand(options: CliOptions, entityName: string): Promi
|
|
|
30
31
|
declare function migrateCommand(options: CliOptions): Promise<void>;
|
|
31
32
|
/**
|
|
32
33
|
* Main sync command - syncs Stripe data to PostgreSQL using webhooks for real-time updates.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* 4. Runs initial backfill of all Stripe data
|
|
37
|
-
* 5. Keeps running to process live webhook events (Ctrl+C to stop)
|
|
34
|
+
* Supports two modes:
|
|
35
|
+
* - WebSocket mode (default): Direct connection to Stripe via WebSocket, no ngrok needed
|
|
36
|
+
* - Webhook mode: Uses ngrok tunnel + Express server (when NGROK_AUTH_TOKEN is provided)
|
|
38
37
|
*/
|
|
39
38
|
declare function syncCommand(options: CliOptions): Promise<void>;
|
|
40
39
|
/**
|
package/dist/cli/lib.js
CHANGED
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
migrateCommand,
|
|
7
7
|
syncCommand,
|
|
8
8
|
uninstallCommand
|
|
9
|
-
} from "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
9
|
+
} from "../chunk-XDVV7YHP.js";
|
|
10
|
+
import "../chunk-E7LIOBPP.js";
|
|
11
|
+
import "../chunk-E2HSDYSR.js";
|
|
12
|
+
import "../chunk-M3MYAMG2.js";
|
|
13
13
|
export {
|
|
14
14
|
backfillCommand,
|
|
15
15
|
createTunnel,
|