@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
|
@@ -42,6 +42,7 @@ __export(connections_exports, {
|
|
|
42
42
|
HOOK_PROPS: () => HOOK_PROPS,
|
|
43
43
|
HOOK_SLOT_PROPS: () => HOOK_SLOT_PROPS,
|
|
44
44
|
INPUTS: () => INPUTS,
|
|
45
|
+
MARKERS: () => MARKERS,
|
|
45
46
|
OAUTH_ENDPOINTS: () => OAUTH_ENDPOINTS,
|
|
46
47
|
OAUTH_FIELDS: () => OAUTH_FIELDS,
|
|
47
48
|
OUTCOMES: () => OUTCOMES,
|
|
@@ -69,6 +70,7 @@ __export(connections_exports, {
|
|
|
69
70
|
effectsOf: () => effectsOf,
|
|
70
71
|
ensureSystemWorkflows: () => ensureSystemWorkflows,
|
|
71
72
|
hookSupport: () => hookSupport,
|
|
73
|
+
implementsHook: () => implementsHook,
|
|
72
74
|
isStale: () => isStale,
|
|
73
75
|
isStatus: () => isStatus,
|
|
74
76
|
mergeSettings: () => mergeSettings,
|
|
@@ -79,14 +81,17 @@ __export(connections_exports, {
|
|
|
79
81
|
publicSettingsBySlug: () => publicSettingsBySlug,
|
|
80
82
|
reconcileConnectionScopes: () => reconcileConnectionScopes,
|
|
81
83
|
redactSettings: () => redactSettings,
|
|
84
|
+
reference: () => reference,
|
|
82
85
|
resolveConnection: () => resolveConnection,
|
|
83
86
|
runHook: () => runHook,
|
|
84
87
|
scopesMessage: () => scopesMessage,
|
|
88
|
+
spliced: () => spliced,
|
|
85
89
|
stepLabels: () => stepLabels,
|
|
86
90
|
stepQueues: () => stepQueues,
|
|
87
91
|
stepRoutes: () => stepRoutes,
|
|
88
92
|
surviving: () => surviving,
|
|
89
93
|
systemSteps: () => systemSteps,
|
|
94
|
+
systemWorkflows: () => systemWorkflows,
|
|
90
95
|
tokenSettings: () => tokenSettings,
|
|
91
96
|
vendors: () => vendors
|
|
92
97
|
});
|
|
@@ -589,6 +594,16 @@ var TRIGGERS = Object.freeze({
|
|
|
589
594
|
"range.ended": Object.freeze({ event: "range.ended", type: "range" }),
|
|
590
595
|
"range.started": Object.freeze({ event: "range.started", type: "range" }),
|
|
591
596
|
"schedule.day": Object.freeze({ event: "day", type: "schedule" }),
|
|
597
|
+
// THE THREE SHOPIFY TRIGGERS, which were never in this vocabulary because the
|
|
598
|
+
// four workflows they fire were hand-written in drawbridge-sync's register job
|
|
599
|
+
// rather than provisioned from the manifest. `webhook` is a fourth trigger
|
|
600
|
+
// TYPE, and it is the honest name: these fire when the vendor calls us, which
|
|
601
|
+
// is neither an event Drawbridge raised nor a schedule it keeps.
|
|
602
|
+
"shopify.order.create": Object.freeze({ event: "shopify.order.create", type: "webhook" }),
|
|
603
|
+
"shopify.product.update": Object.freeze({ event: "shopify.product.update", type: "webhook" }),
|
|
604
|
+
// The token audit, dispatched programmatically by the register job when a
|
|
605
|
+
// store's grant is exchanged or rotated.
|
|
606
|
+
"shopify.token": Object.freeze({ event: "shopify.token", type: "event" }),
|
|
592
607
|
"schedule.month": Object.freeze({ event: "month", type: "schedule" }),
|
|
593
608
|
"schedule.week": Object.freeze({ event: "week", type: "schedule" }),
|
|
594
609
|
"segment.contact.add": Object.freeze({ event: "segment.contact.add", type: "event" }),
|
|
@@ -641,7 +656,7 @@ var OUTCOMES = Object.freeze({
|
|
|
641
656
|
unimplemented: "unimplemented",
|
|
642
657
|
unsupported: "unsupported"
|
|
643
658
|
});
|
|
644
|
-
var STATUSES = Object.freeze(["active", "
|
|
659
|
+
var STATUSES = Object.freeze(["active", "error", "pending"]);
|
|
645
660
|
var PLATFORM_CREDENTIALS = Object.freeze([
|
|
646
661
|
// Seals a merchant's stored connection settings. The webhook connection is
|
|
647
662
|
// gated on it because its whole credential is a secret WE mint and store, so
|
|
@@ -698,10 +713,7 @@ var ERROR_SOURCES = Object.freeze({
|
|
|
698
713
|
"shopify.register.webhooks": true
|
|
699
714
|
});
|
|
700
715
|
var bearing = (entry) => ERROR_SOURCES[entry == null ? void 0 : entry.source] !== false;
|
|
701
|
-
var assess = ({ errors = []
|
|
702
|
-
if (status === "disconnected") return status;
|
|
703
|
-
return (errors || []).some(bearing) ? "error" : "active";
|
|
704
|
-
};
|
|
716
|
+
var assess = ({ errors = [] } = {}) => (errors || []).some(bearing) ? "error" : "active";
|
|
705
717
|
var surviving = ({ errors = [], proved = [] } = {}) => (errors || []).filter((entry) => !proved.includes(entry == null ? void 0 : entry.source));
|
|
706
718
|
var isStatus = (status) => STATUSES.includes(status);
|
|
707
719
|
|
|
@@ -893,16 +905,28 @@ var systemSteps = (node, path = []) => Object.entries(node || {}).flatMap(
|
|
|
893
905
|
return value && typeof value === "object" ? systemSteps(value, [...path, key]) : [];
|
|
894
906
|
}
|
|
895
907
|
);
|
|
908
|
+
var systemWorkflows = (manifest) => {
|
|
909
|
+
var _a, _b, _c;
|
|
910
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
911
|
+
for (const [type, declared] of systemSteps(manifest == null ? void 0 : manifest.steps)) {
|
|
912
|
+
const trigger = ((_a = declared.workflow) == null ? void 0 : _a.trigger) || declared.trigger;
|
|
913
|
+
if (!(trigger == null ? void 0 : trigger.event) || !(trigger == null ? void 0 : trigger.type)) continue;
|
|
914
|
+
const title = ((_b = declared.workflow) == null ? void 0 : _b.key) || declared.key;
|
|
915
|
+
if (!title) continue;
|
|
916
|
+
if (!grouped.has(title)) grouped.set(title, { steps: [], title, trigger });
|
|
917
|
+
grouped.get(title).steps.push({ order: ((_c = declared.workflow) == null ? void 0 : _c.order) ?? 0, type });
|
|
918
|
+
}
|
|
919
|
+
return [...grouped.values()].map((workflow) => ({
|
|
920
|
+
...workflow,
|
|
921
|
+
steps: workflow.steps.slice().sort((a, b) => a.order - b.order)
|
|
922
|
+
}));
|
|
923
|
+
};
|
|
896
924
|
var ensureSystemWorkflows = async ({ connections: registry = connections, controller, doc }) => {
|
|
897
|
-
|
|
898
|
-
if (!(doc == null ? void 0 : doc.id) || doc.slug === "shopify") return [];
|
|
925
|
+
if (!(doc == null ? void 0 : doc.id)) return [];
|
|
899
926
|
const manifest = registry[doc.slug];
|
|
900
927
|
if (!manifest) return [];
|
|
901
928
|
const provisioned = [];
|
|
902
|
-
for (const
|
|
903
|
-
if (!((_a = declared.trigger) == null ? void 0 : _a.event) || !((_b = declared.trigger) == null ? void 0 : _b.type)) continue;
|
|
904
|
-
const title = declared.key;
|
|
905
|
-
if (!title) continue;
|
|
929
|
+
for (const { steps: members, title, trigger } of systemWorkflows(manifest)) {
|
|
906
930
|
const existing = await controller.count({
|
|
907
931
|
collection: "workflow",
|
|
908
932
|
query: {
|
|
@@ -919,13 +943,10 @@ var ensureSystemWorkflows = async ({ connections: registry = connections, contro
|
|
|
919
943
|
connection: doc.id,
|
|
920
944
|
organization: doc.organization,
|
|
921
945
|
status: "active",
|
|
922
|
-
steps:
|
|
923
|
-
settings: {},
|
|
924
|
-
type
|
|
925
|
-
}],
|
|
946
|
+
steps: members.map(({ type }) => ({ settings: {}, type })),
|
|
926
947
|
system: true,
|
|
927
948
|
title,
|
|
928
|
-
trigger
|
|
949
|
+
trigger
|
|
929
950
|
};
|
|
930
951
|
let created;
|
|
931
952
|
try {
|
|
@@ -942,6 +963,135 @@ var ensureSystemWorkflows = async ({ connections: registry = connections, contro
|
|
|
942
963
|
return provisioned;
|
|
943
964
|
};
|
|
944
965
|
|
|
966
|
+
// lib/connections/reference.js
|
|
967
|
+
var MARKER = {
|
|
968
|
+
end: "<!-- /generated -->",
|
|
969
|
+
start: "<!-- generated: drawbridge-utils lib/connections/reference.js \u2014 do not edit by hand -->"
|
|
970
|
+
};
|
|
971
|
+
var slugs = () => Object.keys(connections).sort();
|
|
972
|
+
var slots = (node = HOOKS, path = []) => Object.entries(node).flatMap(
|
|
973
|
+
([key, value]) => Array.isArray(value) ? value.map((verb) => [...path, key, verb].join(".")) : slots(value, [...path, key])
|
|
974
|
+
);
|
|
975
|
+
var implemented = (hooks, name) => {
|
|
976
|
+
const found = name.split(".").reduce(
|
|
977
|
+
(node, key) => node && typeof node === "object" && Object.hasOwn(node, key) ? node[key] : void 0,
|
|
978
|
+
hooks
|
|
979
|
+
);
|
|
980
|
+
if (typeof found === "function") return "yes";
|
|
981
|
+
if (found && typeof found === "object") return "elsewhere";
|
|
982
|
+
return "no";
|
|
983
|
+
};
|
|
984
|
+
var table = (header, rows) => [
|
|
985
|
+
"| " + header.join(" | ") + " |",
|
|
986
|
+
"|" + header.map(() => "---").join("|") + "|",
|
|
987
|
+
...rows.map((row2) => "| " + row2.join(" | ") + " |")
|
|
988
|
+
].join("\n");
|
|
989
|
+
var matrix = () => {
|
|
990
|
+
const columns = slugs();
|
|
991
|
+
return table(
|
|
992
|
+
["Hook", ...columns],
|
|
993
|
+
slots().map((name) => [
|
|
994
|
+
"`" + name + "`",
|
|
995
|
+
...columns.map((slug2) => implemented(connections[slug2].hooks, name))
|
|
996
|
+
])
|
|
997
|
+
);
|
|
998
|
+
};
|
|
999
|
+
var vocabularies = () => {
|
|
1000
|
+
const list2 = (values) => values.map((value) => "`" + value + "`").join(", ");
|
|
1001
|
+
return table(
|
|
1002
|
+
["Vocabulary", "Values"],
|
|
1003
|
+
[
|
|
1004
|
+
["`AUTH_KINDS`", list2(AUTH_KINDS)],
|
|
1005
|
+
["`GROUPS`", list2(GROUPS)],
|
|
1006
|
+
["`INPUTS`", list2(INPUTS)],
|
|
1007
|
+
["`STATUSES`", list2(STATUSES)],
|
|
1008
|
+
["`TRIGGERS`", list2(Object.keys(TRIGGERS).sort())],
|
|
1009
|
+
["`TRIGGER_TYPES`", list2(TRIGGER_TYPES)]
|
|
1010
|
+
]
|
|
1011
|
+
);
|
|
1012
|
+
};
|
|
1013
|
+
var steps = () => {
|
|
1014
|
+
const rows = [];
|
|
1015
|
+
const walk = (node, path, slug2) => {
|
|
1016
|
+
var _a;
|
|
1017
|
+
if (!node || typeof node !== "object") return;
|
|
1018
|
+
for (const [key, value] of Object.entries(node)) {
|
|
1019
|
+
if (typeof value === "function") {
|
|
1020
|
+
let declared;
|
|
1021
|
+
try {
|
|
1022
|
+
declared = value({});
|
|
1023
|
+
} catch {
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
if (!(declared == null ? void 0 : declared.type)) continue;
|
|
1027
|
+
const fires = ((_a = declared.workflow) == null ? void 0 : _a.trigger) || declared.trigger;
|
|
1028
|
+
rows.push([
|
|
1029
|
+
"`" + declared.type + "`",
|
|
1030
|
+
slug2,
|
|
1031
|
+
declared.hook ? "`" + declared.hook + "`" : "\u2014",
|
|
1032
|
+
declared.queue,
|
|
1033
|
+
declared.system ? "system" : declared.withdrawn ? "withdrawn" : "builder",
|
|
1034
|
+
fires ? "`" + fires.event + "` / `" + fires.type + "`" : (declared.triggers || []).map((t) => "`" + t + "`").join(", ") || "\u2014"
|
|
1035
|
+
]);
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
walk(value, [...path, key], slug2);
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
for (const slug2 of slugs()) walk(connections[slug2].steps, [], slug2);
|
|
1042
|
+
return table(["Step", "Connection", "Hook", "Queue", "Shape", "Fired by"], rows.sort((a, b) => a[0].localeCompare(b[0]) || a[1].localeCompare(b[1])));
|
|
1043
|
+
};
|
|
1044
|
+
var workflows = () => table(
|
|
1045
|
+
["Workflow", "Connection", "Steps", "Trigger"],
|
|
1046
|
+
slugs().flatMap((slug2) => systemWorkflows(connections[slug2]).map((workflow) => [
|
|
1047
|
+
workflow.title,
|
|
1048
|
+
slug2,
|
|
1049
|
+
workflow.steps.map((step) => "`" + step.type + "`").join(" \u2192 "),
|
|
1050
|
+
"`" + workflow.trigger.event + "` / `" + workflow.trigger.type + "`"
|
|
1051
|
+
]))
|
|
1052
|
+
);
|
|
1053
|
+
var reference = () => [
|
|
1054
|
+
MARKER.start,
|
|
1055
|
+
"",
|
|
1056
|
+
"### The support matrix",
|
|
1057
|
+
"",
|
|
1058
|
+
"`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.",
|
|
1059
|
+
"",
|
|
1060
|
+
matrix(),
|
|
1061
|
+
"",
|
|
1062
|
+
"### The closed vocabularies",
|
|
1063
|
+
"",
|
|
1064
|
+
vocabularies(),
|
|
1065
|
+
"",
|
|
1066
|
+
"### Every step",
|
|
1067
|
+
"",
|
|
1068
|
+
"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.",
|
|
1069
|
+
"",
|
|
1070
|
+
steps(),
|
|
1071
|
+
"",
|
|
1072
|
+
"### The system workflows a connection is provisioned with",
|
|
1073
|
+
"",
|
|
1074
|
+
"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.",
|
|
1075
|
+
"",
|
|
1076
|
+
workflows(),
|
|
1077
|
+
"",
|
|
1078
|
+
"### Counts",
|
|
1079
|
+
"",
|
|
1080
|
+
"- **" + slugs().length + "** connections",
|
|
1081
|
+
"- **" + slots().length + "** hook slots",
|
|
1082
|
+
"- **" + Object.keys(STEPS).length + "** step types",
|
|
1083
|
+
"- **" + Object.keys(TRIGGERS).length + "** triggers",
|
|
1084
|
+
"",
|
|
1085
|
+
MARKER.end
|
|
1086
|
+
].join("\n");
|
|
1087
|
+
var spliced = (document) => {
|
|
1088
|
+
const from = document.indexOf(MARKER.start);
|
|
1089
|
+
const to = document.indexOf(MARKER.end);
|
|
1090
|
+
if (from < 0 || to < 0) return document;
|
|
1091
|
+
return document.slice(0, from) + reference() + document.slice(to + MARKER.end.length);
|
|
1092
|
+
};
|
|
1093
|
+
var MARKERS = MARKER;
|
|
1094
|
+
|
|
945
1095
|
// lib/connections/oauth.js
|
|
946
1096
|
var import_node_crypto = require("crypto");
|
|
947
1097
|
var credentials = ({ basic, clientId, clientSecret }) => basic ? {
|
|
@@ -5910,7 +6060,7 @@ var getAppToken = async ({ clientId, clientSecret, fetcher }) => {
|
|
|
5910
6060
|
return fetchAppToken({ clientId, clientSecret, fetcher });
|
|
5911
6061
|
};
|
|
5912
6062
|
var toShopGid = (shopId) => String(shopId).startsWith("gid://") ? String(shopId) : "gid://shopify/Shop/" + shopId;
|
|
5913
|
-
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference, revision, shopId, timestamp, value }) => {
|
|
6063
|
+
var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempotencyKey, reference: reference2, revision, shopId, timestamp, value }) => {
|
|
5914
6064
|
if (!shopId || !eventHandle || !(Number(value) > 0)) {
|
|
5915
6065
|
throw new Error("sendAppEvent requires shopId, eventHandle, and a positive value");
|
|
5916
6066
|
}
|
|
@@ -5924,8 +6074,8 @@ var sendAppEvent = async ({ fetcher, clientId, clientSecret, eventHandle, idempo
|
|
|
5924
6074
|
const body = {
|
|
5925
6075
|
attributes: {
|
|
5926
6076
|
value: Number(value),
|
|
5927
|
-
...
|
|
5928
|
-
reference: String(
|
|
6077
|
+
...reference2 && {
|
|
6078
|
+
reference: String(reference2).slice(0, 128)
|
|
5929
6079
|
}
|
|
5930
6080
|
},
|
|
5931
6081
|
event_handle: eventHandle,
|
|
@@ -8086,6 +8236,12 @@ var shopify_default2 = {
|
|
|
8086
8236
|
orderId: String(orderId),
|
|
8087
8237
|
rate,
|
|
8088
8238
|
shopId: connection.source.id,
|
|
8239
|
+
// WHICH VENDOR IS BEING CHARGED, so the sender does not have
|
|
8240
|
+
// to assume. queue/usage.js named the slug itself and
|
|
8241
|
+
// defaults to it for anything already queued when this
|
|
8242
|
+
// shipped — an app-store vendor billing through its own meter
|
|
8243
|
+
// enqueues the same job with its own slug.
|
|
8244
|
+
slug: connection.slug,
|
|
8089
8245
|
// The App Events API returns no event id, so one is generated
|
|
8090
8246
|
// here — the event handle plus the order id — and sent as the
|
|
8091
8247
|
// event's `reference`. queue/usage.js stamps the same id onto
|
|
@@ -9309,6 +9465,13 @@ var shopify_default2 = {
|
|
|
9309
9465
|
key: "Shopify Order Tracking",
|
|
9310
9466
|
queue: "connection",
|
|
9311
9467
|
system: true,
|
|
9468
|
+
// THE TRIGGER THE REGISTER JOB HAND-WROTE. It is byte-identical to
|
|
9469
|
+
// the one on every stored workflow, because these documents exist on
|
|
9470
|
+
// prod with thousands of runs behind them and provisioning is
|
|
9471
|
+
// idempotent on (connection, system, title) — a different trigger
|
|
9472
|
+
// here would not be picked up by the existing rows anyway, and a
|
|
9473
|
+
// different TITLE would provision a second workflow for every store.
|
|
9474
|
+
trigger: { event: "shopify.order.create", type: "webhook" },
|
|
9312
9475
|
type: "step.commerce.order.record"
|
|
9313
9476
|
})
|
|
9314
9477
|
},
|
|
@@ -9319,6 +9482,15 @@ var shopify_default2 = {
|
|
|
9319
9482
|
key: "Shopify Product Sync",
|
|
9320
9483
|
queue: "connection",
|
|
9321
9484
|
system: true,
|
|
9485
|
+
// `shopify.product.update`, NOT `product.insert`. The plan named the
|
|
9486
|
+
// latter, and it is the right long-term answer — a Drawbridge product
|
|
9487
|
+
// row appearing should pull the vendor's copy, rather than
|
|
9488
|
+
// stream/product.js branching on the slug and enqueueing a
|
|
9489
|
+
// vendor-named queue. But a workflow carries ONE trigger, this one is
|
|
9490
|
+
// stored on prod against the webhook, and changing a live workflow's
|
|
9491
|
+
// trigger is a data migration. So the declaration matches what exists
|
|
9492
|
+
// and `product.insert` stays a separate, migration-gated change.
|
|
9493
|
+
trigger: { event: "shopify.product.update", type: "webhook" },
|
|
9322
9494
|
type: "step.commerce.product.sync"
|
|
9323
9495
|
})
|
|
9324
9496
|
}
|
|
@@ -9340,26 +9512,37 @@ var shopify_default2 = {
|
|
|
9340
9512
|
type: "step.connection.health.check"
|
|
9341
9513
|
})
|
|
9342
9514
|
},
|
|
9343
|
-
//
|
|
9344
|
-
//
|
|
9345
|
-
//
|
|
9346
|
-
//
|
|
9347
|
-
//
|
|
9348
|
-
//
|
|
9515
|
+
// THE ONE WORKFLOW THAT CARRIES TWO STEPS, and the shape the plan called
|
|
9516
|
+
// the single genuinely new thing in it.
|
|
9517
|
+
//
|
|
9518
|
+
// Every other system step is its own workflow, titled by its own `key`.
|
|
9519
|
+
// These two are "Shopify Token Activity", one row in the merchant's list
|
|
9520
|
+
// with both steps in it — so they declare `workflow`, which names the
|
|
9521
|
+
// title, the trigger and the ORDER, and the provisioner groups on it.
|
|
9522
|
+
// Without that key the pair could only be provisioned by the hand-written
|
|
9523
|
+
// spec in drawbridge-sync's register job, which is exactly why that spec
|
|
9524
|
+
// outlived every other one.
|
|
9525
|
+
//
|
|
9526
|
+
// AUDIT-ONLY. The step documents are written manually at OAuth time and
|
|
9527
|
+
// the workflow is never dispatched; `queue` is declared defensively so
|
|
9528
|
+
// that if it ever IS, the job lands on a real queue and the handler lookup
|
|
9529
|
+
// misses cleanly rather than throwing "Unknown step type".
|
|
9349
9530
|
token: {
|
|
9350
9531
|
exchange: () => ({
|
|
9351
9532
|
description: "Records the token exchange that completed an install. Audit only \u2014 never dispatched.",
|
|
9352
9533
|
key: "Shopify Token Exchange",
|
|
9353
9534
|
queue: "connection",
|
|
9354
9535
|
system: true,
|
|
9355
|
-
type: "step.connection.token.exchange"
|
|
9536
|
+
type: "step.connection.token.exchange",
|
|
9537
|
+
workflow: { key: "Shopify Token Activity", order: 0, trigger: { event: "shopify.token", type: "event" } }
|
|
9356
9538
|
}),
|
|
9357
9539
|
refresh: () => ({
|
|
9358
9540
|
description: "Records a token rotation. Audit only \u2014 never dispatched.",
|
|
9359
9541
|
key: "Shopify Token Refresh",
|
|
9360
9542
|
queue: "connection",
|
|
9361
9543
|
system: true,
|
|
9362
|
-
type: "step.connection.token.refresh"
|
|
9544
|
+
type: "step.connection.token.refresh",
|
|
9545
|
+
workflow: { key: "Shopify Token Activity", order: 1, trigger: { event: "shopify.token", type: "event" } }
|
|
9363
9546
|
})
|
|
9364
9547
|
}
|
|
9365
9548
|
}
|
|
@@ -9815,7 +9998,7 @@ var webhook_default = {
|
|
|
9815
9998
|
// The connect prompt only where connecting is not already the card's whole
|
|
9816
9999
|
// story: a disconnected or errored document renders a Connect action itself,
|
|
9817
10000
|
// and a task repeating it talks over the button.
|
|
9818
|
-
tasks: ({ settings, status } = {}) =>
|
|
10001
|
+
tasks: ({ settings, status } = {}) => status === "error" ? [] : (settings == null ? void 0 : settings.secret) ? [
|
|
9819
10002
|
{
|
|
9820
10003
|
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.',
|
|
9821
10004
|
title: "Requests must be verified",
|
|
@@ -9902,6 +10085,77 @@ var attentive_default3 = {
|
|
|
9902
10085
|
}
|
|
9903
10086
|
};
|
|
9904
10087
|
|
|
10088
|
+
// lib/connections/icons/brightdata.js
|
|
10089
|
+
var brightdata_default = `<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
10090
|
+
<rect width="500" height="500" fill="#0F58FF"/>
|
|
10091
|
+
<circle cx="250" cy="250" r="52" fill="white"/>
|
|
10092
|
+
<rect x="234" y="70" width="32" height="96" rx="16" fill="white"/>
|
|
10093
|
+
<rect x="234" y="334" width="32" height="96" rx="16" fill="white"/>
|
|
10094
|
+
<rect x="70" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
10095
|
+
<rect x="334" y="234" width="96" height="32" rx="16" fill="white"/>
|
|
10096
|
+
<rect x="117" y="140" width="32" height="96" rx="16" transform="rotate(-45 117 140)" fill="white"/>
|
|
10097
|
+
<rect x="304" y="327" width="32" height="96" rx="16" transform="rotate(-45 304 327)" fill="white"/>
|
|
10098
|
+
<rect x="360" y="117" width="32" height="96" rx="16" transform="rotate(45 360 117)" fill="white"/>
|
|
10099
|
+
<rect x="173" y="304" width="32" height="96" rx="16" transform="rotate(45 173 304)" fill="white"/>
|
|
10100
|
+
</svg>`;
|
|
10101
|
+
|
|
10102
|
+
// lib/connections/vendors/brightdata.js
|
|
10103
|
+
var brightdata_default2 = {
|
|
10104
|
+
fields: [
|
|
10105
|
+
{
|
|
10106
|
+
credential: "BRIGHTDATA_BROWSER_URI",
|
|
10107
|
+
input: "password",
|
|
10108
|
+
key: "browserUri",
|
|
10109
|
+
// A wss:// endpoint with the account's user and password in its
|
|
10110
|
+
// authority, which is why it is a password field rather than a url one:
|
|
10111
|
+
// the whole string is the credential.
|
|
10112
|
+
label: "Scraping Browser endpoint",
|
|
10113
|
+
message: "The wss:// Scraping Browser endpoint, carrying its own credentials. Used to render pages that need a real browser.",
|
|
10114
|
+
redact: true,
|
|
10115
|
+
required: true
|
|
10116
|
+
},
|
|
10117
|
+
{
|
|
10118
|
+
credential: "BRIGHTDATA_UNLOCKER_API_KEY",
|
|
10119
|
+
input: "password",
|
|
10120
|
+
key: "unlockerApiKey",
|
|
10121
|
+
label: "Web Unlocker API key",
|
|
10122
|
+
// NO EXPIRY FIELD, and that is a decision rather than an omission. Bright
|
|
10123
|
+
// Data offers an `Unlimited` expiry and recommends against it; Drawbridge
|
|
10124
|
+
// takes the unlimited key deliberately, because there is no way to rotate
|
|
10125
|
+
// this programmatically and a date nothing can act on is a date that only
|
|
10126
|
+
// surfaces after it has already broken scraping. If a fixed expiry is ever
|
|
10127
|
+
// taken instead, the field to add is a stored date and a warning ahead of
|
|
10128
|
+
// it — not a rotation, which the vendor cannot support.
|
|
10129
|
+
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.",
|
|
10130
|
+
redact: true,
|
|
10131
|
+
required: true
|
|
10132
|
+
},
|
|
10133
|
+
{
|
|
10134
|
+
credential: "BRIGHTDATA_UNLOCKER_ZONE",
|
|
10135
|
+
input: "text",
|
|
10136
|
+
key: "unlockerZone",
|
|
10137
|
+
label: "Web Unlocker zone",
|
|
10138
|
+
// Not secret — a zone is a label on the account, and it is shown rather
|
|
10139
|
+
// than redacted so an operator can confirm which one is in use without
|
|
10140
|
+
// clearing the field to read it.
|
|
10141
|
+
message: "The zone name the Unlocker requests run against.",
|
|
10142
|
+
required: true
|
|
10143
|
+
}
|
|
10144
|
+
],
|
|
10145
|
+
icon: brightdata_default,
|
|
10146
|
+
name: "Bright Data",
|
|
10147
|
+
slug: "brightdata",
|
|
10148
|
+
// WHERE THE FACTS CAME FROM, read 2026-09-16.
|
|
10149
|
+
urls: {
|
|
10150
|
+
api: "https://docs.brightdata.com/api-reference/authentication",
|
|
10151
|
+
dashboard: "https://brightdata.com/cp/setting/users",
|
|
10152
|
+
// Bright Data has no scope model — a key carries the account's own access —
|
|
10153
|
+
// so there is nothing to link. `false` is the recorded answer rather than a
|
|
10154
|
+
// missing key.
|
|
10155
|
+
scopes: false
|
|
10156
|
+
}
|
|
10157
|
+
};
|
|
10158
|
+
|
|
9905
10159
|
// lib/connections/vendors/hubspot.js
|
|
9906
10160
|
var hubspot_default2 = {
|
|
9907
10161
|
// WHAT AN ADMIN TYPES on the provider screen. `credential` names the env var a
|
|
@@ -10215,7 +10469,7 @@ var twilio_default2 = {
|
|
|
10215
10469
|
|
|
10216
10470
|
// lib/connections/index.js
|
|
10217
10471
|
var QUEUES = ["connection", "notification", "segment", "webhook"];
|
|
10218
|
-
var
|
|
10472
|
+
var implemented2 = (hooks, path) => {
|
|
10219
10473
|
const hook = path.split(".").reduce((node, key) => node == null ? void 0 : node[key], hooks);
|
|
10220
10474
|
return typeof hook === "function" || !!hook && typeof hook === "object";
|
|
10221
10475
|
};
|
|
@@ -10277,7 +10531,7 @@ var checkIcon = (owner, icon) => {
|
|
|
10277
10531
|
}
|
|
10278
10532
|
};
|
|
10279
10533
|
var vendors = Object.freeze(Object.fromEntries(
|
|
10280
|
-
[attentive_default3, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
10534
|
+
[attentive_default3, brightdata_default2, hubspot_default2, klaviyo_default3, mailchimp_default3, sendgrid_default2, shopify_default3, twilio_default2].map((vendor) => [vendor.slug, vendor])
|
|
10281
10535
|
));
|
|
10282
10536
|
var buildVendor = (vendor) => {
|
|
10283
10537
|
if (!(vendor == null ? void 0 : vendor.slug)) throw new Error("A vendor needs a slug");
|
|
@@ -10304,7 +10558,7 @@ for (const [slug2, vendor] of Object.entries(vendors)) {
|
|
|
10304
10558
|
buildVendor(vendor);
|
|
10305
10559
|
}
|
|
10306
10560
|
var build = (manifest) => {
|
|
10307
|
-
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;
|
|
10561
|
+
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;
|
|
10308
10562
|
if (!(manifest == null ? void 0 : manifest.slug)) throw new Error("A connection needs a slug");
|
|
10309
10563
|
if (!(manifest == null ? void 0 : manifest.title)) throw new Error(manifest.slug + " needs a title");
|
|
10310
10564
|
if (manifest == null ? void 0 : manifest.private) throw new Error(manifest.slug + " declares private \u2014 that is feature : false now");
|
|
@@ -10333,7 +10587,7 @@ var build = (manifest) => {
|
|
|
10333
10587
|
if (!field.hook.startsWith("resources.")) {
|
|
10334
10588
|
throw new Error(manifest.slug + "." + field.key + " reads from " + field.hook + " \u2014 a picker may only read resources.*");
|
|
10335
10589
|
}
|
|
10336
|
-
if (!
|
|
10590
|
+
if (!implemented2(manifest.hooks, field.hook)) {
|
|
10337
10591
|
throw new Error(manifest.slug + "." + field.key + " reads " + field.hook + ", which this vendor does not implement");
|
|
10338
10592
|
}
|
|
10339
10593
|
}
|
|
@@ -10372,7 +10626,7 @@ var build = (manifest) => {
|
|
|
10372
10626
|
}
|
|
10373
10627
|
let callback = null;
|
|
10374
10628
|
const type = kinds[0];
|
|
10375
|
-
if (["install", "oauth"].includes(type) && !
|
|
10629
|
+
if (["install", "oauth"].includes(type) && !implemented2(manifest.hooks, "lifecycle.health")) {
|
|
10376
10630
|
throw new Error(
|
|
10377
10631
|
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"
|
|
10378
10632
|
);
|
|
@@ -10401,10 +10655,10 @@ var build = (manifest) => {
|
|
|
10401
10655
|
}
|
|
10402
10656
|
callback = "/api/connection/" + manifest.slug + "/callback";
|
|
10403
10657
|
}
|
|
10404
|
-
if (
|
|
10658
|
+
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)) {
|
|
10405
10659
|
throw new Error(manifest.slug + " implements inbound.event but declares no inbound.headers.event");
|
|
10406
10660
|
}
|
|
10407
|
-
if (
|
|
10661
|
+
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)) {
|
|
10408
10662
|
throw new Error(manifest.slug + " implements inbound.verify but declares no inbound.headers.signature");
|
|
10409
10663
|
}
|
|
10410
10664
|
if (!Array.isArray((_w = manifest == null ? void 0 : manifest.content) == null ? void 0 : _w.guide) || !manifest.content.guide.length) {
|
|
@@ -10415,7 +10669,7 @@ var build = (manifest) => {
|
|
|
10415
10669
|
if (Array.isArray(node)) {
|
|
10416
10670
|
for (const verb of node) {
|
|
10417
10671
|
const hook = declared2 == null ? void 0 : declared2[verb];
|
|
10418
|
-
if (hook !== false && !
|
|
10672
|
+
if (hook !== false && !implemented2({ [verb]: hook }, verb)) {
|
|
10419
10673
|
throw new Error(manifest.slug + " must answer hooks." + [...path, verb].join(".") + " \u2014 false, a function, or {} if another repo implements it");
|
|
10420
10674
|
}
|
|
10421
10675
|
}
|
|
@@ -10561,7 +10815,7 @@ var build = (manifest) => {
|
|
|
10561
10815
|
if (!QUEUES.includes(declared2 == null ? void 0 : declared2.queue)) {
|
|
10562
10816
|
throw new Error(manifest.slug + " step " + type2 + " needs a queue \u2014 one of " + QUEUES.join(", "));
|
|
10563
10817
|
}
|
|
10564
|
-
if (declared2.hook && !
|
|
10818
|
+
if (declared2.hook && !implemented2(manifest.hooks, declared2.hook)) {
|
|
10565
10819
|
throw new Error(manifest.slug + " step " + type2 + " points at hook " + declared2.hook + ", which this vendor does not implement");
|
|
10566
10820
|
}
|
|
10567
10821
|
for (const trigger of declared2.triggers || []) {
|
|
@@ -10570,11 +10824,22 @@ var build = (manifest) => {
|
|
|
10570
10824
|
manifest.slug + " step " + type2 + " may be triggered by " + trigger + ", which is not a trigger \u2014 one of " + Object.keys(TRIGGERS).join(", ")
|
|
10571
10825
|
);
|
|
10572
10826
|
}
|
|
10573
|
-
if (declared2.
|
|
10574
|
-
|
|
10827
|
+
if (declared2.workflow) {
|
|
10828
|
+
if (!declared2.workflow.key) {
|
|
10829
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no key \u2014 the title several steps share");
|
|
10830
|
+
}
|
|
10831
|
+
if (typeof declared2.workflow.order !== "number") {
|
|
10832
|
+
throw new Error(manifest.slug + " step " + type2 + " declares a workflow with no order \u2014 two steps in one row need a sequence");
|
|
10833
|
+
}
|
|
10834
|
+
if (declared2.trigger) {
|
|
10835
|
+
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");
|
|
10836
|
+
}
|
|
10837
|
+
}
|
|
10838
|
+
for (const candidate of [declared2.trigger, (_y = declared2.workflow) == null ? void 0 : _y.trigger].filter(Boolean)) {
|
|
10839
|
+
const known2 = TRIGGERS[Object.keys(TRIGGERS).find((key) => TRIGGERS[key].event === candidate.event && TRIGGERS[key].type === candidate.type)];
|
|
10575
10840
|
if (!known2) {
|
|
10576
10841
|
throw new Error(
|
|
10577
|
-
manifest.slug + " step " + type2 + " declares trigger { event : " +
|
|
10842
|
+
manifest.slug + " step " + type2 + " declares trigger { event : " + candidate.event + ", type : " + candidate.type + " }, which nothing dispatches"
|
|
10578
10843
|
);
|
|
10579
10844
|
}
|
|
10580
10845
|
}
|
|
@@ -10644,9 +10909,10 @@ var publicSettingsBySlug = Object.fromEntries(
|
|
|
10644
10909
|
var connectionSteps = (env = {}) => Object.entries(availableConnections(env)).flatMap(
|
|
10645
10910
|
([slug2, manifest]) => leaves(manifest.steps).map(([, step]) => ({ ...step({}), slug: slug2 }))
|
|
10646
10911
|
);
|
|
10912
|
+
var implementsHook = (hooks, name) => implemented2(hooks, name);
|
|
10647
10913
|
var hookSupport = (name) => ({
|
|
10648
|
-
no: Object.keys(connections).filter((slug2) => !
|
|
10649
|
-
yes: Object.keys(connections).filter((slug2) =>
|
|
10914
|
+
no: Object.keys(connections).filter((slug2) => !implemented2(connections[slug2].hooks, name)),
|
|
10915
|
+
yes: Object.keys(connections).filter((slug2) => implemented2(connections[slug2].hooks, name))
|
|
10650
10916
|
});
|
|
10651
10917
|
var connectFields = (slug2) => {
|
|
10652
10918
|
var _a;
|
|
@@ -10803,6 +11069,7 @@ var resolveConnection = (item, data, env = {}) => {
|
|
|
10803
11069
|
HOOK_PROPS,
|
|
10804
11070
|
HOOK_SLOT_PROPS,
|
|
10805
11071
|
INPUTS,
|
|
11072
|
+
MARKERS,
|
|
10806
11073
|
OAUTH_ENDPOINTS,
|
|
10807
11074
|
OAUTH_FIELDS,
|
|
10808
11075
|
OUTCOMES,
|
|
@@ -10830,6 +11097,7 @@ var resolveConnection = (item, data, env = {}) => {
|
|
|
10830
11097
|
effectsOf,
|
|
10831
11098
|
ensureSystemWorkflows,
|
|
10832
11099
|
hookSupport,
|
|
11100
|
+
implementsHook,
|
|
10833
11101
|
isStale,
|
|
10834
11102
|
isStatus,
|
|
10835
11103
|
mergeSettings,
|
|
@@ -10840,14 +11108,17 @@ var resolveConnection = (item, data, env = {}) => {
|
|
|
10840
11108
|
publicSettingsBySlug,
|
|
10841
11109
|
reconcileConnectionScopes,
|
|
10842
11110
|
redactSettings,
|
|
11111
|
+
reference,
|
|
10843
11112
|
resolveConnection,
|
|
10844
11113
|
runHook,
|
|
10845
11114
|
scopesMessage,
|
|
11115
|
+
spliced,
|
|
10846
11116
|
stepLabels,
|
|
10847
11117
|
stepQueues,
|
|
10848
11118
|
stepRoutes,
|
|
10849
11119
|
surviving,
|
|
10850
11120
|
systemSteps,
|
|
11121
|
+
systemWorkflows,
|
|
10851
11122
|
tokenSettings,
|
|
10852
11123
|
vendors
|
|
10853
11124
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ANSWER_KEYS, a as AUTH_KINDS, b as AUTH_TYPES, E as ERROR_SOURCES, G as GROUPS, H as HOOKS, c as HOOK_CONFIG, d as HOOK_EFFECTS, e as HOOK_NAMES, f as HOOK_OPTIONS, g as HOOK_PROPS, h as HOOK_SLOT_PROPS, I as INPUTS, O as OAUTH_ENDPOINTS, i as OAUTH_FIELDS, j as OUTCOMES, P as PLATFORM_CREDENTIALS, R as RETIRED, S as STATUSES, k as STEPS, l as STEP_TYPES, T as TRIGGERS, m as TRIGGER_TYPES, W as WRITE_OPERATIONS, n as accessToken, o as answerOf, p as assess, q as availableConnections, r as build, s as buildVendor, t as catalogConnections, v as connectFields, w as connectionSettings, x as connectionSteps, y as connections, z as effectsOf, B as ensureSystemWorkflows, C as hookSupport, D as
|
|
1
|
+
export { A as ANSWER_KEYS, a as AUTH_KINDS, b as AUTH_TYPES, E as ERROR_SOURCES, G as GROUPS, H as HOOKS, c as HOOK_CONFIG, d as HOOK_EFFECTS, e as HOOK_NAMES, f as HOOK_OPTIONS, g as HOOK_PROPS, h as HOOK_SLOT_PROPS, I as INPUTS, M as MARKERS, O as OAUTH_ENDPOINTS, i as OAUTH_FIELDS, j as OUTCOMES, P as PLATFORM_CREDENTIALS, R as RETIRED, S as STATUSES, k as STEPS, l as STEP_TYPES, T as TRIGGERS, m as TRIGGER_TYPES, W as WRITE_OPERATIONS, n as accessToken, o as answerOf, p as assess, q as availableConnections, r as build, s as buildVendor, t as catalogConnections, v as connectFields, w as connectionSettings, x as connectionSteps, y as connections, z as effectsOf, B as ensureSystemWorkflows, C as hookSupport, D as implementsHook, F as isStale, J as isStatus, K as mergeSettings, L as perform, N as projectConnection, Q as publicConnectionKeys, U as publicSettingsBySlug, V as reconcileConnectionScopes, X as redactSettings, Y as reference, Z as resolveConnection, _ as runHook, $ as scopesMessage, a0 as spliced, a1 as stepLabels, a2 as stepQueues, a3 as stepRoutes, a4 as surviving, a5 as systemSteps, a6 as systemWorkflows, a7 as tokenSettings, a8 as vendors } from '../index-C2rxasGZ.cjs';
|
|
2
2
|
export { authToken, consentUrl, pkcePair } from './oauth.cjs';
|
|
3
3
|
import '../usage.cjs';
|
|
4
4
|
import '../encrypt.cjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ANSWER_KEYS, a as AUTH_KINDS, b as AUTH_TYPES, E as ERROR_SOURCES, G as GROUPS, H as HOOKS, c as HOOK_CONFIG, d as HOOK_EFFECTS, e as HOOK_NAMES, f as HOOK_OPTIONS, g as HOOK_PROPS, h as HOOK_SLOT_PROPS, I as INPUTS, O as OAUTH_ENDPOINTS, i as OAUTH_FIELDS, j as OUTCOMES, P as PLATFORM_CREDENTIALS, R as RETIRED, S as STATUSES, k as STEPS, l as STEP_TYPES, T as TRIGGERS, m as TRIGGER_TYPES, W as WRITE_OPERATIONS, n as accessToken, o as answerOf, p as assess, q as availableConnections, r as build, s as buildVendor, t as catalogConnections, v as connectFields, w as connectionSettings, x as connectionSteps, y as connections, z as effectsOf, B as ensureSystemWorkflows, C as hookSupport, D as
|
|
1
|
+
export { A as ANSWER_KEYS, a as AUTH_KINDS, b as AUTH_TYPES, E as ERROR_SOURCES, G as GROUPS, H as HOOKS, c as HOOK_CONFIG, d as HOOK_EFFECTS, e as HOOK_NAMES, f as HOOK_OPTIONS, g as HOOK_PROPS, h as HOOK_SLOT_PROPS, I as INPUTS, M as MARKERS, O as OAUTH_ENDPOINTS, i as OAUTH_FIELDS, j as OUTCOMES, P as PLATFORM_CREDENTIALS, R as RETIRED, S as STATUSES, k as STEPS, l as STEP_TYPES, T as TRIGGERS, m as TRIGGER_TYPES, W as WRITE_OPERATIONS, n as accessToken, o as answerOf, p as assess, q as availableConnections, r as build, s as buildVendor, t as catalogConnections, v as connectFields, w as connectionSettings, x as connectionSteps, y as connections, z as effectsOf, B as ensureSystemWorkflows, C as hookSupport, D as implementsHook, F as isStale, J as isStatus, K as mergeSettings, L as perform, N as projectConnection, Q as publicConnectionKeys, U as publicSettingsBySlug, V as reconcileConnectionScopes, X as redactSettings, Y as reference, Z as resolveConnection, _ as runHook, $ as scopesMessage, a0 as spliced, a1 as stepLabels, a2 as stepQueues, a3 as stepRoutes, a4 as surviving, a5 as systemSteps, a6 as systemWorkflows, a7 as tokenSettings, a8 as vendors } from '../index-DOYCXtd7.js';
|
|
2
2
|
export { authToken, consentUrl, pkcePair } from './oauth.js';
|
|
3
3
|
import '../usage.js';
|
|
4
4
|
import '../encrypt.js';
|