@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
|
@@ -501,6 +501,16 @@ var TRIGGERS = Object.freeze({
|
|
|
501
501
|
"range.ended": Object.freeze({ event: "range.ended", type: "range" }),
|
|
502
502
|
"range.started": Object.freeze({ event: "range.started", type: "range" }),
|
|
503
503
|
"schedule.day": Object.freeze({ event: "day", type: "schedule" }),
|
|
504
|
+
// THE THREE SHOPIFY TRIGGERS, which were never in this vocabulary because the
|
|
505
|
+
// four workflows they fire were hand-written in drawbridge-sync's register job
|
|
506
|
+
// rather than provisioned from the manifest. `webhook` is a fourth trigger
|
|
507
|
+
// TYPE, and it is the honest name: these fire when the vendor calls us, which
|
|
508
|
+
// is neither an event Drawbridge raised nor a schedule it keeps.
|
|
509
|
+
"shopify.order.create": Object.freeze({ event: "shopify.order.create", type: "webhook" }),
|
|
510
|
+
"shopify.product.update": Object.freeze({ event: "shopify.product.update", type: "webhook" }),
|
|
511
|
+
// The token audit, dispatched programmatically by the register job when a
|
|
512
|
+
// store's grant is exchanged or rotated.
|
|
513
|
+
"shopify.token": Object.freeze({ event: "shopify.token", type: "event" }),
|
|
504
514
|
"schedule.month": Object.freeze({ event: "month", type: "schedule" }),
|
|
505
515
|
"schedule.week": Object.freeze({ event: "week", type: "schedule" }),
|
|
506
516
|
"segment.contact.add": Object.freeze({ event: "segment.contact.add", type: "event" }),
|
|
@@ -553,7 +563,7 @@ var OUTCOMES = Object.freeze({
|
|
|
553
563
|
unimplemented: "unimplemented",
|
|
554
564
|
unsupported: "unsupported"
|
|
555
565
|
});
|
|
556
|
-
var STATUSES = Object.freeze(["active", "
|
|
566
|
+
var STATUSES = Object.freeze(["active", "error", "pending"]);
|
|
557
567
|
var PLATFORM_CREDENTIALS = Object.freeze([
|
|
558
568
|
// Seals a merchant's stored connection settings. The webhook connection is
|
|
559
569
|
// gated on it because its whole credential is a secret WE mint and store, so
|
|
@@ -610,10 +620,7 @@ var ERROR_SOURCES = Object.freeze({
|
|
|
610
620
|
"shopify.register.webhooks": true
|
|
611
621
|
});
|
|
612
622
|
var bearing = (entry) => ERROR_SOURCES[entry == null ? void 0 : entry.source] !== false;
|
|
613
|
-
var assess = ({ errors = []
|
|
614
|
-
if (status === "disconnected") return status;
|
|
615
|
-
return (errors || []).some(bearing) ? "error" : "active";
|
|
616
|
-
};
|
|
623
|
+
var assess = ({ errors = [] } = {}) => (errors || []).some(bearing) ? "error" : "active";
|
|
617
624
|
var surviving = ({ errors = [], proved = [] } = {}) => (errors || []).filter((entry) => !proved.includes(entry == null ? void 0 : entry.source));
|
|
618
625
|
var isStatus = (status) => STATUSES.includes(status);
|
|
619
626
|
|
|
@@ -805,16 +812,28 @@ var systemSteps = (node, path = []) => Object.entries(node || {}).flatMap(
|
|
|
805
812
|
return value && typeof value === "object" ? systemSteps(value, [...path, key]) : [];
|
|
806
813
|
}
|
|
807
814
|
);
|
|
815
|
+
var systemWorkflows = (manifest) => {
|
|
816
|
+
var _a, _b, _c;
|
|
817
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
818
|
+
for (const [type, declared] of systemSteps(manifest == null ? void 0 : manifest.steps)) {
|
|
819
|
+
const trigger = ((_a = declared.workflow) == null ? void 0 : _a.trigger) || declared.trigger;
|
|
820
|
+
if (!(trigger == null ? void 0 : trigger.event) || !(trigger == null ? void 0 : trigger.type)) continue;
|
|
821
|
+
const title = ((_b = declared.workflow) == null ? void 0 : _b.key) || declared.key;
|
|
822
|
+
if (!title) continue;
|
|
823
|
+
if (!grouped.has(title)) grouped.set(title, { steps: [], title, trigger });
|
|
824
|
+
grouped.get(title).steps.push({ order: ((_c = declared.workflow) == null ? void 0 : _c.order) ?? 0, type });
|
|
825
|
+
}
|
|
826
|
+
return [...grouped.values()].map((workflow) => ({
|
|
827
|
+
...workflow,
|
|
828
|
+
steps: workflow.steps.slice().sort((a, b) => a.order - b.order)
|
|
829
|
+
}));
|
|
830
|
+
};
|
|
808
831
|
var ensureSystemWorkflows = async ({ connections: registry = connections, controller, doc }) => {
|
|
809
|
-
|
|
810
|
-
if (!(doc == null ? void 0 : doc.id) || doc.slug === "shopify") return [];
|
|
832
|
+
if (!(doc == null ? void 0 : doc.id)) return [];
|
|
811
833
|
const manifest = registry[doc.slug];
|
|
812
834
|
if (!manifest) return [];
|
|
813
835
|
const provisioned = [];
|
|
814
|
-
for (const
|
|
815
|
-
if (!((_a = declared.trigger) == null ? void 0 : _a.event) || !((_b = declared.trigger) == null ? void 0 : _b.type)) continue;
|
|
816
|
-
const title = declared.key;
|
|
817
|
-
if (!title) continue;
|
|
836
|
+
for (const { steps: members, title, trigger } of systemWorkflows(manifest)) {
|
|
818
837
|
const existing = await controller.count({
|
|
819
838
|
collection: "workflow",
|
|
820
839
|
query: {
|
|
@@ -831,13 +850,10 @@ var ensureSystemWorkflows = async ({ connections: registry = connections, contro
|
|
|
831
850
|
connection: doc.id,
|
|
832
851
|
organization: doc.organization,
|
|
833
852
|
status: "active",
|
|
834
|
-
steps:
|
|
835
|
-
settings: {},
|
|
836
|
-
type
|
|
837
|
-
}],
|
|
853
|
+
steps: members.map(({ type }) => ({ settings: {}, type })),
|
|
838
854
|
system: true,
|
|
839
855
|
title,
|
|
840
|
-
trigger
|
|
856
|
+
trigger
|
|
841
857
|
};
|
|
842
858
|
let created;
|
|
843
859
|
try {
|
|
@@ -854,6 +870,135 @@ var ensureSystemWorkflows = async ({ connections: registry = connections, contro
|
|
|
854
870
|
return provisioned;
|
|
855
871
|
};
|
|
856
872
|
|
|
873
|
+
// lib/connections/reference.js
|
|
874
|
+
var MARKER = {
|
|
875
|
+
end: "<!-- /generated -->",
|
|
876
|
+
start: "<!-- generated: drawbridge-utils lib/connections/reference.js \u2014 do not edit by hand -->"
|
|
877
|
+
};
|
|
878
|
+
var slugs = () => Object.keys(connections).sort();
|
|
879
|
+
var slots = (node = HOOKS, path = []) => Object.entries(node).flatMap(
|
|
880
|
+
([key, value]) => Array.isArray(value) ? value.map((verb) => [...path, key, verb].join(".")) : slots(value, [...path, key])
|
|
881
|
+
);
|
|
882
|
+
var implemented = (hooks, name) => {
|
|
883
|
+
const found = name.split(".").reduce(
|
|
884
|
+
(node, key) => node && typeof node === "object" && Object.hasOwn(node, key) ? node[key] : void 0,
|
|
885
|
+
hooks
|
|
886
|
+
);
|
|
887
|
+
if (typeof found === "function") return "yes";
|
|
888
|
+
if (found && typeof found === "object") return "elsewhere";
|
|
889
|
+
return "no";
|
|
890
|
+
};
|
|
891
|
+
var table = (header, rows) => [
|
|
892
|
+
"| " + header.join(" | ") + " |",
|
|
893
|
+
"|" + header.map(() => "---").join("|") + "|",
|
|
894
|
+
...rows.map((row2) => "| " + row2.join(" | ") + " |")
|
|
895
|
+
].join("\n");
|
|
896
|
+
var matrix = () => {
|
|
897
|
+
const columns = slugs();
|
|
898
|
+
return table(
|
|
899
|
+
["Hook", ...columns],
|
|
900
|
+
slots().map((name) => [
|
|
901
|
+
"`" + name + "`",
|
|
902
|
+
...columns.map((slug2) => implemented(connections[slug2].hooks, name))
|
|
903
|
+
])
|
|
904
|
+
);
|
|
905
|
+
};
|
|
906
|
+
var vocabularies = () => {
|
|
907
|
+
const list2 = (values) => values.map((value) => "`" + value + "`").join(", ");
|
|
908
|
+
return table(
|
|
909
|
+
["Vocabulary", "Values"],
|
|
910
|
+
[
|
|
911
|
+
["`AUTH_KINDS`", list2(AUTH_KINDS)],
|
|
912
|
+
["`GROUPS`", list2(GROUPS)],
|
|
913
|
+
["`INPUTS`", list2(INPUTS)],
|
|
914
|
+
["`STATUSES`", list2(STATUSES)],
|
|
915
|
+
["`TRIGGERS`", list2(Object.keys(TRIGGERS).sort())],
|
|
916
|
+
["`TRIGGER_TYPES`", list2(TRIGGER_TYPES)]
|
|
917
|
+
]
|
|
918
|
+
);
|
|
919
|
+
};
|
|
920
|
+
var steps = () => {
|
|
921
|
+
const rows = [];
|
|
922
|
+
const walk = (node, path, slug2) => {
|
|
923
|
+
var _a;
|
|
924
|
+
if (!node || typeof node !== "object") return;
|
|
925
|
+
for (const [key, value] of Object.entries(node)) {
|
|
926
|
+
if (typeof value === "function") {
|
|
927
|
+
let declared;
|
|
928
|
+
try {
|
|
929
|
+
declared = value({});
|
|
930
|
+
} catch {
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
if (!(declared == null ? void 0 : declared.type)) continue;
|
|
934
|
+
const fires = ((_a = declared.workflow) == null ? void 0 : _a.trigger) || declared.trigger;
|
|
935
|
+
rows.push([
|
|
936
|
+
"`" + declared.type + "`",
|
|
937
|
+
slug2,
|
|
938
|
+
declared.hook ? "`" + declared.hook + "`" : "\u2014",
|
|
939
|
+
declared.queue,
|
|
940
|
+
declared.system ? "system" : declared.withdrawn ? "withdrawn" : "builder",
|
|
941
|
+
fires ? "`" + fires.event + "` / `" + fires.type + "`" : (declared.triggers || []).map((t) => "`" + t + "`").join(", ") || "\u2014"
|
|
942
|
+
]);
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
walk(value, [...path, key], slug2);
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
for (const slug2 of slugs()) walk(connections[slug2].steps, [], slug2);
|
|
949
|
+
return table(["Step", "Connection", "Hook", "Queue", "Shape", "Fired by"], rows.sort((a, b) => a[0].localeCompare(b[0]) || a[1].localeCompare(b[1])));
|
|
950
|
+
};
|
|
951
|
+
var workflows = () => table(
|
|
952
|
+
["Workflow", "Connection", "Steps", "Trigger"],
|
|
953
|
+
slugs().flatMap((slug2) => systemWorkflows(connections[slug2]).map((workflow) => [
|
|
954
|
+
workflow.title,
|
|
955
|
+
slug2,
|
|
956
|
+
workflow.steps.map((step) => "`" + step.type + "`").join(" \u2192 "),
|
|
957
|
+
"`" + workflow.trigger.event + "` / `" + workflow.trigger.type + "`"
|
|
958
|
+
]))
|
|
959
|
+
);
|
|
960
|
+
var reference = () => [
|
|
961
|
+
MARKER.start,
|
|
962
|
+
"",
|
|
963
|
+
"### The support matrix",
|
|
964
|
+
"",
|
|
965
|
+
"`yes` is a body in this package. `elsewhere` is declared here and implemented in the repo holding the dependencies \u2014 a different fact from `no`, and collapsing the two is how a table tells a reader the package can do something it cannot.",
|
|
966
|
+
"",
|
|
967
|
+
matrix(),
|
|
968
|
+
"",
|
|
969
|
+
"### The closed vocabularies",
|
|
970
|
+
"",
|
|
971
|
+
vocabularies(),
|
|
972
|
+
"",
|
|
973
|
+
"### Every step",
|
|
974
|
+
"",
|
|
975
|
+
"A step with no hook is real: Shopify's token-audit pair is declared so a stray dispatch lands on a real queue, and is never fired.",
|
|
976
|
+
"",
|
|
977
|
+
steps(),
|
|
978
|
+
"",
|
|
979
|
+
"### The system workflows a connection is provisioned with",
|
|
980
|
+
"",
|
|
981
|
+
"Idempotent on (connection, system, title), so the TITLE is load-bearing \u2014 one that varied by account would provision a second workflow every time it changed.",
|
|
982
|
+
"",
|
|
983
|
+
workflows(),
|
|
984
|
+
"",
|
|
985
|
+
"### Counts",
|
|
986
|
+
"",
|
|
987
|
+
"- **" + slugs().length + "** connections",
|
|
988
|
+
"- **" + slots().length + "** hook slots",
|
|
989
|
+
"- **" + Object.keys(STEPS).length + "** step types",
|
|
990
|
+
"- **" + Object.keys(TRIGGERS).length + "** triggers",
|
|
991
|
+
"",
|
|
992
|
+
MARKER.end
|
|
993
|
+
].join("\n");
|
|
994
|
+
var spliced = (document) => {
|
|
995
|
+
const from = document.indexOf(MARKER.start);
|
|
996
|
+
const to = document.indexOf(MARKER.end);
|
|
997
|
+
if (from < 0 || to < 0) return document;
|
|
998
|
+
return document.slice(0, from) + reference() + document.slice(to + MARKER.end.length);
|
|
999
|
+
};
|
|
1000
|
+
var MARKERS = MARKER;
|
|
1001
|
+
|
|
857
1002
|
// lib/connections/oauth.js
|
|
858
1003
|
import { createHash, randomBytes } from "crypto";
|
|
859
1004
|
var credentials = ({ basic, clientId, clientSecret }) => basic ? {
|
|
@@ -5822,7 +5967,7 @@ var getAppToken = async ({ clientId, clientSecret, fetcher }) => {
|
|
|
5822
5967
|
return fetchAppToken({ clientId, clientSecret, fetcher });
|
|
5823
5968
|
};
|
|
5824
5969
|
var toShopGid = (shopId) => String(shopId).startsWith("gid://") ? String(shopId) : "gid://shopify/Shop/" + shopId;
|
|
5825
|
-
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference, revision, shopId, timestamp, value }) => {
|
|
5970
|
+
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference: reference2, revision, shopId, timestamp, value }) => {
|
|
5826
5971
|
if (!shopId || !eventHandle || !(Number(value) > 0)) {
|
|
5827
5972
|
throw new Error("sendAppEvent requires shopId, eventHandle, and a positive value");
|
|
5828
5973
|
}
|
|
@@ -5836,8 +5981,8 @@ var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempo
|
|
|
5836
5981
|
const body = {
|
|
5837
5982
|
attributes: {
|
|
5838
5983
|
value: Number(value),
|
|
5839
|
-
...
|
|
5840
|
-
reference: String(
|
|
5984
|
+
...reference2 && {
|
|
5985
|
+
reference: String(reference2).slice(0, 128)
|
|
5841
5986
|
}
|
|
5842
5987
|
},
|
|
5843
5988
|
event_handle: eventHandle,
|
|
@@ -7998,6 +8143,12 @@ var shopify_default2 = {
|
|
|
7998
8143
|
orderId: String(orderId),
|
|
7999
8144
|
rate,
|
|
8000
8145
|
shopId: connection.source.id,
|
|
8146
|
+
// WHICH VENDOR IS BEING CHARGED, so the sender does not have
|
|
8147
|
+
// to assume. queue/usage.js named the slug itself and
|
|
8148
|
+
// defaults to it for anything already queued when this
|
|
8149
|
+
// shipped — an app-store vendor billing through its own meter
|
|
8150
|
+
// enqueues the same job with its own slug.
|
|
8151
|
+
slug: connection.slug,
|
|
8001
8152
|
// The App Events API returns no event id, so one is generated
|
|
8002
8153
|
// here — the event handle plus the order id — and sent as the
|
|
8003
8154
|
// event's `reference`. queue/usage.js stamps the same id onto
|
|
@@ -9221,6 +9372,13 @@ var shopify_default2 = {
|
|
|
9221
9372
|
key: "Shopify Order Tracking",
|
|
9222
9373
|
queue: "connection",
|
|
9223
9374
|
system: true,
|
|
9375
|
+
// THE TRIGGER THE REGISTER JOB HAND-WROTE. It is byte-identical to
|
|
9376
|
+
// the one on every stored workflow, because these documents exist on
|
|
9377
|
+
// prod with thousands of runs behind them and provisioning is
|
|
9378
|
+
// idempotent on (connection, system, title) — a different trigger
|
|
9379
|
+
// here would not be picked up by the existing rows anyway, and a
|
|
9380
|
+
// different TITLE would provision a second workflow for every store.
|
|
9381
|
+
trigger: { event: "shopify.order.create", type: "webhook" },
|
|
9224
9382
|
type: "step.commerce.order.record"
|
|
9225
9383
|
})
|
|
9226
9384
|
},
|
|
@@ -9231,6 +9389,15 @@ var shopify_default2 = {
|
|
|
9231
9389
|
key: "Shopify Product Sync",
|
|
9232
9390
|
queue: "connection",
|
|
9233
9391
|
system: true,
|
|
9392
|
+
// `shopify.product.update`, NOT `product.insert`. The plan named the
|
|
9393
|
+
// latter, and it is the right long-term answer — a Drawbridge product
|
|
9394
|
+
// row appearing should pull the vendor's copy, rather than
|
|
9395
|
+
// stream/product.js branching on the slug and enqueueing a
|
|
9396
|
+
// vendor-named queue. But a workflow carries ONE trigger, this one is
|
|
9397
|
+
// stored on prod against the webhook, and changing a live workflow's
|
|
9398
|
+
// trigger is a data migration. So the declaration matches what exists
|
|
9399
|
+
// and `product.insert` stays a separate, migration-gated change.
|
|
9400
|
+
trigger: { event: "shopify.product.update", type: "webhook" },
|
|
9234
9401
|
type: "step.commerce.product.sync"
|
|
9235
9402
|
})
|
|
9236
9403
|
}
|
|
@@ -9252,26 +9419,37 @@ var shopify_default2 = {
|
|
|
9252
9419
|
type: "step.connection.health.check"
|
|
9253
9420
|
})
|
|
9254
9421
|
},
|
|
9255
|
-
//
|
|
9256
|
-
//
|
|
9257
|
-
//
|
|
9258
|
-
//
|
|
9259
|
-
//
|
|
9260
|
-
//
|
|
9422
|
+
// THE ONE WORKFLOW THAT CARRIES TWO STEPS, and the shape the plan called
|
|
9423
|
+
// the single genuinely new thing in it.
|
|
9424
|
+
//
|
|
9425
|
+
// Every other system step is its own workflow, titled by its own `key`.
|
|
9426
|
+
// These two are "Shopify Token Activity", one row in the merchant's list
|
|
9427
|
+
// with both steps in it — so they declare `workflow`, which names the
|
|
9428
|
+
// title, the trigger and the ORDER, and the provisioner groups on it.
|
|
9429
|
+
// Without that key the pair could only be provisioned by the hand-written
|
|
9430
|
+
// spec in drawbridge-sync's register job, which is exactly why that spec
|
|
9431
|
+
// outlived every other one.
|
|
9432
|
+
//
|
|
9433
|
+
// AUDIT-ONLY. The step documents are written manually at OAuth time and
|
|
9434
|
+
// the workflow is never dispatched; `queue` is declared defensively so
|
|
9435
|
+
// that if it ever IS, the job lands on a real queue and the handler lookup
|
|
9436
|
+
// misses cleanly rather than throwing "Unknown step type".
|
|
9261
9437
|
token: {
|
|
9262
9438
|
exchange: () => ({
|
|
9263
9439
|
description: "Records the token exchange that completed an install. Audit only \u2014 never dispatched.",
|
|
9264
9440
|
key: "Shopify Token Exchange",
|
|
9265
9441
|
queue: "connection",
|
|
9266
9442
|
system: true,
|
|
9267
|
-
type: "step.connection.token.exchange"
|
|
9443
|
+
type: "step.connection.token.exchange",
|
|
9444
|
+
workflow: { key: "Shopify Token Activity", order: 0, trigger: { event: "shopify.token", type: "event" } }
|
|
9268
9445
|
}),
|
|
9269
9446
|
refresh: () => ({
|
|
9270
9447
|
description: "Records a token rotation. Audit only \u2014 never dispatched.",
|
|
9271
9448
|
key: "Shopify Token Refresh",
|
|
9272
9449
|
queue: "connection",
|
|
9273
9450
|
system: true,
|
|
9274
|
-
type: "step.connection.token.refresh"
|
|
9451
|
+
type: "step.connection.token.refresh",
|
|
9452
|
+
workflow: { key: "Shopify Token Activity", order: 1, trigger: { event: "shopify.token", type: "event" } }
|
|
9275
9453
|
})
|
|
9276
9454
|
}
|
|
9277
9455
|
}
|
|
@@ -9727,7 +9905,7 @@ var webhook_default = {
|
|
|
9727
9905
|
// The connect prompt only where connecting is not already the card's whole
|
|
9728
9906
|
// story: a disconnected or errored document renders a Connect action itself,
|
|
9729
9907
|
// and a task repeating it talks over the button.
|
|
9730
|
-
tasks: ({ settings, status } = {}) =>
|
|
9908
|
+
tasks: ({ settings, status } = {}) => status === "error" ? [] : (settings == null ? void 0 : settings.secret) ? [
|
|
9731
9909
|
{
|
|
9732
9910
|
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.',
|
|
9733
9911
|
title: "Requests must be verified",
|
|
@@ -9814,6 +9992,77 @@ var attentive_default3 = {
|
|
|
9814
9992
|
}
|
|
9815
9993
|
};
|
|
9816
9994
|
|
|
9995
|
+
// lib/connections/icons/brightdata.js
|
|
9996
|
+
var brightdata_default = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
9997
|
+
<rect width="500" height="500" fill="#0F58FF"/>
|
|
9998
|
+
<circle cx="250" cy="250" r="52" fill="white"/>
|
|
9999
|
+
<rect x="234" y="70" width="32" height="96" rx="16" fill="white"/>
|
|
10000
|
+
<rect x="234" y="334" width="32" height="96" rx="16" fill="white"/>
|
|
10001
|
+
<rect x="70" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
10002
|
+
<rect x="334" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
10003
|
+
<rect x="117" y="140" width="32" height="96" rx="16" transform="rotate(-45 117 140)" fill="white"/>
|
|
10004
|
+
<rect x="304" y="327" width="32" height="96" rx="16" transform="rotate(-45 304 327)" fill="white"/>
|
|
10005
|
+
<rect x="360" y="117" width="32" height="96" rx="16" transform="rotate(45 360 117)" fill="white"/>
|
|
10006
|
+
<rect x="173" y="304" width="32" height="96" rx="16" transform="rotate(45 173 304)" fill="white"/>
|
|
10007
|
+
</svg>`;
|
|
10008
|
+
|
|
10009
|
+
// lib/connections/vendors/brightdata.js
|
|
10010
|
+
var brightdata_default2 = {
|
|
10011
|
+
fields: [
|
|
10012
|
+
{
|
|
10013
|
+
credential: "BRIGHTDATA_BROWSER_URI",
|
|
10014
|
+
input: "password",
|
|
10015
|
+
key: "browserUri",
|
|
10016
|
+
// A wss:// endpoint with the account's user and password in its
|
|
10017
|
+
// authority, which is why it is a password field rather than a url one:
|
|
10018
|
+
// the whole string is the credential.
|
|
10019
|
+
label: "Scraping Browser endpoint",
|
|
10020
|
+
message: "The wss:// Scraping Browser endpoint, carrying its own credentials. Used to render pages that need a real browser.",
|
|
10021
|
+
redact: true,
|
|
10022
|
+
required: true
|
|
10023
|
+
},
|
|
10024
|
+
{
|
|
10025
|
+
credential: "BRIGHTDATA_UNLOCKER_API_KEY",
|
|
10026
|
+
input: "password",
|
|
10027
|
+
key: "unlockerApiKey",
|
|
10028
|
+
label: "Web Unlocker API key",
|
|
10029
|
+
// NO EXPIRY FIELD, and that is a decision rather than an omission. Bright
|
|
10030
|
+
// Data offers an `Unlimited` expiry and recommends against it; Drawbridge
|
|
10031
|
+
// takes the unlimited key deliberately, because there is no way to rotate
|
|
10032
|
+
// this programmatically and a date nothing can act on is a date that only
|
|
10033
|
+
// surfaces after it has already broken scraping. If a fixed expiry is ever
|
|
10034
|
+
// taken instead, the field to add is a stored date and a warning ahead of
|
|
10035
|
+
// it — not a rotation, which the vendor cannot support.
|
|
10036
|
+
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.",
|
|
10037
|
+
redact: true,
|
|
10038
|
+
required: true
|
|
10039
|
+
},
|
|
10040
|
+
{
|
|
10041
|
+
credential: "BRIGHTDATA_UNLOCKER_ZONE",
|
|
10042
|
+
input: "text",
|
|
10043
|
+
key: "unlockerZone",
|
|
10044
|
+
label: "Web Unlocker zone",
|
|
10045
|
+
// Not secret — a zone is a label on the account, and it is shown rather
|
|
10046
|
+
// than redacted so an operator can confirm which one is in use without
|
|
10047
|
+
// clearing the field to read it.
|
|
10048
|
+
message: "The zone name the Unlocker requests run against.",
|
|
10049
|
+
required: true
|
|
10050
|
+
}
|
|
10051
|
+
],
|
|
10052
|
+
icon: brightdata_default,
|
|
10053
|
+
name: "Bright Data",
|
|
10054
|
+
slug: "brightdata",
|
|
10055
|
+
// WHERE THE FACTS CAME FROM, read 2026-09-16.
|
|
10056
|
+
urls: {
|
|
10057
|
+
api: "https://docs.brightdata.com/api-reference/authentication",
|
|
10058
|
+
dashboard: "https://brightdata.com/cp/setting/users",
|
|
10059
|
+
// Bright Data has no scope model — a key carries the account's own access —
|
|
10060
|
+
// so there is nothing to link. `false` is the recorded answer rather than a
|
|
10061
|
+
// missing key.
|
|
10062
|
+
scopes: false
|
|
10063
|
+
}
|
|
10064
|
+
};
|
|
10065
|
+
|
|
9817
10066
|
// lib/connections/vendors/hubspot.js
|
|
9818
10067
|
var hubspot_default2 = {
|
|
9819
10068
|
// WHAT AN ADMIN TYPES on the provider screen. `credential` names the env var a
|
|
@@ -10127,7 +10376,7 @@ var twilio_default2 = {
|
|
|
10127
10376
|
|
|
10128
10377
|
// lib/connections/index.js
|
|
10129
10378
|
var QUEUES = ["connection", "notification", "segment", "webhook"];
|
|
10130
|
-
var
|
|
10379
|
+
var implemented2 = (hooks, path) => {
|
|
10131
10380
|
const hook = path.split(".").reduce((node, key) => node == null ? void 0 : node[key], hooks);
|
|
10132
10381
|
return typeof hook === "function" || !!hook && typeof hook === "object";
|
|
10133
10382
|
};
|
|
@@ -10189,7 +10438,7 @@ var checkIcon = (owner, icon) => {
|
|
|
10189
10438
|
}
|
|
10190
10439
|
};
|
|
10191
10440
|
var vendors = Object.freeze(Object.fromEntries(
|
|
10192
|
-
[attentive_default3, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
10441
|
+
[attentive_default3, brightdata_default2, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
10193
10442
|
));
|
|
10194
10443
|
var buildVendor = (vendor) => {
|
|
10195
10444
|
if (!(vendor == null ? void 0 : vendor.slug)) throw new Error("A vendor needs a slug");
|
|
@@ -10216,7 +10465,7 @@ for (const [slug2, vendor] of Object.entries(vendors)) {
|
|
|
10216
10465
|
buildVendor(vendor);
|
|
10217
10466
|
}
|
|
10218
10467
|
var build = (manifest) => {
|
|
10219
|
-
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;
|
|
10468
|
+
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;
|
|
10220
10469
|
if (!(manifest == null ? void 0 : manifest.slug)) throw new Error("A connection needs a slug");
|
|
10221
10470
|
if (!(manifest == null ? void 0 : manifest.title)) throw new Error(manifest.slug + " needs a title");
|
|
10222
10471
|
if (manifest == null ? void 0 : manifest.private) throw new Error(manifest.slug + " declares private \u2014 that is feature : false now");
|
|
@@ -10245,7 +10494,7 @@ var build = (manifest) => {
|
|
|
10245
10494
|
if (!field.hook.startsWith("resources.")) {
|
|
10246
10495
|
throw new Error(manifest.slug + "." + field.key + " reads from " + field.hook + " \u2014 a picker may only read resources.*");
|
|
10247
10496
|
}
|
|
10248
|
-
if (!
|
|
10497
|
+
if (!implemented2(manifest.hooks, field.hook)) {
|
|
10249
10498
|
throw new Error(manifest.slug + "." + field.key + " reads " + field.hook + ", which this vendor does not implement");
|
|
10250
10499
|
}
|
|
10251
10500
|
}
|
|
@@ -10284,7 +10533,7 @@ var build = (manifest) => {
|
|
|
10284
10533
|
}
|
|
10285
10534
|
let callback = null;
|
|
10286
10535
|
const type = kinds[0];
|
|
10287
|
-
if (["install", "oauth"].includes(type) && !
|
|
10536
|
+
if (["install", "oauth"].includes(type) && !implemented2(manifest.hooks, "lifecycle.health")) {
|
|
10288
10537
|
throw new Error(
|
|
10289
10538
|
manifest.slug + " is " + type + " and must implement hooks.lifecycle.health \u2014 a credential somebody else can revoke, expire or narrow tells us nothing when it dies"
|
|
10290
10539
|
);
|
|
@@ -10313,10 +10562,10 @@ var build = (manifest) => {
|
|
|
10313
10562
|
}
|
|
10314
10563
|
callback = "/api/connection/" + manifest.slug + "/callback";
|
|
10315
10564
|
}
|
|
10316
|
-
if (
|
|
10565
|
+
if (implemented2(manifest.hooks, "inbound.event") && typeof ((_n = (_m = manifest.hooks) == null ? void 0 : _m.inbound) == null ? void 0 : _n.event) !== "function" && !((_r = (_q = (_p = (_o = manifest.hooks) == null ? void 0 : _o.inbound) == null ? void 0 : _p.config) == null ? void 0 : _q.headers) == null ? void 0 : _r.event)) {
|
|
10317
10566
|
throw new Error(manifest.slug + " implements inbound.event but declares no inbound.headers.event");
|
|
10318
10567
|
}
|
|
10319
|
-
if (
|
|
10568
|
+
if (implemented2(manifest.hooks, "inbound.verify") && !((_v = (_u = (_t = (_s = manifest.hooks) == null ? void 0 : _s.inbound) == null ? void 0 : _t.config) == null ? void 0 : _u.headers) == null ? void 0 : _v.signature)) {
|
|
10320
10569
|
throw new Error(manifest.slug + " implements inbound.verify but declares no inbound.headers.signature");
|
|
10321
10570
|
}
|
|
10322
10571
|
if (!Array.isArray((_w = manifest == null ? void 0 : manifest.content) == null ? void 0 : _w.guide) || !manifest.content.guide.length) {
|
|
@@ -10327,7 +10576,7 @@ var build = (manifest) => {
|
|
|
10327
10576
|
if (Array.isArray(node)) {
|
|
10328
10577
|
for (const verb of node) {
|
|
10329
10578
|
const hook = declared2 == null ? void 0 : declared2[verb];
|
|
10330
|
-
if (hook !== false && !
|
|
10579
|
+
if (hook !== false && !implemented2({ [verb]: hook }, verb)) {
|
|
10331
10580
|
throw new Error(manifest.slug + " must answer hooks." + [...path, verb].join(".") + " \u2014 false, a function, or {} if another repo implements it");
|
|
10332
10581
|
}
|
|
10333
10582
|
}
|
|
@@ -10473,7 +10722,7 @@ var build = (manifest) => {
|
|
|
10473
10722
|
if (!QUEUES.includes(declared2 == null ? void 0 : declared2.queue)) {
|
|
10474
10723
|
throw new Error(manifest.slug + " step " + type2 + " needs a queue \u2014 one of " + QUEUES.join(", "));
|
|
10475
10724
|
}
|
|
10476
|
-
if (declared2.hook && !
|
|
10725
|
+
if (declared2.hook && !implemented2(manifest.hooks, declared2.hook)) {
|
|
10477
10726
|
throw new Error(manifest.slug + " step " + type2 + " points at hook " + declared2.hook + ", which this vendor does not implement");
|
|
10478
10727
|
}
|
|
10479
10728
|
for (const trigger of declared2.triggers || []) {
|
|
@@ -10482,11 +10731,22 @@ var build = (manifest) => {
|
|
|
10482
10731
|
manifest.slug + " step " + type2 + " may be triggered by " + trigger + ", which is not a trigger \u2014 one of " + Object.keys(TRIGGERS).join(", ")
|
|
10483
10732
|
);
|
|
10484
10733
|
}
|
|
10485
|
-
if (declared2.
|
|
10486
|
-
|
|
10734
|
+
if (declared2.workflow) {
|
|
10735
|
+
if (!declared2.workflow.key) {
|
|
10736
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no key \u2014 the title several steps share");
|
|
10737
|
+
}
|
|
10738
|
+
if (typeof declared2.workflow.order !== "number") {
|
|
10739
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no order \u2014 two steps in one row need a sequence");
|
|
10740
|
+
}
|
|
10741
|
+
if (declared2.trigger) {
|
|
10742
|
+
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");
|
|
10743
|
+
}
|
|
10744
|
+
}
|
|
10745
|
+
for (const candidate of [declared2.trigger, (_y = declared2.workflow) == null ? void 0 : _y.trigger].filter(Boolean)) {
|
|
10746
|
+
const known2 = TRIGGERS[Object.keys(TRIGGERS).find((key) => TRIGGERS[key].event === candidate.event && TRIGGERS[key].type === candidate.type)];
|
|
10487
10747
|
if (!known2) {
|
|
10488
10748
|
throw new Error(
|
|
10489
|
-
manifest.slug + " step " + type2 + " declares trigger { event : " +
|
|
10749
|
+
manifest.slug + " step " + type2 + " declares trigger { event : " + candidate.event + ", type : " + candidate.type + " }, which nothing dispatches"
|
|
10490
10750
|
);
|
|
10491
10751
|
}
|
|
10492
10752
|
}
|
|
@@ -10556,9 +10816,10 @@ var publicSettingsBySlug = Object.fromEntries(
|
|
|
10556
10816
|
var connectionSteps = (env = {}) => Object.entries(availableConnections(env)).flatMap(
|
|
10557
10817
|
([slug2, manifest]) => leaves(manifest.steps).map(([, step]) => ({ ...step({}), slug: slug2 }))
|
|
10558
10818
|
);
|
|
10819
|
+
var implementsHook = (hooks, name) => implemented2(hooks, name);
|
|
10559
10820
|
var hookSupport = (name) => ({
|
|
10560
|
-
no: Object.keys(connections).filter((slug2) => !
|
|
10561
|
-
yes: Object.keys(connections).filter((slug2) =>
|
|
10821
|
+
no: Object.keys(connections).filter((slug2) => !implemented2(connections[slug2].hooks, name)),
|
|
10822
|
+
yes: Object.keys(connections).filter((slug2) => implemented2(connections[slug2].hooks, name))
|
|
10562
10823
|
});
|
|
10563
10824
|
var connectFields = (slug2) => {
|
|
10564
10825
|
var _a;
|
|
@@ -10714,6 +10975,7 @@ export {
|
|
|
10714
10975
|
HOOK_PROPS,
|
|
10715
10976
|
HOOK_SLOT_PROPS,
|
|
10716
10977
|
INPUTS,
|
|
10978
|
+
MARKERS,
|
|
10717
10979
|
OAUTH_ENDPOINTS,
|
|
10718
10980
|
OAUTH_FIELDS,
|
|
10719
10981
|
OUTCOMES,
|
|
@@ -10741,6 +11003,7 @@ export {
|
|
|
10741
11003
|
effectsOf,
|
|
10742
11004
|
ensureSystemWorkflows,
|
|
10743
11005
|
hookSupport,
|
|
11006
|
+
implementsHook,
|
|
10744
11007
|
isStale,
|
|
10745
11008
|
isStatus,
|
|
10746
11009
|
mergeSettings,
|
|
@@ -10751,14 +11014,17 @@ export {
|
|
|
10751
11014
|
publicSettingsBySlug,
|
|
10752
11015
|
reconcileConnectionScopes,
|
|
10753
11016
|
redactSettings,
|
|
11017
|
+
reference,
|
|
10754
11018
|
resolveConnection,
|
|
10755
11019
|
runHook,
|
|
10756
11020
|
scopesMessage,
|
|
11021
|
+
spliced,
|
|
10757
11022
|
stepLabels,
|
|
10758
11023
|
stepQueues,
|
|
10759
11024
|
stepRoutes,
|
|
10760
11025
|
surviving,
|
|
10761
11026
|
systemSteps,
|
|
11027
|
+
systemWorkflows,
|
|
10762
11028
|
tokenSettings,
|
|
10763
11029
|
vendors
|
|
10764
11030
|
};
|