@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/providers.cjs
CHANGED
|
@@ -496,6 +496,16 @@ var TRIGGERS = Object.freeze({
|
|
|
496
496
|
"range.ended": Object.freeze({ event: "range.ended", type: "range" }),
|
|
497
497
|
"range.started": Object.freeze({ event: "range.started", type: "range" }),
|
|
498
498
|
"schedule.day": Object.freeze({ event: "day", type: "schedule" }),
|
|
499
|
+
// THE THREE SHOPIFY TRIGGERS, which were never in this vocabulary because the
|
|
500
|
+
// four workflows they fire were hand-written in drawbridge-sync's register job
|
|
501
|
+
// rather than provisioned from the manifest. `webhook` is a fourth trigger
|
|
502
|
+
// TYPE, and it is the honest name: these fire when the vendor calls us, which
|
|
503
|
+
// is neither an event Drawbridge raised nor a schedule it keeps.
|
|
504
|
+
"shopify.order.create": Object.freeze({ event: "shopify.order.create", type: "webhook" }),
|
|
505
|
+
"shopify.product.update": Object.freeze({ event: "shopify.product.update", type: "webhook" }),
|
|
506
|
+
// The token audit, dispatched programmatically by the register job when a
|
|
507
|
+
// store's grant is exchanged or rotated.
|
|
508
|
+
"shopify.token": Object.freeze({ event: "shopify.token", type: "event" }),
|
|
499
509
|
"schedule.month": Object.freeze({ event: "month", type: "schedule" }),
|
|
500
510
|
"schedule.week": Object.freeze({ event: "week", type: "schedule" }),
|
|
501
511
|
"segment.contact.add": Object.freeze({ event: "segment.contact.add", type: "event" }),
|
|
@@ -548,7 +558,7 @@ var OUTCOMES = Object.freeze({
|
|
|
548
558
|
unimplemented: "unimplemented",
|
|
549
559
|
unsupported: "unsupported"
|
|
550
560
|
});
|
|
551
|
-
var STATUSES = Object.freeze(["active", "
|
|
561
|
+
var STATUSES = Object.freeze(["active", "error", "pending"]);
|
|
552
562
|
var PLATFORM_CREDENTIALS = Object.freeze([
|
|
553
563
|
// Seals a merchant's stored connection settings. The webhook connection is
|
|
554
564
|
// gated on it because its whole credential is a secret WE mint and store, so
|
|
@@ -5529,7 +5539,7 @@ var getAppToken = async ({ clientId, clientSecret, fetcher }) => {
|
|
|
5529
5539
|
return fetchAppToken({ clientId, clientSecret, fetcher });
|
|
5530
5540
|
};
|
|
5531
5541
|
var toShopGid = (shopId) => String(shopId).startsWith("gid://") ? String(shopId) : "gid://shopify/Shop/" + shopId;
|
|
5532
|
-
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference, revision, shopId, timestamp, value }) => {
|
|
5542
|
+
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference: reference2, revision, shopId, timestamp, value }) => {
|
|
5533
5543
|
if (!shopId || !eventHandle || !(Number(value) > 0)) {
|
|
5534
5544
|
throw new Error("sendAppEvent requires shopId, eventHandle, and a positive value");
|
|
5535
5545
|
}
|
|
@@ -5543,8 +5553,8 @@ var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempo
|
|
|
5543
5553
|
const body = {
|
|
5544
5554
|
attributes: {
|
|
5545
5555
|
value: Number(value),
|
|
5546
|
-
...
|
|
5547
|
-
reference: String(
|
|
5556
|
+
...reference2 && {
|
|
5557
|
+
reference: String(reference2).slice(0, 128)
|
|
5548
5558
|
}
|
|
5549
5559
|
},
|
|
5550
5560
|
event_handle: eventHandle,
|
|
@@ -7705,6 +7715,12 @@ var shopify_default2 = {
|
|
|
7705
7715
|
orderId: String(orderId),
|
|
7706
7716
|
rate,
|
|
7707
7717
|
shopId: connection.source.id,
|
|
7718
|
+
// WHICH VENDOR IS BEING CHARGED, so the sender does not have
|
|
7719
|
+
// to assume. queue/usage.js named the slug itself and
|
|
7720
|
+
// defaults to it for anything already queued when this
|
|
7721
|
+
// shipped — an app-store vendor billing through its own meter
|
|
7722
|
+
// enqueues the same job with its own slug.
|
|
7723
|
+
slug: connection.slug,
|
|
7708
7724
|
// The App Events API returns no event id, so one is generated
|
|
7709
7725
|
// here — the event handle plus the order id — and sent as the
|
|
7710
7726
|
// event's `reference`. queue/usage.js stamps the same id onto
|
|
@@ -8928,6 +8944,13 @@ var shopify_default2 = {
|
|
|
8928
8944
|
key: "Shopify Order Tracking",
|
|
8929
8945
|
queue: "connection",
|
|
8930
8946
|
system: true,
|
|
8947
|
+
// THE TRIGGER THE REGISTER JOB HAND-WROTE. It is byte-identical to
|
|
8948
|
+
// the one on every stored workflow, because these documents exist on
|
|
8949
|
+
// prod with thousands of runs behind them and provisioning is
|
|
8950
|
+
// idempotent on (connection, system, title) — a different trigger
|
|
8951
|
+
// here would not be picked up by the existing rows anyway, and a
|
|
8952
|
+
// different TITLE would provision a second workflow for every store.
|
|
8953
|
+
trigger: { event: "shopify.order.create", type: "webhook" },
|
|
8931
8954
|
type: "step.commerce.order.record"
|
|
8932
8955
|
})
|
|
8933
8956
|
},
|
|
@@ -8938,6 +8961,15 @@ var shopify_default2 = {
|
|
|
8938
8961
|
key: "Shopify Product Sync",
|
|
8939
8962
|
queue: "connection",
|
|
8940
8963
|
system: true,
|
|
8964
|
+
// `shopify.product.update`, NOT `product.insert`. The plan named the
|
|
8965
|
+
// latter, and it is the right long-term answer — a Drawbridge product
|
|
8966
|
+
// row appearing should pull the vendor's copy, rather than
|
|
8967
|
+
// stream/product.js branching on the slug and enqueueing a
|
|
8968
|
+
// vendor-named queue. But a workflow carries ONE trigger, this one is
|
|
8969
|
+
// stored on prod against the webhook, and changing a live workflow's
|
|
8970
|
+
// trigger is a data migration. So the declaration matches what exists
|
|
8971
|
+
// and `product.insert` stays a separate, migration-gated change.
|
|
8972
|
+
trigger: { event: "shopify.product.update", type: "webhook" },
|
|
8941
8973
|
type: "step.commerce.product.sync"
|
|
8942
8974
|
})
|
|
8943
8975
|
}
|
|
@@ -8959,26 +8991,37 @@ var shopify_default2 = {
|
|
|
8959
8991
|
type: "step.connection.health.check"
|
|
8960
8992
|
})
|
|
8961
8993
|
},
|
|
8962
|
-
//
|
|
8963
|
-
//
|
|
8964
|
-
//
|
|
8965
|
-
//
|
|
8966
|
-
//
|
|
8967
|
-
//
|
|
8994
|
+
// THE ONE WORKFLOW THAT CARRIES TWO STEPS, and the shape the plan called
|
|
8995
|
+
// the single genuinely new thing in it.
|
|
8996
|
+
//
|
|
8997
|
+
// Every other system step is its own workflow, titled by its own `key`.
|
|
8998
|
+
// These two are "Shopify Token Activity", one row in the merchant's list
|
|
8999
|
+
// with both steps in it — so they declare `workflow`, which names the
|
|
9000
|
+
// title, the trigger and the ORDER, and the provisioner groups on it.
|
|
9001
|
+
// Without that key the pair could only be provisioned by the hand-written
|
|
9002
|
+
// spec in drawbridge-sync's register job, which is exactly why that spec
|
|
9003
|
+
// outlived every other one.
|
|
9004
|
+
//
|
|
9005
|
+
// AUDIT-ONLY. The step documents are written manually at OAuth time and
|
|
9006
|
+
// the workflow is never dispatched; `queue` is declared defensively so
|
|
9007
|
+
// that if it ever IS, the job lands on a real queue and the handler lookup
|
|
9008
|
+
// misses cleanly rather than throwing "Unknown step type".
|
|
8968
9009
|
token: {
|
|
8969
9010
|
exchange: () => ({
|
|
8970
9011
|
description: "Records the token exchange that completed an install. Audit only \u2014 never dispatched.",
|
|
8971
9012
|
key: "Shopify Token Exchange",
|
|
8972
9013
|
queue: "connection",
|
|
8973
9014
|
system: true,
|
|
8974
|
-
type: "step.connection.token.exchange"
|
|
9015
|
+
type: "step.connection.token.exchange",
|
|
9016
|
+
workflow: { key: "Shopify Token Activity", order: 0, trigger: { event: "shopify.token", type: "event" } }
|
|
8975
9017
|
}),
|
|
8976
9018
|
refresh: () => ({
|
|
8977
9019
|
description: "Records a token rotation. Audit only \u2014 never dispatched.",
|
|
8978
9020
|
key: "Shopify Token Refresh",
|
|
8979
9021
|
queue: "connection",
|
|
8980
9022
|
system: true,
|
|
8981
|
-
type: "step.connection.token.refresh"
|
|
9023
|
+
type: "step.connection.token.refresh",
|
|
9024
|
+
workflow: { key: "Shopify Token Activity", order: 1, trigger: { event: "shopify.token", type: "event" } }
|
|
8982
9025
|
})
|
|
8983
9026
|
}
|
|
8984
9027
|
}
|
|
@@ -9434,7 +9477,7 @@ var webhook_default = {
|
|
|
9434
9477
|
// The connect prompt only where connecting is not already the card's whole
|
|
9435
9478
|
// story: a disconnected or errored document renders a Connect action itself,
|
|
9436
9479
|
// and a task repeating it talks over the button.
|
|
9437
|
-
tasks: ({ settings, status } = {}) =>
|
|
9480
|
+
tasks: ({ settings, status } = {}) => status === "error" ? [] : (settings == null ? void 0 : settings.secret) ? [
|
|
9438
9481
|
{
|
|
9439
9482
|
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.',
|
|
9440
9483
|
title: "Requests must be verified",
|
|
@@ -9521,6 +9564,77 @@ var attentive_default3 = {
|
|
|
9521
9564
|
}
|
|
9522
9565
|
};
|
|
9523
9566
|
|
|
9567
|
+
// lib/connections/icons/brightdata.js
|
|
9568
|
+
var brightdata_default = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
9569
|
+
<rect width="500" height="500" fill="#0F58FF"/>
|
|
9570
|
+
<circle cx="250" cy="250" r="52" fill="white"/>
|
|
9571
|
+
<rect x="234" y="70" width="32" height="96" rx="16" fill="white"/>
|
|
9572
|
+
<rect x="234" y="334" width="32" height="96" rx="16" fill="white"/>
|
|
9573
|
+
<rect x="70" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
9574
|
+
<rect x="334" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
9575
|
+
<rect x="117" y="140" width="32" height="96" rx="16" transform="rotate(-45 117 140)" fill="white"/>
|
|
9576
|
+
<rect x="304" y="327" width="32" height="96" rx="16" transform="rotate(-45 304 327)" fill="white"/>
|
|
9577
|
+
<rect x="360" y="117" width="32" height="96" rx="16" transform="rotate(45 360 117)" fill="white"/>
|
|
9578
|
+
<rect x="173" y="304" width="32" height="96" rx="16" transform="rotate(45 173 304)" fill="white"/>
|
|
9579
|
+
</svg>`;
|
|
9580
|
+
|
|
9581
|
+
// lib/connections/vendors/brightdata.js
|
|
9582
|
+
var brightdata_default2 = {
|
|
9583
|
+
fields: [
|
|
9584
|
+
{
|
|
9585
|
+
credential: "BRIGHTDATA_BROWSER_URI",
|
|
9586
|
+
input: "password",
|
|
9587
|
+
key: "browserUri",
|
|
9588
|
+
// A wss:// endpoint with the account's user and password in its
|
|
9589
|
+
// authority, which is why it is a password field rather than a url one:
|
|
9590
|
+
// the whole string is the credential.
|
|
9591
|
+
label: "Scraping Browser endpoint",
|
|
9592
|
+
message: "The wss:// Scraping Browser endpoint, carrying its own credentials. Used to render pages that need a real browser.",
|
|
9593
|
+
redact: true,
|
|
9594
|
+
required: true
|
|
9595
|
+
},
|
|
9596
|
+
{
|
|
9597
|
+
credential: "BRIGHTDATA_UNLOCKER_API_KEY",
|
|
9598
|
+
input: "password",
|
|
9599
|
+
key: "unlockerApiKey",
|
|
9600
|
+
label: "Web Unlocker API key",
|
|
9601
|
+
// NO EXPIRY FIELD, and that is a decision rather than an omission. Bright
|
|
9602
|
+
// Data offers an `Unlimited` expiry and recommends against it; Drawbridge
|
|
9603
|
+
// takes the unlimited key deliberately, because there is no way to rotate
|
|
9604
|
+
// this programmatically and a date nothing can act on is a date that only
|
|
9605
|
+
// surfaces after it has already broken scraping. If a fixed expiry is ever
|
|
9606
|
+
// taken instead, the field to add is a stored date and a warning ahead of
|
|
9607
|
+
// it — not a rotation, which the vendor cannot support.
|
|
9608
|
+
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.",
|
|
9609
|
+
redact: true,
|
|
9610
|
+
required: true
|
|
9611
|
+
},
|
|
9612
|
+
{
|
|
9613
|
+
credential: "BRIGHTDATA_UNLOCKER_ZONE",
|
|
9614
|
+
input: "text",
|
|
9615
|
+
key: "unlockerZone",
|
|
9616
|
+
label: "Web Unlocker zone",
|
|
9617
|
+
// Not secret — a zone is a label on the account, and it is shown rather
|
|
9618
|
+
// than redacted so an operator can confirm which one is in use without
|
|
9619
|
+
// clearing the field to read it.
|
|
9620
|
+
message: "The zone name the Unlocker requests run against.",
|
|
9621
|
+
required: true
|
|
9622
|
+
}
|
|
9623
|
+
],
|
|
9624
|
+
icon: brightdata_default,
|
|
9625
|
+
name: "Bright Data",
|
|
9626
|
+
slug: "brightdata",
|
|
9627
|
+
// WHERE THE FACTS CAME FROM, read 2026-09-16.
|
|
9628
|
+
urls: {
|
|
9629
|
+
api: "https://docs.brightdata.com/api-reference/authentication",
|
|
9630
|
+
dashboard: "https://brightdata.com/cp/setting/users",
|
|
9631
|
+
// Bright Data has no scope model — a key carries the account's own access —
|
|
9632
|
+
// so there is nothing to link. `false` is the recorded answer rather than a
|
|
9633
|
+
// missing key.
|
|
9634
|
+
scopes: false
|
|
9635
|
+
}
|
|
9636
|
+
};
|
|
9637
|
+
|
|
9524
9638
|
// lib/connections/vendors/hubspot.js
|
|
9525
9639
|
var hubspot_default2 = {
|
|
9526
9640
|
// WHAT AN ADMIN TYPES on the provider screen. `credential` names the env var a
|
|
@@ -9896,7 +10010,7 @@ var checkIcon = (owner, icon) => {
|
|
|
9896
10010
|
}
|
|
9897
10011
|
};
|
|
9898
10012
|
var vendors = Object.freeze(Object.fromEntries(
|
|
9899
|
-
[attentive_default3, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
10013
|
+
[attentive_default3, brightdata_default2, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
9900
10014
|
));
|
|
9901
10015
|
var buildVendor = (vendor) => {
|
|
9902
10016
|
if (!(vendor == null ? void 0 : vendor.slug)) throw new Error("A vendor needs a slug");
|
|
@@ -9923,7 +10037,7 @@ for (const [slug2, vendor] of Object.entries(vendors)) {
|
|
|
9923
10037
|
buildVendor(vendor);
|
|
9924
10038
|
}
|
|
9925
10039
|
var build = (manifest) => {
|
|
9926
|
-
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;
|
|
10040
|
+
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;
|
|
9927
10041
|
if (!(manifest == null ? void 0 : manifest.slug)) throw new Error("A connection needs a slug");
|
|
9928
10042
|
if (!(manifest == null ? void 0 : manifest.title)) throw new Error(manifest.slug + " needs a title");
|
|
9929
10043
|
if (manifest == null ? void 0 : manifest.private) throw new Error(manifest.slug + " declares private \u2014 that is feature : false now");
|
|
@@ -10189,11 +10303,22 @@ var build = (manifest) => {
|
|
|
10189
10303
|
manifest.slug + " step " + type2 + " may be triggered by " + trigger + ", which is not a trigger \u2014 one of " + Object.keys(TRIGGERS).join(", ")
|
|
10190
10304
|
);
|
|
10191
10305
|
}
|
|
10192
|
-
if (declared2.
|
|
10193
|
-
|
|
10306
|
+
if (declared2.workflow) {
|
|
10307
|
+
if (!declared2.workflow.key) {
|
|
10308
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no key \u2014 the title several steps share");
|
|
10309
|
+
}
|
|
10310
|
+
if (typeof declared2.workflow.order !== "number") {
|
|
10311
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no order \u2014 two steps in one row need a sequence");
|
|
10312
|
+
}
|
|
10313
|
+
if (declared2.trigger) {
|
|
10314
|
+
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");
|
|
10315
|
+
}
|
|
10316
|
+
}
|
|
10317
|
+
for (const candidate of [declared2.trigger, (_y = declared2.workflow) == null ? void 0 : _y.trigger].filter(Boolean)) {
|
|
10318
|
+
const known2 = TRIGGERS[Object.keys(TRIGGERS).find((key) => TRIGGERS[key].event === candidate.event && TRIGGERS[key].type === candidate.type)];
|
|
10194
10319
|
if (!known2) {
|
|
10195
10320
|
throw new Error(
|
|
10196
|
-
manifest.slug + " step " + type2 + " declares trigger { event : " +
|
|
10321
|
+
manifest.slug + " step " + type2 + " declares trigger { event : " + candidate.event + ", type : " + candidate.type + " }, which nothing dispatches"
|
|
10197
10322
|
);
|
|
10198
10323
|
}
|
|
10199
10324
|
}
|
package/dist/providers.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a8 as vendors, K as mergeSettings, y as connections } from './index-C2rxasGZ.cjs';
|
|
2
2
|
import { decrypt, encrypt } from './encrypt.cjs';
|
|
3
3
|
import { slugify } from './slugify.cjs';
|
|
4
4
|
import './usage.cjs';
|
package/dist/providers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a8 as vendors, K as mergeSettings, y as connections } from './index-DOYCXtd7.js';
|
|
2
2
|
import { decrypt, encrypt } from './encrypt.js';
|
|
3
3
|
import { slugify } from './slugify.js';
|
|
4
4
|
import './usage.js';
|
package/dist/providers.js
CHANGED
|
@@ -453,6 +453,16 @@ var TRIGGERS = Object.freeze({
|
|
|
453
453
|
"range.ended": Object.freeze({ event: "range.ended", type: "range" }),
|
|
454
454
|
"range.started": Object.freeze({ event: "range.started", type: "range" }),
|
|
455
455
|
"schedule.day": Object.freeze({ event: "day", type: "schedule" }),
|
|
456
|
+
// THE THREE SHOPIFY TRIGGERS, which were never in this vocabulary because the
|
|
457
|
+
// four workflows they fire were hand-written in drawbridge-sync's register job
|
|
458
|
+
// rather than provisioned from the manifest. `webhook` is a fourth trigger
|
|
459
|
+
// TYPE, and it is the honest name: these fire when the vendor calls us, which
|
|
460
|
+
// is neither an event Drawbridge raised nor a schedule it keeps.
|
|
461
|
+
"shopify.order.create": Object.freeze({ event: "shopify.order.create", type: "webhook" }),
|
|
462
|
+
"shopify.product.update": Object.freeze({ event: "shopify.product.update", type: "webhook" }),
|
|
463
|
+
// The token audit, dispatched programmatically by the register job when a
|
|
464
|
+
// store's grant is exchanged or rotated.
|
|
465
|
+
"shopify.token": Object.freeze({ event: "shopify.token", type: "event" }),
|
|
456
466
|
"schedule.month": Object.freeze({ event: "month", type: "schedule" }),
|
|
457
467
|
"schedule.week": Object.freeze({ event: "week", type: "schedule" }),
|
|
458
468
|
"segment.contact.add": Object.freeze({ event: "segment.contact.add", type: "event" }),
|
|
@@ -505,7 +515,7 @@ var OUTCOMES = Object.freeze({
|
|
|
505
515
|
unimplemented: "unimplemented",
|
|
506
516
|
unsupported: "unsupported"
|
|
507
517
|
});
|
|
508
|
-
var STATUSES = Object.freeze(["active", "
|
|
518
|
+
var STATUSES = Object.freeze(["active", "error", "pending"]);
|
|
509
519
|
var PLATFORM_CREDENTIALS = Object.freeze([
|
|
510
520
|
// Seals a merchant's stored connection settings. The webhook connection is
|
|
511
521
|
// gated on it because its whole credential is a secret WE mint and store, so
|
|
@@ -5486,7 +5496,7 @@ var getAppToken = async ({ clientId, clientSecret, fetcher }) => {
|
|
|
5486
5496
|
return fetchAppToken({ clientId, clientSecret, fetcher });
|
|
5487
5497
|
};
|
|
5488
5498
|
var toShopGid = (shopId) => String(shopId).startsWith("gid://") ? String(shopId) : "gid://shopify/Shop/" + shopId;
|
|
5489
|
-
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference, revision, shopId, timestamp, value }) => {
|
|
5499
|
+
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference: reference2, revision, shopId, timestamp, value }) => {
|
|
5490
5500
|
if (!shopId || !eventHandle || !(Number(value) > 0)) {
|
|
5491
5501
|
throw new Error("sendAppEvent requires shopId, eventHandle, and a positive value");
|
|
5492
5502
|
}
|
|
@@ -5500,8 +5510,8 @@ var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempo
|
|
|
5500
5510
|
const body = {
|
|
5501
5511
|
attributes: {
|
|
5502
5512
|
value: Number(value),
|
|
5503
|
-
...
|
|
5504
|
-
reference: String(
|
|
5513
|
+
...reference2 && {
|
|
5514
|
+
reference: String(reference2).slice(0, 128)
|
|
5505
5515
|
}
|
|
5506
5516
|
},
|
|
5507
5517
|
event_handle: eventHandle,
|
|
@@ -7662,6 +7672,12 @@ var shopify_default2 = {
|
|
|
7662
7672
|
orderId: String(orderId),
|
|
7663
7673
|
rate,
|
|
7664
7674
|
shopId: connection.source.id,
|
|
7675
|
+
// WHICH VENDOR IS BEING CHARGED, so the sender does not have
|
|
7676
|
+
// to assume. queue/usage.js named the slug itself and
|
|
7677
|
+
// defaults to it for anything already queued when this
|
|
7678
|
+
// shipped — an app-store vendor billing through its own meter
|
|
7679
|
+
// enqueues the same job with its own slug.
|
|
7680
|
+
slug: connection.slug,
|
|
7665
7681
|
// The App Events API returns no event id, so one is generated
|
|
7666
7682
|
// here — the event handle plus the order id — and sent as the
|
|
7667
7683
|
// event's `reference`. queue/usage.js stamps the same id onto
|
|
@@ -8885,6 +8901,13 @@ var shopify_default2 = {
|
|
|
8885
8901
|
key: "Shopify Order Tracking",
|
|
8886
8902
|
queue: "connection",
|
|
8887
8903
|
system: true,
|
|
8904
|
+
// THE TRIGGER THE REGISTER JOB HAND-WROTE. It is byte-identical to
|
|
8905
|
+
// the one on every stored workflow, because these documents exist on
|
|
8906
|
+
// prod with thousands of runs behind them and provisioning is
|
|
8907
|
+
// idempotent on (connection, system, title) — a different trigger
|
|
8908
|
+
// here would not be picked up by the existing rows anyway, and a
|
|
8909
|
+
// different TITLE would provision a second workflow for every store.
|
|
8910
|
+
trigger: { event: "shopify.order.create", type: "webhook" },
|
|
8888
8911
|
type: "step.commerce.order.record"
|
|
8889
8912
|
})
|
|
8890
8913
|
},
|
|
@@ -8895,6 +8918,15 @@ var shopify_default2 = {
|
|
|
8895
8918
|
key: "Shopify Product Sync",
|
|
8896
8919
|
queue: "connection",
|
|
8897
8920
|
system: true,
|
|
8921
|
+
// `shopify.product.update`, NOT `product.insert`. The plan named the
|
|
8922
|
+
// latter, and it is the right long-term answer — a Drawbridge product
|
|
8923
|
+
// row appearing should pull the vendor's copy, rather than
|
|
8924
|
+
// stream/product.js branching on the slug and enqueueing a
|
|
8925
|
+
// vendor-named queue. But a workflow carries ONE trigger, this one is
|
|
8926
|
+
// stored on prod against the webhook, and changing a live workflow's
|
|
8927
|
+
// trigger is a data migration. So the declaration matches what exists
|
|
8928
|
+
// and `product.insert` stays a separate, migration-gated change.
|
|
8929
|
+
trigger: { event: "shopify.product.update", type: "webhook" },
|
|
8898
8930
|
type: "step.commerce.product.sync"
|
|
8899
8931
|
})
|
|
8900
8932
|
}
|
|
@@ -8916,26 +8948,37 @@ var shopify_default2 = {
|
|
|
8916
8948
|
type: "step.connection.health.check"
|
|
8917
8949
|
})
|
|
8918
8950
|
},
|
|
8919
|
-
//
|
|
8920
|
-
//
|
|
8921
|
-
//
|
|
8922
|
-
//
|
|
8923
|
-
//
|
|
8924
|
-
//
|
|
8951
|
+
// THE ONE WORKFLOW THAT CARRIES TWO STEPS, and the shape the plan called
|
|
8952
|
+
// the single genuinely new thing in it.
|
|
8953
|
+
//
|
|
8954
|
+
// Every other system step is its own workflow, titled by its own `key`.
|
|
8955
|
+
// These two are "Shopify Token Activity", one row in the merchant's list
|
|
8956
|
+
// with both steps in it — so they declare `workflow`, which names the
|
|
8957
|
+
// title, the trigger and the ORDER, and the provisioner groups on it.
|
|
8958
|
+
// Without that key the pair could only be provisioned by the hand-written
|
|
8959
|
+
// spec in drawbridge-sync's register job, which is exactly why that spec
|
|
8960
|
+
// outlived every other one.
|
|
8961
|
+
//
|
|
8962
|
+
// AUDIT-ONLY. The step documents are written manually at OAuth time and
|
|
8963
|
+
// the workflow is never dispatched; `queue` is declared defensively so
|
|
8964
|
+
// that if it ever IS, the job lands on a real queue and the handler lookup
|
|
8965
|
+
// misses cleanly rather than throwing "Unknown step type".
|
|
8925
8966
|
token: {
|
|
8926
8967
|
exchange: () => ({
|
|
8927
8968
|
description: "Records the token exchange that completed an install. Audit only \u2014 never dispatched.",
|
|
8928
8969
|
key: "Shopify Token Exchange",
|
|
8929
8970
|
queue: "connection",
|
|
8930
8971
|
system: true,
|
|
8931
|
-
type: "step.connection.token.exchange"
|
|
8972
|
+
type: "step.connection.token.exchange",
|
|
8973
|
+
workflow: { key: "Shopify Token Activity", order: 0, trigger: { event: "shopify.token", type: "event" } }
|
|
8932
8974
|
}),
|
|
8933
8975
|
refresh: () => ({
|
|
8934
8976
|
description: "Records a token rotation. Audit only \u2014 never dispatched.",
|
|
8935
8977
|
key: "Shopify Token Refresh",
|
|
8936
8978
|
queue: "connection",
|
|
8937
8979
|
system: true,
|
|
8938
|
-
type: "step.connection.token.refresh"
|
|
8980
|
+
type: "step.connection.token.refresh",
|
|
8981
|
+
workflow: { key: "Shopify Token Activity", order: 1, trigger: { event: "shopify.token", type: "event" } }
|
|
8939
8982
|
})
|
|
8940
8983
|
}
|
|
8941
8984
|
}
|
|
@@ -9391,7 +9434,7 @@ var webhook_default = {
|
|
|
9391
9434
|
// The connect prompt only where connecting is not already the card's whole
|
|
9392
9435
|
// story: a disconnected or errored document renders a Connect action itself,
|
|
9393
9436
|
// and a task repeating it talks over the button.
|
|
9394
|
-
tasks: ({ settings, status } = {}) =>
|
|
9437
|
+
tasks: ({ settings, status } = {}) => status === "error" ? [] : (settings == null ? void 0 : settings.secret) ? [
|
|
9395
9438
|
{
|
|
9396
9439
|
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.',
|
|
9397
9440
|
title: "Requests must be verified",
|
|
@@ -9478,6 +9521,77 @@ var attentive_default3 = {
|
|
|
9478
9521
|
}
|
|
9479
9522
|
};
|
|
9480
9523
|
|
|
9524
|
+
// lib/connections/icons/brightdata.js
|
|
9525
|
+
var brightdata_default = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
9526
|
+
<rect width="500" height="500" fill="#0F58FF"/>
|
|
9527
|
+
<circle cx="250" cy="250" r="52" fill="white"/>
|
|
9528
|
+
<rect x="234" y="70" width="32" height="96" rx="16" fill="white"/>
|
|
9529
|
+
<rect x="234" y="334" width="32" height="96" rx="16" fill="white"/>
|
|
9530
|
+
<rect x="70" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
9531
|
+
<rect x="334" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
9532
|
+
<rect x="117" y="140" width="32" height="96" rx="16" transform="rotate(-45 117 140)" fill="white"/>
|
|
9533
|
+
<rect x="304" y="327" width="32" height="96" rx="16" transform="rotate(-45 304 327)" fill="white"/>
|
|
9534
|
+
<rect x="360" y="117" width="32" height="96" rx="16" transform="rotate(45 360 117)" fill="white"/>
|
|
9535
|
+
<rect x="173" y="304" width="32" height="96" rx="16" transform="rotate(45 173 304)" fill="white"/>
|
|
9536
|
+
</svg>`;
|
|
9537
|
+
|
|
9538
|
+
// lib/connections/vendors/brightdata.js
|
|
9539
|
+
var brightdata_default2 = {
|
|
9540
|
+
fields: [
|
|
9541
|
+
{
|
|
9542
|
+
credential: "BRIGHTDATA_BROWSER_URI",
|
|
9543
|
+
input: "password",
|
|
9544
|
+
key: "browserUri",
|
|
9545
|
+
// A wss:// endpoint with the account's user and password in its
|
|
9546
|
+
// authority, which is why it is a password field rather than a url one:
|
|
9547
|
+
// the whole string is the credential.
|
|
9548
|
+
label: "Scraping Browser endpoint",
|
|
9549
|
+
message: "The wss:// Scraping Browser endpoint, carrying its own credentials. Used to render pages that need a real browser.",
|
|
9550
|
+
redact: true,
|
|
9551
|
+
required: true
|
|
9552
|
+
},
|
|
9553
|
+
{
|
|
9554
|
+
credential: "BRIGHTDATA_UNLOCKER_API_KEY",
|
|
9555
|
+
input: "password",
|
|
9556
|
+
key: "unlockerApiKey",
|
|
9557
|
+
label: "Web Unlocker API key",
|
|
9558
|
+
// NO EXPIRY FIELD, and that is a decision rather than an omission. Bright
|
|
9559
|
+
// Data offers an `Unlimited` expiry and recommends against it; Drawbridge
|
|
9560
|
+
// takes the unlimited key deliberately, because there is no way to rotate
|
|
9561
|
+
// this programmatically and a date nothing can act on is a date that only
|
|
9562
|
+
// surfaces after it has already broken scraping. If a fixed expiry is ever
|
|
9563
|
+
// taken instead, the field to add is a stored date and a warning ahead of
|
|
9564
|
+
// it — not a rotation, which the vendor cannot support.
|
|
9565
|
+
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.",
|
|
9566
|
+
redact: true,
|
|
9567
|
+
required: true
|
|
9568
|
+
},
|
|
9569
|
+
{
|
|
9570
|
+
credential: "BRIGHTDATA_UNLOCKER_ZONE",
|
|
9571
|
+
input: "text",
|
|
9572
|
+
key: "unlockerZone",
|
|
9573
|
+
label: "Web Unlocker zone",
|
|
9574
|
+
// Not secret — a zone is a label on the account, and it is shown rather
|
|
9575
|
+
// than redacted so an operator can confirm which one is in use without
|
|
9576
|
+
// clearing the field to read it.
|
|
9577
|
+
message: "The zone name the Unlocker requests run against.",
|
|
9578
|
+
required: true
|
|
9579
|
+
}
|
|
9580
|
+
],
|
|
9581
|
+
icon: brightdata_default,
|
|
9582
|
+
name: "Bright Data",
|
|
9583
|
+
slug: "brightdata",
|
|
9584
|
+
// WHERE THE FACTS CAME FROM, read 2026-09-16.
|
|
9585
|
+
urls: {
|
|
9586
|
+
api: "https://docs.brightdata.com/api-reference/authentication",
|
|
9587
|
+
dashboard: "https://brightdata.com/cp/setting/users",
|
|
9588
|
+
// Bright Data has no scope model — a key carries the account's own access —
|
|
9589
|
+
// so there is nothing to link. `false` is the recorded answer rather than a
|
|
9590
|
+
// missing key.
|
|
9591
|
+
scopes: false
|
|
9592
|
+
}
|
|
9593
|
+
};
|
|
9594
|
+
|
|
9481
9595
|
// lib/connections/vendors/hubspot.js
|
|
9482
9596
|
var hubspot_default2 = {
|
|
9483
9597
|
// WHAT AN ADMIN TYPES on the provider screen. `credential` names the env var a
|
|
@@ -9853,7 +9967,7 @@ var checkIcon = (owner, icon) => {
|
|
|
9853
9967
|
}
|
|
9854
9968
|
};
|
|
9855
9969
|
var vendors = Object.freeze(Object.fromEntries(
|
|
9856
|
-
[attentive_default3, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
9970
|
+
[attentive_default3, brightdata_default2, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
9857
9971
|
));
|
|
9858
9972
|
var buildVendor = (vendor) => {
|
|
9859
9973
|
if (!(vendor == null ? void 0 : vendor.slug)) throw new Error("A vendor needs a slug");
|
|
@@ -9880,7 +9994,7 @@ for (const [slug2, vendor] of Object.entries(vendors)) {
|
|
|
9880
9994
|
buildVendor(vendor);
|
|
9881
9995
|
}
|
|
9882
9996
|
var build = (manifest) => {
|
|
9883
|
-
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;
|
|
9997
|
+
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;
|
|
9884
9998
|
if (!(manifest == null ? void 0 : manifest.slug)) throw new Error("A connection needs a slug");
|
|
9885
9999
|
if (!(manifest == null ? void 0 : manifest.title)) throw new Error(manifest.slug + " needs a title");
|
|
9886
10000
|
if (manifest == null ? void 0 : manifest.private) throw new Error(manifest.slug + " declares private \u2014 that is feature : false now");
|
|
@@ -10146,11 +10260,22 @@ var build = (manifest) => {
|
|
|
10146
10260
|
manifest.slug + " step " + type2 + " may be triggered by " + trigger + ", which is not a trigger \u2014 one of " + Object.keys(TRIGGERS).join(", ")
|
|
10147
10261
|
);
|
|
10148
10262
|
}
|
|
10149
|
-
if (declared2.
|
|
10150
|
-
|
|
10263
|
+
if (declared2.workflow) {
|
|
10264
|
+
if (!declared2.workflow.key) {
|
|
10265
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no key \u2014 the title several steps share");
|
|
10266
|
+
}
|
|
10267
|
+
if (typeof declared2.workflow.order !== "number") {
|
|
10268
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no order \u2014 two steps in one row need a sequence");
|
|
10269
|
+
}
|
|
10270
|
+
if (declared2.trigger) {
|
|
10271
|
+
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");
|
|
10272
|
+
}
|
|
10273
|
+
}
|
|
10274
|
+
for (const candidate of [declared2.trigger, (_y = declared2.workflow) == null ? void 0 : _y.trigger].filter(Boolean)) {
|
|
10275
|
+
const known2 = TRIGGERS[Object.keys(TRIGGERS).find((key) => TRIGGERS[key].event === candidate.event && TRIGGERS[key].type === candidate.type)];
|
|
10151
10276
|
if (!known2) {
|
|
10152
10277
|
throw new Error(
|
|
10153
|
-
manifest.slug + " step " + type2 + " declares trigger { event : " +
|
|
10278
|
+
manifest.slug + " step " + type2 + " declares trigger { event : " + candidate.event + ", type : " + candidate.type + " }, which nothing dispatches"
|
|
10154
10279
|
);
|
|
10155
10280
|
}
|
|
10156
10281
|
}
|
package/package.json
CHANGED