@drawbridge/drawbridge-utils 0.0.177 → 0.0.178
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/connections/index.cjs +313 -42
- package/dist/connections/index.d.cts +1 -1
- package/dist/connections/index.d.ts +1 -1
- package/dist/connections/index.js +308 -42
- package/dist/features.cjs +143 -18
- package/dist/features.d.cts +1 -1
- package/dist/features.d.ts +1 -1
- package/dist/features.js +143 -18
- package/dist/{index-C59xHago.d.cts → index-C2rxasGZ.d.cts} +504 -69
- package/dist/{index-B546oDNo.d.ts → index-DOYCXtd7.d.ts} +504 -69
- package/dist/plans.cjs +143 -18
- package/dist/plans.d.cts +1 -1
- package/dist/plans.d.ts +1 -1
- package/dist/plans.js +143 -18
- package/dist/pricing.cjs +143 -18
- package/dist/pricing.d.cts +1 -1
- package/dist/pricing.d.ts +1 -1
- package/dist/pricing.js +143 -18
- package/dist/providers.cjs +143 -18
- package/dist/providers.d.cts +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +143 -18
- package/package.json +1 -1
package/dist/pricing.cjs
CHANGED
|
@@ -792,6 +792,16 @@ var TRIGGERS = Object.freeze({
|
|
|
792
792
|
"range.ended": Object.freeze({ event: "range.ended", type: "range" }),
|
|
793
793
|
"range.started": Object.freeze({ event: "range.started", type: "range" }),
|
|
794
794
|
"schedule.day": Object.freeze({ event: "day", type: "schedule" }),
|
|
795
|
+
// THE THREE SHOPIFY TRIGGERS, which were never in this vocabulary because the
|
|
796
|
+
// four workflows they fire were hand-written in drawbridge-sync's register job
|
|
797
|
+
// rather than provisioned from the manifest. `webhook` is a fourth trigger
|
|
798
|
+
// TYPE, and it is the honest name: these fire when the vendor calls us, which
|
|
799
|
+
// is neither an event Drawbridge raised nor a schedule it keeps.
|
|
800
|
+
"shopify.order.create": Object.freeze({ event: "shopify.order.create", type: "webhook" }),
|
|
801
|
+
"shopify.product.update": Object.freeze({ event: "shopify.product.update", type: "webhook" }),
|
|
802
|
+
// The token audit, dispatched programmatically by the register job when a
|
|
803
|
+
// store's grant is exchanged or rotated.
|
|
804
|
+
"shopify.token": Object.freeze({ event: "shopify.token", type: "event" }),
|
|
795
805
|
"schedule.month": Object.freeze({ event: "month", type: "schedule" }),
|
|
796
806
|
"schedule.week": Object.freeze({ event: "week", type: "schedule" }),
|
|
797
807
|
"segment.contact.add": Object.freeze({ event: "segment.contact.add", type: "event" }),
|
|
@@ -844,7 +854,7 @@ var OUTCOMES = Object.freeze({
|
|
|
844
854
|
unimplemented: "unimplemented",
|
|
845
855
|
unsupported: "unsupported"
|
|
846
856
|
});
|
|
847
|
-
var STATUSES = Object.freeze(["active", "
|
|
857
|
+
var STATUSES = Object.freeze(["active", "error", "pending"]);
|
|
848
858
|
var PLATFORM_CREDENTIALS = Object.freeze([
|
|
849
859
|
// Seals a merchant's stored connection settings. The webhook connection is
|
|
850
860
|
// gated on it because its whole credential is a secret WE mint and store, so
|
|
@@ -5810,7 +5820,7 @@ var getAppToken = async ({ clientId, clientSecret, fetcher }) => {
|
|
|
5810
5820
|
return fetchAppToken({ clientId, clientSecret, fetcher });
|
|
5811
5821
|
};
|
|
5812
5822
|
var toShopGid = (shopId) => String(shopId).startsWith("gid://") ? String(shopId) : "gid://shopify/Shop/" + shopId;
|
|
5813
|
-
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference, revision, shopId, timestamp, value }) => {
|
|
5823
|
+
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference: reference2, revision, shopId, timestamp, value }) => {
|
|
5814
5824
|
if (!shopId || !eventHandle || !(Number(value) > 0)) {
|
|
5815
5825
|
throw new Error("sendAppEvent requires shopId, eventHandle, and a positive value");
|
|
5816
5826
|
}
|
|
@@ -5824,8 +5834,8 @@ var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempo
|
|
|
5824
5834
|
const body = {
|
|
5825
5835
|
attributes: {
|
|
5826
5836
|
value: Number(value),
|
|
5827
|
-
...
|
|
5828
|
-
reference: String(
|
|
5837
|
+
...reference2 && {
|
|
5838
|
+
reference: String(reference2).slice(0, 128)
|
|
5829
5839
|
}
|
|
5830
5840
|
},
|
|
5831
5841
|
event_handle: eventHandle,
|
|
@@ -7986,6 +7996,12 @@ var shopify_default2 = {
|
|
|
7986
7996
|
orderId: String(orderId),
|
|
7987
7997
|
rate,
|
|
7988
7998
|
shopId: connection2.source.id,
|
|
7999
|
+
// WHICH VENDOR IS BEING CHARGED, so the sender does not have
|
|
8000
|
+
// to assume. queue/usage.js named the slug itself and
|
|
8001
|
+
// defaults to it for anything already queued when this
|
|
8002
|
+
// shipped — an app-store vendor billing through its own meter
|
|
8003
|
+
// enqueues the same job with its own slug.
|
|
8004
|
+
slug: connection2.slug,
|
|
7989
8005
|
// The App Events API returns no event id, so one is generated
|
|
7990
8006
|
// here — the event handle plus the order id — and sent as the
|
|
7991
8007
|
// event's `reference`. queue/usage.js stamps the same id onto
|
|
@@ -9209,6 +9225,13 @@ var shopify_default2 = {
|
|
|
9209
9225
|
key: "Shopify Order Tracking",
|
|
9210
9226
|
queue: "connection",
|
|
9211
9227
|
system: true,
|
|
9228
|
+
// THE TRIGGER THE REGISTER JOB HAND-WROTE. It is byte-identical to
|
|
9229
|
+
// the one on every stored workflow, because these documents exist on
|
|
9230
|
+
// prod with thousands of runs behind them and provisioning is
|
|
9231
|
+
// idempotent on (connection, system, title) — a different trigger
|
|
9232
|
+
// here would not be picked up by the existing rows anyway, and a
|
|
9233
|
+
// different TITLE would provision a second workflow for every store.
|
|
9234
|
+
trigger: { event: "shopify.order.create", type: "webhook" },
|
|
9212
9235
|
type: "step.commerce.order.record"
|
|
9213
9236
|
})
|
|
9214
9237
|
},
|
|
@@ -9219,6 +9242,15 @@ var shopify_default2 = {
|
|
|
9219
9242
|
key: "Shopify Product Sync",
|
|
9220
9243
|
queue: "connection",
|
|
9221
9244
|
system: true,
|
|
9245
|
+
// `shopify.product.update`, NOT `product.insert`. The plan named the
|
|
9246
|
+
// latter, and it is the right long-term answer — a Drawbridge product
|
|
9247
|
+
// row appearing should pull the vendor's copy, rather than
|
|
9248
|
+
// stream/product.js branching on the slug and enqueueing a
|
|
9249
|
+
// vendor-named queue. But a workflow carries ONE trigger, this one is
|
|
9250
|
+
// stored on prod against the webhook, and changing a live workflow's
|
|
9251
|
+
// trigger is a data migration. So the declaration matches what exists
|
|
9252
|
+
// and `product.insert` stays a separate, migration-gated change.
|
|
9253
|
+
trigger: { event: "shopify.product.update", type: "webhook" },
|
|
9222
9254
|
type: "step.commerce.product.sync"
|
|
9223
9255
|
})
|
|
9224
9256
|
}
|
|
@@ -9240,26 +9272,37 @@ var shopify_default2 = {
|
|
|
9240
9272
|
type: "step.connection.health.check"
|
|
9241
9273
|
})
|
|
9242
9274
|
},
|
|
9243
|
-
//
|
|
9244
|
-
//
|
|
9245
|
-
//
|
|
9246
|
-
//
|
|
9247
|
-
//
|
|
9248
|
-
//
|
|
9275
|
+
// THE ONE WORKFLOW THAT CARRIES TWO STEPS, and the shape the plan called
|
|
9276
|
+
// the single genuinely new thing in it.
|
|
9277
|
+
//
|
|
9278
|
+
// Every other system step is its own workflow, titled by its own `key`.
|
|
9279
|
+
// These two are "Shopify Token Activity", one row in the merchant's list
|
|
9280
|
+
// with both steps in it — so they declare `workflow`, which names the
|
|
9281
|
+
// title, the trigger and the ORDER, and the provisioner groups on it.
|
|
9282
|
+
// Without that key the pair could only be provisioned by the hand-written
|
|
9283
|
+
// spec in drawbridge-sync's register job, which is exactly why that spec
|
|
9284
|
+
// outlived every other one.
|
|
9285
|
+
//
|
|
9286
|
+
// AUDIT-ONLY. The step documents are written manually at OAuth time and
|
|
9287
|
+
// the workflow is never dispatched; `queue` is declared defensively so
|
|
9288
|
+
// that if it ever IS, the job lands on a real queue and the handler lookup
|
|
9289
|
+
// misses cleanly rather than throwing "Unknown step type".
|
|
9249
9290
|
token: {
|
|
9250
9291
|
exchange: () => ({
|
|
9251
9292
|
description: "Records the token exchange that completed an install. Audit only \u2014 never dispatched.",
|
|
9252
9293
|
key: "Shopify Token Exchange",
|
|
9253
9294
|
queue: "connection",
|
|
9254
9295
|
system: true,
|
|
9255
|
-
type: "step.connection.token.exchange"
|
|
9296
|
+
type: "step.connection.token.exchange",
|
|
9297
|
+
workflow: { key: "Shopify Token Activity", order: 0, trigger: { event: "shopify.token", type: "event" } }
|
|
9256
9298
|
}),
|
|
9257
9299
|
refresh: () => ({
|
|
9258
9300
|
description: "Records a token rotation. Audit only \u2014 never dispatched.",
|
|
9259
9301
|
key: "Shopify Token Refresh",
|
|
9260
9302
|
queue: "connection",
|
|
9261
9303
|
system: true,
|
|
9262
|
-
type: "step.connection.token.refresh"
|
|
9304
|
+
type: "step.connection.token.refresh",
|
|
9305
|
+
workflow: { key: "Shopify Token Activity", order: 1, trigger: { event: "shopify.token", type: "event" } }
|
|
9263
9306
|
})
|
|
9264
9307
|
}
|
|
9265
9308
|
}
|
|
@@ -9715,7 +9758,7 @@ var webhook_default = {
|
|
|
9715
9758
|
// The connect prompt only where connecting is not already the card's whole
|
|
9716
9759
|
// story: a disconnected or errored document renders a Connect action itself,
|
|
9717
9760
|
// and a task repeating it talks over the button.
|
|
9718
|
-
tasks: ({ settings, status } = {}) =>
|
|
9761
|
+
tasks: ({ settings, status } = {}) => status === "error" ? [] : (settings == null ? void 0 : settings.secret) ? [
|
|
9719
9762
|
{
|
|
9720
9763
|
message: 'Compute HMAC-SHA256( secret, t + "." + raw body ) and constant-time compare it against a v1 in the X-Drawbridge-Signature header; reject timestamps older than five minutes.',
|
|
9721
9764
|
title: "Requests must be verified",
|
|
@@ -9802,6 +9845,77 @@ var attentive_default3 = {
|
|
|
9802
9845
|
}
|
|
9803
9846
|
};
|
|
9804
9847
|
|
|
9848
|
+
// lib/connections/icons/brightdata.js
|
|
9849
|
+
var brightdata_default = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
9850
|
+
<rect width="500" height="500" fill="#0F58FF"/>
|
|
9851
|
+
<circle cx="250" cy="250" r="52" fill="white"/>
|
|
9852
|
+
<rect x="234" y="70" width="32" height="96" rx="16" fill="white"/>
|
|
9853
|
+
<rect x="234" y="334" width="32" height="96" rx="16" fill="white"/>
|
|
9854
|
+
<rect x="70" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
9855
|
+
<rect x="334" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
9856
|
+
<rect x="117" y="140" width="32" height="96" rx="16" transform="rotate(-45 117 140)" fill="white"/>
|
|
9857
|
+
<rect x="304" y="327" width="32" height="96" rx="16" transform="rotate(-45 304 327)" fill="white"/>
|
|
9858
|
+
<rect x="360" y="117" width="32" height="96" rx="16" transform="rotate(45 360 117)" fill="white"/>
|
|
9859
|
+
<rect x="173" y="304" width="32" height="96" rx="16" transform="rotate(45 173 304)" fill="white"/>
|
|
9860
|
+
</svg>`;
|
|
9861
|
+
|
|
9862
|
+
// lib/connections/vendors/brightdata.js
|
|
9863
|
+
var brightdata_default2 = {
|
|
9864
|
+
fields: [
|
|
9865
|
+
{
|
|
9866
|
+
credential: "BRIGHTDATA_BROWSER_URI",
|
|
9867
|
+
input: "password",
|
|
9868
|
+
key: "browserUri",
|
|
9869
|
+
// A wss:// endpoint with the account's user and password in its
|
|
9870
|
+
// authority, which is why it is a password field rather than a url one:
|
|
9871
|
+
// the whole string is the credential.
|
|
9872
|
+
label: "Scraping Browser endpoint",
|
|
9873
|
+
message: "The wss:// Scraping Browser endpoint, carrying its own credentials. Used to render pages that need a real browser.",
|
|
9874
|
+
redact: true,
|
|
9875
|
+
required: true
|
|
9876
|
+
},
|
|
9877
|
+
{
|
|
9878
|
+
credential: "BRIGHTDATA_UNLOCKER_API_KEY",
|
|
9879
|
+
input: "password",
|
|
9880
|
+
key: "unlockerApiKey",
|
|
9881
|
+
label: "Web Unlocker API key",
|
|
9882
|
+
// NO EXPIRY FIELD, and that is a decision rather than an omission. Bright
|
|
9883
|
+
// Data offers an `Unlimited` expiry and recommends against it; Drawbridge
|
|
9884
|
+
// takes the unlimited key deliberately, because there is no way to rotate
|
|
9885
|
+
// this programmatically and a date nothing can act on is a date that only
|
|
9886
|
+
// surfaces after it has already broken scraping. If a fixed expiry is ever
|
|
9887
|
+
// taken instead, the field to add is a stored date and a warning ahead of
|
|
9888
|
+
// it — not a rotation, which the vendor cannot support.
|
|
9889
|
+
message: "Bright Data account settings \u2192 API keys. Taken with an unlimited expiry: their API offers no way to rotate a key, so a dated one can only ever break scraping silently.",
|
|
9890
|
+
redact: true,
|
|
9891
|
+
required: true
|
|
9892
|
+
},
|
|
9893
|
+
{
|
|
9894
|
+
credential: "BRIGHTDATA_UNLOCKER_ZONE",
|
|
9895
|
+
input: "text",
|
|
9896
|
+
key: "unlockerZone",
|
|
9897
|
+
label: "Web Unlocker zone",
|
|
9898
|
+
// Not secret — a zone is a label on the account, and it is shown rather
|
|
9899
|
+
// than redacted so an operator can confirm which one is in use without
|
|
9900
|
+
// clearing the field to read it.
|
|
9901
|
+
message: "The zone name the Unlocker requests run against.",
|
|
9902
|
+
required: true
|
|
9903
|
+
}
|
|
9904
|
+
],
|
|
9905
|
+
icon: brightdata_default,
|
|
9906
|
+
name: "Bright Data",
|
|
9907
|
+
slug: "brightdata",
|
|
9908
|
+
// WHERE THE FACTS CAME FROM, read 2026-09-16.
|
|
9909
|
+
urls: {
|
|
9910
|
+
api: "https://docs.brightdata.com/api-reference/authentication",
|
|
9911
|
+
dashboard: "https://brightdata.com/cp/setting/users",
|
|
9912
|
+
// Bright Data has no scope model — a key carries the account's own access —
|
|
9913
|
+
// so there is nothing to link. `false` is the recorded answer rather than a
|
|
9914
|
+
// missing key.
|
|
9915
|
+
scopes: false
|
|
9916
|
+
}
|
|
9917
|
+
};
|
|
9918
|
+
|
|
9805
9919
|
// lib/connections/vendors/hubspot.js
|
|
9806
9920
|
var hubspot_default2 = {
|
|
9807
9921
|
// WHAT AN ADMIN TYPES on the provider screen. `credential` names the env var a
|
|
@@ -10177,7 +10291,7 @@ var checkIcon = (owner, icon) => {
|
|
|
10177
10291
|
}
|
|
10178
10292
|
};
|
|
10179
10293
|
var vendors = Object.freeze(Object.fromEntries(
|
|
10180
|
-
[attentive_default3, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
10294
|
+
[attentive_default3, brightdata_default2, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
10181
10295
|
));
|
|
10182
10296
|
var buildVendor = (vendor) => {
|
|
10183
10297
|
if (!(vendor == null ? void 0 : vendor.slug)) throw new Error("A vendor needs a slug");
|
|
@@ -10204,7 +10318,7 @@ for (const [slug2, vendor] of Object.entries(vendors)) {
|
|
|
10204
10318
|
buildVendor(vendor);
|
|
10205
10319
|
}
|
|
10206
10320
|
var build = (manifest) => {
|
|
10207
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
|
|
10321
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
10208
10322
|
if (!(manifest == null ? void 0 : manifest.slug)) throw new Error("A connection needs a slug");
|
|
10209
10323
|
if (!(manifest == null ? void 0 : manifest.title)) throw new Error(manifest.slug + " needs a title");
|
|
10210
10324
|
if (manifest == null ? void 0 : manifest.private) throw new Error(manifest.slug + " declares private \u2014 that is feature : false now");
|
|
@@ -10470,11 +10584,22 @@ var build = (manifest) => {
|
|
|
10470
10584
|
manifest.slug + " step " + type2 + " may be triggered by " + trigger + ", which is not a trigger \u2014 one of " + Object.keys(TRIGGERS).join(", ")
|
|
10471
10585
|
);
|
|
10472
10586
|
}
|
|
10473
|
-
if (declared2.
|
|
10474
|
-
|
|
10587
|
+
if (declared2.workflow) {
|
|
10588
|
+
if (!declared2.workflow.key) {
|
|
10589
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no key \u2014 the title several steps share");
|
|
10590
|
+
}
|
|
10591
|
+
if (typeof declared2.workflow.order !== "number") {
|
|
10592
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no order \u2014 two steps in one row need a sequence");
|
|
10593
|
+
}
|
|
10594
|
+
if (declared2.trigger) {
|
|
10595
|
+
throw new Error(manifest.slug + " step " + type2 + " declares BOTH a trigger and a workflow \u2014 the workflow carries the trigger for every step in it");
|
|
10596
|
+
}
|
|
10597
|
+
}
|
|
10598
|
+
for (const candidate of [declared2.trigger, (_y = declared2.workflow) == null ? void 0 : _y.trigger].filter(Boolean)) {
|
|
10599
|
+
const known2 = TRIGGERS[Object.keys(TRIGGERS).find((key) => TRIGGERS[key].event === candidate.event && TRIGGERS[key].type === candidate.type)];
|
|
10475
10600
|
if (!known2) {
|
|
10476
10601
|
throw new Error(
|
|
10477
|
-
manifest.slug + " step " + type2 + " declares trigger { event : " +
|
|
10602
|
+
manifest.slug + " step " + type2 + " declares trigger { event : " + candidate.event + ", type : " + candidate.type + " }, which nothing dispatches"
|
|
10478
10603
|
);
|
|
10479
10604
|
}
|
|
10480
10605
|
}
|
package/dist/pricing.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as channels } from './index-
|
|
1
|
+
import { u as channels } from './index-C2rxasGZ.cjs';
|
|
2
2
|
import { MARKUP } from './billing.cjs';
|
|
3
3
|
export { LOCAL_FLAT_CENTS, PREMIUM_CPM_CENTS, PREMIUM_RATE_PER_GB, REUSE_FLAT_CENTS, STANDARD_CPM_CENTS, STANDARD_RATE_PER_GB, ai, scrape } from './billing.cjs';
|
|
4
4
|
export { conversionRate, free, plans, resolvePlan, unitAmountDecimal } from './plans.cjs';
|
package/dist/pricing.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as channels } from './index-
|
|
1
|
+
import { u as channels } from './index-DOYCXtd7.js';
|
|
2
2
|
import { MARKUP } from './billing.js';
|
|
3
3
|
export { LOCAL_FLAT_CENTS, PREMIUM_CPM_CENTS, PREMIUM_RATE_PER_GB, REUSE_FLAT_CENTS, STANDARD_CPM_CENTS, STANDARD_RATE_PER_GB, ai, scrape } from './billing.js';
|
|
4
4
|
export { conversionRate, free, plans, resolvePlan, unitAmountDecimal } from './plans.js';
|
package/dist/pricing.js
CHANGED
|
@@ -746,6 +746,16 @@ var TRIGGERS = Object.freeze({
|
|
|
746
746
|
"range.ended": Object.freeze({ event: "range.ended", type: "range" }),
|
|
747
747
|
"range.started": Object.freeze({ event: "range.started", type: "range" }),
|
|
748
748
|
"schedule.day": Object.freeze({ event: "day", type: "schedule" }),
|
|
749
|
+
// THE THREE SHOPIFY TRIGGERS, which were never in this vocabulary because the
|
|
750
|
+
// four workflows they fire were hand-written in drawbridge-sync's register job
|
|
751
|
+
// rather than provisioned from the manifest. `webhook` is a fourth trigger
|
|
752
|
+
// TYPE, and it is the honest name: these fire when the vendor calls us, which
|
|
753
|
+
// is neither an event Drawbridge raised nor a schedule it keeps.
|
|
754
|
+
"shopify.order.create": Object.freeze({ event: "shopify.order.create", type: "webhook" }),
|
|
755
|
+
"shopify.product.update": Object.freeze({ event: "shopify.product.update", type: "webhook" }),
|
|
756
|
+
// The token audit, dispatched programmatically by the register job when a
|
|
757
|
+
// store's grant is exchanged or rotated.
|
|
758
|
+
"shopify.token": Object.freeze({ event: "shopify.token", type: "event" }),
|
|
749
759
|
"schedule.month": Object.freeze({ event: "month", type: "schedule" }),
|
|
750
760
|
"schedule.week": Object.freeze({ event: "week", type: "schedule" }),
|
|
751
761
|
"segment.contact.add": Object.freeze({ event: "segment.contact.add", type: "event" }),
|
|
@@ -798,7 +808,7 @@ var OUTCOMES = Object.freeze({
|
|
|
798
808
|
unimplemented: "unimplemented",
|
|
799
809
|
unsupported: "unsupported"
|
|
800
810
|
});
|
|
801
|
-
var STATUSES = Object.freeze(["active", "
|
|
811
|
+
var STATUSES = Object.freeze(["active", "error", "pending"]);
|
|
802
812
|
var PLATFORM_CREDENTIALS = Object.freeze([
|
|
803
813
|
// Seals a merchant's stored connection settings. The webhook connection is
|
|
804
814
|
// gated on it because its whole credential is a secret WE mint and store, so
|
|
@@ -5764,7 +5774,7 @@ var getAppToken = async ({ clientId, clientSecret, fetcher }) => {
|
|
|
5764
5774
|
return fetchAppToken({ clientId, clientSecret, fetcher });
|
|
5765
5775
|
};
|
|
5766
5776
|
var toShopGid = (shopId) => String(shopId).startsWith("gid://") ? String(shopId) : "gid://shopify/Shop/" + shopId;
|
|
5767
|
-
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference, revision, shopId, timestamp, value }) => {
|
|
5777
|
+
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference: reference2, revision, shopId, timestamp, value }) => {
|
|
5768
5778
|
if (!shopId || !eventHandle || !(Number(value) > 0)) {
|
|
5769
5779
|
throw new Error("sendAppEvent requires shopId, eventHandle, and a positive value");
|
|
5770
5780
|
}
|
|
@@ -5778,8 +5788,8 @@ var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempo
|
|
|
5778
5788
|
const body = {
|
|
5779
5789
|
attributes: {
|
|
5780
5790
|
value: Number(value),
|
|
5781
|
-
...
|
|
5782
|
-
reference: String(
|
|
5791
|
+
...reference2 && {
|
|
5792
|
+
reference: String(reference2).slice(0, 128)
|
|
5783
5793
|
}
|
|
5784
5794
|
},
|
|
5785
5795
|
event_handle: eventHandle,
|
|
@@ -7940,6 +7950,12 @@ var shopify_default2 = {
|
|
|
7940
7950
|
orderId: String(orderId),
|
|
7941
7951
|
rate,
|
|
7942
7952
|
shopId: connection2.source.id,
|
|
7953
|
+
// WHICH VENDOR IS BEING CHARGED, so the sender does not have
|
|
7954
|
+
// to assume. queue/usage.js named the slug itself and
|
|
7955
|
+
// defaults to it for anything already queued when this
|
|
7956
|
+
// shipped — an app-store vendor billing through its own meter
|
|
7957
|
+
// enqueues the same job with its own slug.
|
|
7958
|
+
slug: connection2.slug,
|
|
7943
7959
|
// The App Events API returns no event id, so one is generated
|
|
7944
7960
|
// here — the event handle plus the order id — and sent as the
|
|
7945
7961
|
// event's `reference`. queue/usage.js stamps the same id onto
|
|
@@ -9163,6 +9179,13 @@ var shopify_default2 = {
|
|
|
9163
9179
|
key: "Shopify Order Tracking",
|
|
9164
9180
|
queue: "connection",
|
|
9165
9181
|
system: true,
|
|
9182
|
+
// THE TRIGGER THE REGISTER JOB HAND-WROTE. It is byte-identical to
|
|
9183
|
+
// the one on every stored workflow, because these documents exist on
|
|
9184
|
+
// prod with thousands of runs behind them and provisioning is
|
|
9185
|
+
// idempotent on (connection, system, title) — a different trigger
|
|
9186
|
+
// here would not be picked up by the existing rows anyway, and a
|
|
9187
|
+
// different TITLE would provision a second workflow for every store.
|
|
9188
|
+
trigger: { event: "shopify.order.create", type: "webhook" },
|
|
9166
9189
|
type: "step.commerce.order.record"
|
|
9167
9190
|
})
|
|
9168
9191
|
},
|
|
@@ -9173,6 +9196,15 @@ var shopify_default2 = {
|
|
|
9173
9196
|
key: "Shopify Product Sync",
|
|
9174
9197
|
queue: "connection",
|
|
9175
9198
|
system: true,
|
|
9199
|
+
// `shopify.product.update`, NOT `product.insert`. The plan named the
|
|
9200
|
+
// latter, and it is the right long-term answer — a Drawbridge product
|
|
9201
|
+
// row appearing should pull the vendor's copy, rather than
|
|
9202
|
+
// stream/product.js branching on the slug and enqueueing a
|
|
9203
|
+
// vendor-named queue. But a workflow carries ONE trigger, this one is
|
|
9204
|
+
// stored on prod against the webhook, and changing a live workflow's
|
|
9205
|
+
// trigger is a data migration. So the declaration matches what exists
|
|
9206
|
+
// and `product.insert` stays a separate, migration-gated change.
|
|
9207
|
+
trigger: { event: "shopify.product.update", type: "webhook" },
|
|
9176
9208
|
type: "step.commerce.product.sync"
|
|
9177
9209
|
})
|
|
9178
9210
|
}
|
|
@@ -9194,26 +9226,37 @@ var shopify_default2 = {
|
|
|
9194
9226
|
type: "step.connection.health.check"
|
|
9195
9227
|
})
|
|
9196
9228
|
},
|
|
9197
|
-
//
|
|
9198
|
-
//
|
|
9199
|
-
//
|
|
9200
|
-
//
|
|
9201
|
-
//
|
|
9202
|
-
//
|
|
9229
|
+
// THE ONE WORKFLOW THAT CARRIES TWO STEPS, and the shape the plan called
|
|
9230
|
+
// the single genuinely new thing in it.
|
|
9231
|
+
//
|
|
9232
|
+
// Every other system step is its own workflow, titled by its own `key`.
|
|
9233
|
+
// These two are "Shopify Token Activity", one row in the merchant's list
|
|
9234
|
+
// with both steps in it — so they declare `workflow`, which names the
|
|
9235
|
+
// title, the trigger and the ORDER, and the provisioner groups on it.
|
|
9236
|
+
// Without that key the pair could only be provisioned by the hand-written
|
|
9237
|
+
// spec in drawbridge-sync's register job, which is exactly why that spec
|
|
9238
|
+
// outlived every other one.
|
|
9239
|
+
//
|
|
9240
|
+
// AUDIT-ONLY. The step documents are written manually at OAuth time and
|
|
9241
|
+
// the workflow is never dispatched; `queue` is declared defensively so
|
|
9242
|
+
// that if it ever IS, the job lands on a real queue and the handler lookup
|
|
9243
|
+
// misses cleanly rather than throwing "Unknown step type".
|
|
9203
9244
|
token: {
|
|
9204
9245
|
exchange: () => ({
|
|
9205
9246
|
description: "Records the token exchange that completed an install. Audit only \u2014 never dispatched.",
|
|
9206
9247
|
key: "Shopify Token Exchange",
|
|
9207
9248
|
queue: "connection",
|
|
9208
9249
|
system: true,
|
|
9209
|
-
type: "step.connection.token.exchange"
|
|
9250
|
+
type: "step.connection.token.exchange",
|
|
9251
|
+
workflow: { key: "Shopify Token Activity", order: 0, trigger: { event: "shopify.token", type: "event" } }
|
|
9210
9252
|
}),
|
|
9211
9253
|
refresh: () => ({
|
|
9212
9254
|
description: "Records a token rotation. Audit only \u2014 never dispatched.",
|
|
9213
9255
|
key: "Shopify Token Refresh",
|
|
9214
9256
|
queue: "connection",
|
|
9215
9257
|
system: true,
|
|
9216
|
-
type: "step.connection.token.refresh"
|
|
9258
|
+
type: "step.connection.token.refresh",
|
|
9259
|
+
workflow: { key: "Shopify Token Activity", order: 1, trigger: { event: "shopify.token", type: "event" } }
|
|
9217
9260
|
})
|
|
9218
9261
|
}
|
|
9219
9262
|
}
|
|
@@ -9669,7 +9712,7 @@ var webhook_default = {
|
|
|
9669
9712
|
// The connect prompt only where connecting is not already the card's whole
|
|
9670
9713
|
// story: a disconnected or errored document renders a Connect action itself,
|
|
9671
9714
|
// and a task repeating it talks over the button.
|
|
9672
|
-
tasks: ({ settings, status } = {}) =>
|
|
9715
|
+
tasks: ({ settings, status } = {}) => status === "error" ? [] : (settings == null ? void 0 : settings.secret) ? [
|
|
9673
9716
|
{
|
|
9674
9717
|
message: 'Compute HMAC-SHA256( secret, t + "." + raw body ) and constant-time compare it against a v1 in the X-Drawbridge-Signature header; reject timestamps older than five minutes.',
|
|
9675
9718
|
title: "Requests must be verified",
|
|
@@ -9756,6 +9799,77 @@ var attentive_default3 = {
|
|
|
9756
9799
|
}
|
|
9757
9800
|
};
|
|
9758
9801
|
|
|
9802
|
+
// lib/connections/icons/brightdata.js
|
|
9803
|
+
var brightdata_default = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
9804
|
+
<rect width="500" height="500" fill="#0F58FF"/>
|
|
9805
|
+
<circle cx="250" cy="250" r="52" fill="white"/>
|
|
9806
|
+
<rect x="234" y="70" width="32" height="96" rx="16" fill="white"/>
|
|
9807
|
+
<rect x="234" y="334" width="32" height="96" rx="16" fill="white"/>
|
|
9808
|
+
<rect x="70" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
9809
|
+
<rect x="334" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
9810
|
+
<rect x="117" y="140" width="32" height="96" rx="16" transform="rotate(-45 117 140)" fill="white"/>
|
|
9811
|
+
<rect x="304" y="327" width="32" height="96" rx="16" transform="rotate(-45 304 327)" fill="white"/>
|
|
9812
|
+
<rect x="360" y="117" width="32" height="96" rx="16" transform="rotate(45 360 117)" fill="white"/>
|
|
9813
|
+
<rect x="173" y="304" width="32" height="96" rx="16" transform="rotate(45 173 304)" fill="white"/>
|
|
9814
|
+
</svg>`;
|
|
9815
|
+
|
|
9816
|
+
// lib/connections/vendors/brightdata.js
|
|
9817
|
+
var brightdata_default2 = {
|
|
9818
|
+
fields: [
|
|
9819
|
+
{
|
|
9820
|
+
credential: "BRIGHTDATA_BROWSER_URI",
|
|
9821
|
+
input: "password",
|
|
9822
|
+
key: "browserUri",
|
|
9823
|
+
// A wss:// endpoint with the account's user and password in its
|
|
9824
|
+
// authority, which is why it is a password field rather than a url one:
|
|
9825
|
+
// the whole string is the credential.
|
|
9826
|
+
label: "Scraping Browser endpoint",
|
|
9827
|
+
message: "The wss:// Scraping Browser endpoint, carrying its own credentials. Used to render pages that need a real browser.",
|
|
9828
|
+
redact: true,
|
|
9829
|
+
required: true
|
|
9830
|
+
},
|
|
9831
|
+
{
|
|
9832
|
+
credential: "BRIGHTDATA_UNLOCKER_API_KEY",
|
|
9833
|
+
input: "password",
|
|
9834
|
+
key: "unlockerApiKey",
|
|
9835
|
+
label: "Web Unlocker API key",
|
|
9836
|
+
// NO EXPIRY FIELD, and that is a decision rather than an omission. Bright
|
|
9837
|
+
// Data offers an `Unlimited` expiry and recommends against it; Drawbridge
|
|
9838
|
+
// takes the unlimited key deliberately, because there is no way to rotate
|
|
9839
|
+
// this programmatically and a date nothing can act on is a date that only
|
|
9840
|
+
// surfaces after it has already broken scraping. If a fixed expiry is ever
|
|
9841
|
+
// taken instead, the field to add is a stored date and a warning ahead of
|
|
9842
|
+
// it — not a rotation, which the vendor cannot support.
|
|
9843
|
+
message: "Bright Data account settings \u2192 API keys. Taken with an unlimited expiry: their API offers no way to rotate a key, so a dated one can only ever break scraping silently.",
|
|
9844
|
+
redact: true,
|
|
9845
|
+
required: true
|
|
9846
|
+
},
|
|
9847
|
+
{
|
|
9848
|
+
credential: "BRIGHTDATA_UNLOCKER_ZONE",
|
|
9849
|
+
input: "text",
|
|
9850
|
+
key: "unlockerZone",
|
|
9851
|
+
label: "Web Unlocker zone",
|
|
9852
|
+
// Not secret — a zone is a label on the account, and it is shown rather
|
|
9853
|
+
// than redacted so an operator can confirm which one is in use without
|
|
9854
|
+
// clearing the field to read it.
|
|
9855
|
+
message: "The zone name the Unlocker requests run against.",
|
|
9856
|
+
required: true
|
|
9857
|
+
}
|
|
9858
|
+
],
|
|
9859
|
+
icon: brightdata_default,
|
|
9860
|
+
name: "Bright Data",
|
|
9861
|
+
slug: "brightdata",
|
|
9862
|
+
// WHERE THE FACTS CAME FROM, read 2026-09-16.
|
|
9863
|
+
urls: {
|
|
9864
|
+
api: "https://docs.brightdata.com/api-reference/authentication",
|
|
9865
|
+
dashboard: "https://brightdata.com/cp/setting/users",
|
|
9866
|
+
// Bright Data has no scope model — a key carries the account's own access —
|
|
9867
|
+
// so there is nothing to link. `false` is the recorded answer rather than a
|
|
9868
|
+
// missing key.
|
|
9869
|
+
scopes: false
|
|
9870
|
+
}
|
|
9871
|
+
};
|
|
9872
|
+
|
|
9759
9873
|
// lib/connections/vendors/hubspot.js
|
|
9760
9874
|
var hubspot_default2 = {
|
|
9761
9875
|
// WHAT AN ADMIN TYPES on the provider screen. `credential` names the env var a
|
|
@@ -10131,7 +10245,7 @@ var checkIcon = (owner, icon) => {
|
|
|
10131
10245
|
}
|
|
10132
10246
|
};
|
|
10133
10247
|
var vendors = Object.freeze(Object.fromEntries(
|
|
10134
|
-
[attentive_default3, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
10248
|
+
[attentive_default3, brightdata_default2, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
10135
10249
|
));
|
|
10136
10250
|
var buildVendor = (vendor) => {
|
|
10137
10251
|
if (!(vendor == null ? void 0 : vendor.slug)) throw new Error("A vendor needs a slug");
|
|
@@ -10158,7 +10272,7 @@ for (const [slug2, vendor] of Object.entries(vendors)) {
|
|
|
10158
10272
|
buildVendor(vendor);
|
|
10159
10273
|
}
|
|
10160
10274
|
var build = (manifest) => {
|
|
10161
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
|
|
10275
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
10162
10276
|
if (!(manifest == null ? void 0 : manifest.slug)) throw new Error("A connection needs a slug");
|
|
10163
10277
|
if (!(manifest == null ? void 0 : manifest.title)) throw new Error(manifest.slug + " needs a title");
|
|
10164
10278
|
if (manifest == null ? void 0 : manifest.private) throw new Error(manifest.slug + " declares private \u2014 that is feature : false now");
|
|
@@ -10424,11 +10538,22 @@ var build = (manifest) => {
|
|
|
10424
10538
|
manifest.slug + " step " + type2 + " may be triggered by " + trigger + ", which is not a trigger \u2014 one of " + Object.keys(TRIGGERS).join(", ")
|
|
10425
10539
|
);
|
|
10426
10540
|
}
|
|
10427
|
-
if (declared2.
|
|
10428
|
-
|
|
10541
|
+
if (declared2.workflow) {
|
|
10542
|
+
if (!declared2.workflow.key) {
|
|
10543
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no key \u2014 the title several steps share");
|
|
10544
|
+
}
|
|
10545
|
+
if (typeof declared2.workflow.order !== "number") {
|
|
10546
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no order \u2014 two steps in one row need a sequence");
|
|
10547
|
+
}
|
|
10548
|
+
if (declared2.trigger) {
|
|
10549
|
+
throw new Error(manifest.slug + " step " + type2 + " declares BOTH a trigger and a workflow \u2014 the workflow carries the trigger for every step in it");
|
|
10550
|
+
}
|
|
10551
|
+
}
|
|
10552
|
+
for (const candidate of [declared2.trigger, (_y = declared2.workflow) == null ? void 0 : _y.trigger].filter(Boolean)) {
|
|
10553
|
+
const known2 = TRIGGERS[Object.keys(TRIGGERS).find((key) => TRIGGERS[key].event === candidate.event && TRIGGERS[key].type === candidate.type)];
|
|
10429
10554
|
if (!known2) {
|
|
10430
10555
|
throw new Error(
|
|
10431
|
-
manifest.slug + " step " + type2 + " declares trigger { event : " +
|
|
10556
|
+
manifest.slug + " step " + type2 + " declares trigger { event : " + candidate.event + ", type : " + candidate.type + " }, which nothing dispatches"
|
|
10432
10557
|
);
|
|
10433
10558
|
}
|
|
10434
10559
|
}
|