@drawbridge/drawbridge-utils 0.0.170 → 0.0.172
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 +55 -39
- package/dist/connections/index.d.cts +158 -53
- package/dist/connections/index.d.ts +158 -53
- package/dist/connections/index.js +55 -39
- package/dist/providers.cjs +55 -39
- package/dist/providers.js +55 -39
- package/package.json +2 -2
package/dist/providers.cjs
CHANGED
|
@@ -527,6 +527,7 @@ var row = ({ connection: connection2, data: data2, manifest, row: described }) =
|
|
|
527
527
|
};
|
|
528
528
|
};
|
|
529
529
|
var segmentRowWrites = ({ connection: connection2, data: data2, manifest, row: described, segment }) => {
|
|
530
|
+
if ((described == null ? void 0 : described.id) === void 0 || (described == null ? void 0 : described.id) === null) return [];
|
|
530
531
|
const built = row({ connection: connection2, data: data2, manifest, row: described });
|
|
531
532
|
return [
|
|
532
533
|
// PUSH IF ABSENT. The $ne guard is what makes a second concurrent register
|
|
@@ -774,10 +775,11 @@ var attentive_default2 = {
|
|
|
774
775
|
sync: async ({ lead, settings, suppressed, token }, { fetcher } = {}) => {
|
|
775
776
|
var _a, _b, _c, _d;
|
|
776
777
|
const segment = settings == null ? void 0 : settings.segment;
|
|
777
|
-
if (!segment) return { message: "No Attentive segment is chosen for this connection.", skipped: true };
|
|
778
778
|
const email = ((_b = (_a = lead == null ? void 0 : lead.canonical) == null ? void 0 : _a.email) == null ? void 0 : _b.value) || (lead == null ? void 0 : lead.email);
|
|
779
779
|
const phone = toE164(((_d = (_c = lead == null ? void 0 : lead.canonical) == null ? void 0 : _c.phone) == null ? void 0 : _d.value) || (lead == null ? void 0 : lead.phone));
|
|
780
|
-
|
|
780
|
+
const request2 = { email: email || null, phone: phone || null, segment: (settings == null ? void 0 : settings.segment) || null };
|
|
781
|
+
if (!segment) return { message: "No Attentive segment is chosen for this connection.", request: request2, skipped: true };
|
|
782
|
+
if (!email && !phone) return { message: "That lead has no email address or phone number to sync.", request: request2, skipped: true };
|
|
781
783
|
const user = {
|
|
782
784
|
...email && { email },
|
|
783
785
|
...phone && { phone }
|
|
@@ -799,6 +801,7 @@ var attentive_default2 = {
|
|
|
799
801
|
});
|
|
800
802
|
return {
|
|
801
803
|
message: "Attentive accepted an unsubscribe for this contact \u2014 they have opted out.",
|
|
804
|
+
request: request2,
|
|
802
805
|
response: { accepted: true, unsubscribed: true }
|
|
803
806
|
};
|
|
804
807
|
}
|
|
@@ -839,6 +842,7 @@ var attentive_default2 = {
|
|
|
839
842
|
// queued them — a merchant who reads "synced" and looks for the person
|
|
840
843
|
// in Attentive a second later has been told the wrong thing.
|
|
841
844
|
message: "Attentive accepted this contact for the segment. Attentive processes these asynchronously, so it appears there shortly.",
|
|
845
|
+
request: request2,
|
|
842
846
|
response: {
|
|
843
847
|
accepted: true,
|
|
844
848
|
...(membership == null ? void 0 : membership.batchJobId) && { batchJobId: membership.batchJobId }
|
|
@@ -2855,7 +2859,7 @@ var drawbridge_default2 = {
|
|
|
2855
2859
|
text: { max: 2e3, type: "string" }
|
|
2856
2860
|
},
|
|
2857
2861
|
triggers: ["schedule.day", "schedule.week", "schedule.month"],
|
|
2858
|
-
usage: { actions:
|
|
2862
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2859
2863
|
}),
|
|
2860
2864
|
// To organization MEMBERS. Never suppressed — an entrant's opt-out must
|
|
2861
2865
|
// not silence an alert to staff — and not billed.
|
|
@@ -2880,10 +2884,14 @@ var drawbridge_default2 = {
|
|
|
2880
2884
|
subject: { max: 150, type: "string" }
|
|
2881
2885
|
},
|
|
2882
2886
|
triggers: ["lead.insert"],
|
|
2883
|
-
//
|
|
2884
|
-
//
|
|
2885
|
-
//
|
|
2886
|
-
|
|
2887
|
+
// EVERY STEP IN A BILLABLE WORKFLOW COUNTS, team mail included. This
|
|
2888
|
+
// leaves our SendGrid account and costs the same to send as any other
|
|
2889
|
+
// email, so it carries the same weight. Whether anything is charged at
|
|
2890
|
+
// all is the workflow's `billable` tag, not the step's audience — and
|
|
2891
|
+
// this step triggers on lead.insert, so a merchant notifying
|
|
2892
|
+
// themselves on every entry spends against their allowance twice per
|
|
2893
|
+
// lead, once for the entry and once for the notification.
|
|
2894
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2887
2895
|
}),
|
|
2888
2896
|
// To a LEAD. Suppression applies and the send is billed.
|
|
2889
2897
|
send: () => ({
|
|
@@ -2992,7 +3000,7 @@ var api2 = async (path, { fetcher = fetch, method = "GET", payload, token }) =>
|
|
|
2992
3000
|
}
|
|
2993
3001
|
return response.status === 204 ? null : response.json();
|
|
2994
3002
|
};
|
|
2995
|
-
var segmentName = (title) => "Drawbridge: " + title;
|
|
3003
|
+
var segmentName = (title, id) => "Drawbridge: " + title + " (" + String(id).slice(-6) + ")";
|
|
2996
3004
|
var canManageSegments = (settings) => String((settings == null ? void 0 : settings.scope) || "").split(/\s+/).includes("segments:write");
|
|
2997
3005
|
var klaviyo_default2 = {
|
|
2998
3006
|
// OAuth 2.1, and PKCE is REQUIRED rather than recommended: Klaviyo refuses an
|
|
@@ -3239,11 +3247,12 @@ var klaviyo_default2 = {
|
|
|
3239
3247
|
// different thing from deleting the profile.
|
|
3240
3248
|
remove: false,
|
|
3241
3249
|
sync: async ({ context, lead, segments, settings, suppressed, token }, { fetcher } = {}) => {
|
|
3242
|
-
var _a, _b, _c;
|
|
3250
|
+
var _a, _b, _c, _d, _e;
|
|
3243
3251
|
const list = settings == null ? void 0 : settings.list;
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3252
|
+
const request2 = { email: ((_b = (_a = lead == null ? void 0 : lead.canonical) == null ? void 0 : _a.email) == null ? void 0 : _b.value) || (lead == null ? void 0 : lead.email) || null, list: (settings == null ? void 0 : settings.list) || null };
|
|
3253
|
+
if (!list) return { message: "No Klaviyo list is chosen for this connection.", request: request2, skipped: true };
|
|
3254
|
+
const email = ((_d = (_c = lead == null ? void 0 : lead.canonical) == null ? void 0 : _c.email) == null ? void 0 : _d.value) || (lead == null ? void 0 : lead.email);
|
|
3255
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3247
3256
|
const person = (context == null ? void 0 : context.contact) || null;
|
|
3248
3257
|
const totals = (person == null ? void 0 : person.totals) || {};
|
|
3249
3258
|
const count = (value) => typeof value === "number" ? value : (value == null ? void 0 : value.total) || 0;
|
|
@@ -3300,8 +3309,8 @@ var klaviyo_default2 = {
|
|
|
3300
3309
|
},
|
|
3301
3310
|
token
|
|
3302
3311
|
});
|
|
3303
|
-
const profileId = (
|
|
3304
|
-
if (!profileId) return { message: "Klaviyo returned no profile id.", skipped: true };
|
|
3312
|
+
const profileId = (_e = profile == null ? void 0 : profile.data) == null ? void 0 : _e.id;
|
|
3313
|
+
if (!profileId) return { message: "Klaviyo returned no profile id.", request: request2, skipped: true };
|
|
3305
3314
|
await api2("/profile-subscription-bulk-create-jobs/", {
|
|
3306
3315
|
fetcher,
|
|
3307
3316
|
method: "POST",
|
|
@@ -3330,6 +3339,7 @@ var klaviyo_default2 = {
|
|
|
3330
3339
|
// Merged into `context` for later steps in this run.
|
|
3331
3340
|
context: { klaviyoProfileId: profileId },
|
|
3332
3341
|
message: suppressed ? "Synced to Klaviyo as unsubscribed \u2014 this contact has opted out." : "Synced to the Klaviyo list.",
|
|
3342
|
+
request: request2,
|
|
3333
3343
|
// Recorded on the run for support to read back, not a write
|
|
3334
3344
|
// instruction — the hook has already written what it needed to.
|
|
3335
3345
|
response: { klaviyoProfileId: profileId }
|
|
@@ -3365,7 +3375,7 @@ var klaviyo_default2 = {
|
|
|
3365
3375
|
skipped: true
|
|
3366
3376
|
};
|
|
3367
3377
|
}
|
|
3368
|
-
const name = segmentName(segment.title);
|
|
3378
|
+
const name = segmentName(segment.title, segment.id);
|
|
3369
3379
|
const existing = segmentRowFor({ connection: connection2, segment });
|
|
3370
3380
|
let id = null;
|
|
3371
3381
|
if (existing == null ? void 0 : existing.id) {
|
|
@@ -3772,7 +3782,10 @@ var api3 = async (path, { dc, fetcher = fetch, method = "GET", payload, token })
|
|
|
3772
3782
|
};
|
|
3773
3783
|
var subscriberHash = (email) => (0, import_node_crypto5.createHash)("md5").update(String(email).trim().toLowerCase()).digest("hex");
|
|
3774
3784
|
var TAG_NAME_LIMIT = 100;
|
|
3775
|
-
var tagName = (title) =>
|
|
3785
|
+
var tagName = (title, id) => {
|
|
3786
|
+
const suffix = " (" + String(id).slice(-6) + ")";
|
|
3787
|
+
return ("Drawbridge: " + title).slice(0, TAG_NAME_LIMIT - suffix.length) + suffix;
|
|
3788
|
+
};
|
|
3776
3789
|
var mailchimp_default2 = {
|
|
3777
3790
|
// OAUTH 2, authorization code. Every url below is quoted from
|
|
3778
3791
|
// mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/ rather
|
|
@@ -3910,15 +3923,16 @@ var mailchimp_default2 = {
|
|
|
3910
3923
|
// why there is no create-or-update branch here. Quoted from Mailchimp's
|
|
3911
3924
|
// Marketing API reference for the list-members resource.
|
|
3912
3925
|
sync: async ({ connection: connection2, lead, segments, settings, suppressed, token }, { fetcher, read } = {}) => {
|
|
3913
|
-
var _a, _b, _c;
|
|
3926
|
+
var _a, _b, _c, _d, _e;
|
|
3914
3927
|
const audience = settings == null ? void 0 : settings.audience;
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3928
|
+
const request2 = { audience: (settings == null ? void 0 : settings.audience) || null, email: ((_b = (_a = lead == null ? void 0 : lead.canonical) == null ? void 0 : _a.email) == null ? void 0 : _b.value) || (lead == null ? void 0 : lead.email) || null };
|
|
3929
|
+
if (!audience) return { message: "No Mailchimp audience is chosen for this connection.", request: request2, skipped: true };
|
|
3930
|
+
const email = ((_d = (_c = lead == null ? void 0 : lead.canonical) == null ? void 0 : _c.email) == null ? void 0 : _d.value) || (lead == null ? void 0 : lead.email);
|
|
3931
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3918
3932
|
const hash = subscriberHash(email);
|
|
3919
3933
|
const [firstName, ...restOfName] = String((lead == null ? void 0 : lead.name) || "").trim().split(/\s+/).filter(Boolean);
|
|
3920
3934
|
const lastName = restOfName.join(" ");
|
|
3921
|
-
const phone = ((
|
|
3935
|
+
const phone = ((_e = lead == null ? void 0 : lead.phone) == null ? void 0 : _e.number) || null;
|
|
3922
3936
|
const mergeFields = {
|
|
3923
3937
|
...firstName && { FNAME: firstName },
|
|
3924
3938
|
...lastName && { LNAME: lastName },
|
|
@@ -3952,14 +3966,17 @@ var mailchimp_default2 = {
|
|
|
3952
3966
|
}
|
|
3953
3967
|
},
|
|
3954
3968
|
{
|
|
3955
|
-
|
|
3969
|
+
// THE ID AS WELL AS THE TITLE, because the tag name carries it —
|
|
3970
|
+
// see tagName. Without it every name here would end in the
|
|
3971
|
+
// string 'undefined' and match nothing register wrote.
|
|
3972
|
+
$project: { _id: 0, id: 1, title: 1 }
|
|
3956
3973
|
}
|
|
3957
3974
|
]
|
|
3958
3975
|
});
|
|
3959
|
-
const joined = new Set(segments.map((entry) => entry.
|
|
3960
|
-
const tags = (owned || []).
|
|
3961
|
-
name: tagName(title),
|
|
3962
|
-
status: joined.has(
|
|
3976
|
+
const joined = new Set(segments.map((entry) => entry.id));
|
|
3977
|
+
const tags = (owned || []).filter((entry) => entry.id && entry.title).map((entry) => ({
|
|
3978
|
+
name: tagName(entry.title, entry.id),
|
|
3979
|
+
status: joined.has(entry.id) ? "active" : "inactive"
|
|
3963
3980
|
}));
|
|
3964
3981
|
if (tags.length > 0) {
|
|
3965
3982
|
await api3("/lists/" + audience + "/members/" + hash + "/tags", {
|
|
@@ -3975,6 +3992,7 @@ var mailchimp_default2 = {
|
|
|
3975
3992
|
// Merged into `context` for later steps in this run.
|
|
3976
3993
|
context: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash },
|
|
3977
3994
|
message: suppressed ? "Synced to Mailchimp as unsubscribed \u2014 this contact has opted out." : "Synced to the Mailchimp audience.",
|
|
3995
|
+
request: request2,
|
|
3978
3996
|
// Recorded on the run for support to read back, not a write
|
|
3979
3997
|
// instruction — the hook has already written what it needed to.
|
|
3980
3998
|
response: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash }
|
|
@@ -4010,7 +4028,7 @@ var mailchimp_default2 = {
|
|
|
4010
4028
|
if (!audience) return { message: "No Mailchimp audience is chosen for this connection.", skipped: true };
|
|
4011
4029
|
const segment = await currentSegment({ read, segment: context == null ? void 0 : context.segment });
|
|
4012
4030
|
if (!(segment == null ? void 0 : segment.id) || segment.system) return { message: "That segment is not one this connection publishes.", skipped: true };
|
|
4013
|
-
const name = tagName(segment.title);
|
|
4031
|
+
const name = tagName(segment.title, segment.id);
|
|
4014
4032
|
const existing = segmentRowFor({ connection: connection2, segment });
|
|
4015
4033
|
let id = null;
|
|
4016
4034
|
if (existing == null ? void 0 : existing.id) {
|
|
@@ -4582,10 +4600,6 @@ var shopify_default2 = {
|
|
|
4582
4600
|
if (!(context == null ? void 0 : context.lead)) return { message: "Lead id is missing.", request: request2, response: { skipped: true }, skipped: true };
|
|
4583
4601
|
if (!(discount == null ? void 0 : discount.id)) return { message: "Discount is not configured on this step.", request: request2, response: { skipped: true }, skipped: true };
|
|
4584
4602
|
const adminAccessToken = await adminToken();
|
|
4585
|
-
if (!context.shopifyCustomerId) {
|
|
4586
|
-
const customer = await shopify.admin.getOrCreateCustomer({ adminAccessToken, domain: connection2.shop, email: context.email });
|
|
4587
|
-
if (!(customer == null ? void 0 : customer.id)) return { message: "Shopify did not return a customer id \u2014 create/lookup failed.", request: request2, response: { skipped: true }, skipped: true };
|
|
4588
|
-
}
|
|
4589
4603
|
const discountCode = await shopify.admin.createDiscountCode({
|
|
4590
4604
|
adminAccessToken,
|
|
4591
4605
|
code: "DB-" + generateDiscountCode(),
|
|
@@ -5029,7 +5043,7 @@ var shopify_default2 = {
|
|
|
5029
5043
|
//
|
|
5030
5044
|
// The shell has already refused a missing or inactive Shopify connection,
|
|
5031
5045
|
// so what is left is the two things only this hook can know are wrong.
|
|
5032
|
-
product: async ({ connection: connection2, context, workflow }, { mintId } = {}) => {
|
|
5046
|
+
product: async ({ connection: connection2, context, workflow }, { mintId, read } = {}) => {
|
|
5033
5047
|
const request2 = {
|
|
5034
5048
|
numericId: (context == null ? void 0 : context.id) || null,
|
|
5035
5049
|
organizationId: workflow.organization,
|
|
@@ -5038,7 +5052,13 @@ var shopify_default2 = {
|
|
|
5038
5052
|
if (!(context == null ? void 0 : context.id)) return { message: "Skipped \u2014 product webhook payload had no id.", request: request2, response: { skipped: true }, skipped: true };
|
|
5039
5053
|
if (!connection2.shop) return { message: "Skipped \u2014 Shopify connection is missing shop domain.", request: request2, response: { skipped: true }, skipped: true };
|
|
5040
5054
|
const providerId = "gid://shopify/Product/" + context.id;
|
|
5041
|
-
const
|
|
5055
|
+
const query = {
|
|
5056
|
+
"provider.id": providerId,
|
|
5057
|
+
"provider.slug": "shopify",
|
|
5058
|
+
"source.domain": connection2.shop
|
|
5059
|
+
};
|
|
5060
|
+
const existing = (read == null ? void 0 : read.get) ? await read.get({ collection: "product", query }) : null;
|
|
5061
|
+
const productId = (existing == null ? void 0 : existing.id) || mintId();
|
|
5042
5062
|
return {
|
|
5043
5063
|
enqueues: [{
|
|
5044
5064
|
data: { product: productId, providerId, shop: connection2.shop },
|
|
@@ -5068,11 +5088,7 @@ var shopify_default2 = {
|
|
|
5068
5088
|
},
|
|
5069
5089
|
operation: "update",
|
|
5070
5090
|
options: { upsert: true },
|
|
5071
|
-
query
|
|
5072
|
-
"provider.id": providerId,
|
|
5073
|
-
"provider.slug": "shopify",
|
|
5074
|
-
"source.domain": connection2.shop
|
|
5075
|
-
}
|
|
5091
|
+
query
|
|
5076
5092
|
}]
|
|
5077
5093
|
};
|
|
5078
5094
|
}
|
|
@@ -5342,7 +5358,7 @@ var shopify_default2 = {
|
|
|
5342
5358
|
options: { jobId: "connection.update.register." + workflow.connection + "." + (0, import_node_crypto6.randomUUID)() },
|
|
5343
5359
|
queue: "connection"
|
|
5344
5360
|
}],
|
|
5345
|
-
message: ((scopesMissing == null ? void 0 : scopesMissing.length) ? "Health check: ping ok,
|
|
5361
|
+
message: ((scopesMissing == null ? void 0 : scopesMissing.length) ? "Health check: ping ok, scopes reconciled \u2014 connection errored, granted scopes are missing: " + scopesMissing.join(", ") + "." : refreshTokenRotated ? "Health check passed \u2014 refresh token rotated, ping ok, scopes reconciled." : "Health check passed \u2014 ping ok, scopes reconciled.") + (sourceRepaired ? " Billing source repaired from the live shop." : "") + (metered === null ? " No usage line on the store's approved plan \u2014 order billing needs the merchant to approve the updated plan." : ""),
|
|
5346
5362
|
request: request2,
|
|
5347
5363
|
response: {
|
|
5348
5364
|
billingProbeFailed,
|
package/dist/providers.js
CHANGED
|
@@ -478,6 +478,7 @@ var row = ({ connection: connection2, data: data2, manifest, row: described }) =
|
|
|
478
478
|
};
|
|
479
479
|
};
|
|
480
480
|
var segmentRowWrites = ({ connection: connection2, data: data2, manifest, row: described, segment }) => {
|
|
481
|
+
if ((described == null ? void 0 : described.id) === void 0 || (described == null ? void 0 : described.id) === null) return [];
|
|
481
482
|
const built = row({ connection: connection2, data: data2, manifest, row: described });
|
|
482
483
|
return [
|
|
483
484
|
// PUSH IF ABSENT. The $ne guard is what makes a second concurrent register
|
|
@@ -725,10 +726,11 @@ var attentive_default2 = {
|
|
|
725
726
|
sync: async ({ lead, settings, suppressed, token }, { fetcher } = {}) => {
|
|
726
727
|
var _a, _b, _c, _d;
|
|
727
728
|
const segment = settings == null ? void 0 : settings.segment;
|
|
728
|
-
if (!segment) return { message: "No Attentive segment is chosen for this connection.", skipped: true };
|
|
729
729
|
const email = ((_b = (_a = lead == null ? void 0 : lead.canonical) == null ? void 0 : _a.email) == null ? void 0 : _b.value) || (lead == null ? void 0 : lead.email);
|
|
730
730
|
const phone = toE164(((_d = (_c = lead == null ? void 0 : lead.canonical) == null ? void 0 : _c.phone) == null ? void 0 : _d.value) || (lead == null ? void 0 : lead.phone));
|
|
731
|
-
|
|
731
|
+
const request2 = { email: email || null, phone: phone || null, segment: (settings == null ? void 0 : settings.segment) || null };
|
|
732
|
+
if (!segment) return { message: "No Attentive segment is chosen for this connection.", request: request2, skipped: true };
|
|
733
|
+
if (!email && !phone) return { message: "That lead has no email address or phone number to sync.", request: request2, skipped: true };
|
|
732
734
|
const user = {
|
|
733
735
|
...email && { email },
|
|
734
736
|
...phone && { phone }
|
|
@@ -750,6 +752,7 @@ var attentive_default2 = {
|
|
|
750
752
|
});
|
|
751
753
|
return {
|
|
752
754
|
message: "Attentive accepted an unsubscribe for this contact \u2014 they have opted out.",
|
|
755
|
+
request: request2,
|
|
753
756
|
response: { accepted: true, unsubscribed: true }
|
|
754
757
|
};
|
|
755
758
|
}
|
|
@@ -790,6 +793,7 @@ var attentive_default2 = {
|
|
|
790
793
|
// queued them — a merchant who reads "synced" and looks for the person
|
|
791
794
|
// in Attentive a second later has been told the wrong thing.
|
|
792
795
|
message: "Attentive accepted this contact for the segment. Attentive processes these asynchronously, so it appears there shortly.",
|
|
796
|
+
request: request2,
|
|
793
797
|
response: {
|
|
794
798
|
accepted: true,
|
|
795
799
|
...(membership == null ? void 0 : membership.batchJobId) && { batchJobId: membership.batchJobId }
|
|
@@ -2806,7 +2810,7 @@ var drawbridge_default2 = {
|
|
|
2806
2810
|
text: { max: 2e3, type: "string" }
|
|
2807
2811
|
},
|
|
2808
2812
|
triggers: ["schedule.day", "schedule.week", "schedule.month"],
|
|
2809
|
-
usage: { actions:
|
|
2813
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2810
2814
|
}),
|
|
2811
2815
|
// To organization MEMBERS. Never suppressed — an entrant's opt-out must
|
|
2812
2816
|
// not silence an alert to staff — and not billed.
|
|
@@ -2831,10 +2835,14 @@ var drawbridge_default2 = {
|
|
|
2831
2835
|
subject: { max: 150, type: "string" }
|
|
2832
2836
|
},
|
|
2833
2837
|
triggers: ["lead.insert"],
|
|
2834
|
-
//
|
|
2835
|
-
//
|
|
2836
|
-
//
|
|
2837
|
-
|
|
2838
|
+
// EVERY STEP IN A BILLABLE WORKFLOW COUNTS, team mail included. This
|
|
2839
|
+
// leaves our SendGrid account and costs the same to send as any other
|
|
2840
|
+
// email, so it carries the same weight. Whether anything is charged at
|
|
2841
|
+
// all is the workflow's `billable` tag, not the step's audience — and
|
|
2842
|
+
// this step triggers on lead.insert, so a merchant notifying
|
|
2843
|
+
// themselves on every entry spends against their allowance twice per
|
|
2844
|
+
// lead, once for the entry and once for the notification.
|
|
2845
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2838
2846
|
}),
|
|
2839
2847
|
// To a LEAD. Suppression applies and the send is billed.
|
|
2840
2848
|
send: () => ({
|
|
@@ -2943,7 +2951,7 @@ var api2 = async (path, { fetcher = fetch, method = "GET", payload, token }) =>
|
|
|
2943
2951
|
}
|
|
2944
2952
|
return response.status === 204 ? null : response.json();
|
|
2945
2953
|
};
|
|
2946
|
-
var segmentName = (title) => "Drawbridge: " + title;
|
|
2954
|
+
var segmentName = (title, id) => "Drawbridge: " + title + " (" + String(id).slice(-6) + ")";
|
|
2947
2955
|
var canManageSegments = (settings) => String((settings == null ? void 0 : settings.scope) || "").split(/\s+/).includes("segments:write");
|
|
2948
2956
|
var klaviyo_default2 = {
|
|
2949
2957
|
// OAuth 2.1, and PKCE is REQUIRED rather than recommended: Klaviyo refuses an
|
|
@@ -3190,11 +3198,12 @@ var klaviyo_default2 = {
|
|
|
3190
3198
|
// different thing from deleting the profile.
|
|
3191
3199
|
remove: false,
|
|
3192
3200
|
sync: async ({ context, lead, segments, settings, suppressed, token }, { fetcher } = {}) => {
|
|
3193
|
-
var _a, _b, _c;
|
|
3201
|
+
var _a, _b, _c, _d, _e;
|
|
3194
3202
|
const list = settings == null ? void 0 : settings.list;
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3203
|
+
const request2 = { email: ((_b = (_a = lead == null ? void 0 : lead.canonical) == null ? void 0 : _a.email) == null ? void 0 : _b.value) || (lead == null ? void 0 : lead.email) || null, list: (settings == null ? void 0 : settings.list) || null };
|
|
3204
|
+
if (!list) return { message: "No Klaviyo list is chosen for this connection.", request: request2, skipped: true };
|
|
3205
|
+
const email = ((_d = (_c = lead == null ? void 0 : lead.canonical) == null ? void 0 : _c.email) == null ? void 0 : _d.value) || (lead == null ? void 0 : lead.email);
|
|
3206
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3198
3207
|
const person = (context == null ? void 0 : context.contact) || null;
|
|
3199
3208
|
const totals = (person == null ? void 0 : person.totals) || {};
|
|
3200
3209
|
const count = (value) => typeof value === "number" ? value : (value == null ? void 0 : value.total) || 0;
|
|
@@ -3251,8 +3260,8 @@ var klaviyo_default2 = {
|
|
|
3251
3260
|
},
|
|
3252
3261
|
token
|
|
3253
3262
|
});
|
|
3254
|
-
const profileId = (
|
|
3255
|
-
if (!profileId) return { message: "Klaviyo returned no profile id.", skipped: true };
|
|
3263
|
+
const profileId = (_e = profile == null ? void 0 : profile.data) == null ? void 0 : _e.id;
|
|
3264
|
+
if (!profileId) return { message: "Klaviyo returned no profile id.", request: request2, skipped: true };
|
|
3256
3265
|
await api2("/profile-subscription-bulk-create-jobs/", {
|
|
3257
3266
|
fetcher,
|
|
3258
3267
|
method: "POST",
|
|
@@ -3281,6 +3290,7 @@ var klaviyo_default2 = {
|
|
|
3281
3290
|
// Merged into `context` for later steps in this run.
|
|
3282
3291
|
context: { klaviyoProfileId: profileId },
|
|
3283
3292
|
message: suppressed ? "Synced to Klaviyo as unsubscribed \u2014 this contact has opted out." : "Synced to the Klaviyo list.",
|
|
3293
|
+
request: request2,
|
|
3284
3294
|
// Recorded on the run for support to read back, not a write
|
|
3285
3295
|
// instruction — the hook has already written what it needed to.
|
|
3286
3296
|
response: { klaviyoProfileId: profileId }
|
|
@@ -3316,7 +3326,7 @@ var klaviyo_default2 = {
|
|
|
3316
3326
|
skipped: true
|
|
3317
3327
|
};
|
|
3318
3328
|
}
|
|
3319
|
-
const name = segmentName(segment.title);
|
|
3329
|
+
const name = segmentName(segment.title, segment.id);
|
|
3320
3330
|
const existing = segmentRowFor({ connection: connection2, segment });
|
|
3321
3331
|
let id = null;
|
|
3322
3332
|
if (existing == null ? void 0 : existing.id) {
|
|
@@ -3723,7 +3733,10 @@ var api3 = async (path, { dc, fetcher = fetch, method = "GET", payload, token })
|
|
|
3723
3733
|
};
|
|
3724
3734
|
var subscriberHash = (email) => createHash2("md5").update(String(email).trim().toLowerCase()).digest("hex");
|
|
3725
3735
|
var TAG_NAME_LIMIT = 100;
|
|
3726
|
-
var tagName = (title) =>
|
|
3736
|
+
var tagName = (title, id) => {
|
|
3737
|
+
const suffix = " (" + String(id).slice(-6) + ")";
|
|
3738
|
+
return ("Drawbridge: " + title).slice(0, TAG_NAME_LIMIT - suffix.length) + suffix;
|
|
3739
|
+
};
|
|
3727
3740
|
var mailchimp_default2 = {
|
|
3728
3741
|
// OAUTH 2, authorization code. Every url below is quoted from
|
|
3729
3742
|
// mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/ rather
|
|
@@ -3861,15 +3874,16 @@ var mailchimp_default2 = {
|
|
|
3861
3874
|
// why there is no create-or-update branch here. Quoted from Mailchimp's
|
|
3862
3875
|
// Marketing API reference for the list-members resource.
|
|
3863
3876
|
sync: async ({ connection: connection2, lead, segments, settings, suppressed, token }, { fetcher, read } = {}) => {
|
|
3864
|
-
var _a, _b, _c;
|
|
3877
|
+
var _a, _b, _c, _d, _e;
|
|
3865
3878
|
const audience = settings == null ? void 0 : settings.audience;
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3879
|
+
const request2 = { audience: (settings == null ? void 0 : settings.audience) || null, email: ((_b = (_a = lead == null ? void 0 : lead.canonical) == null ? void 0 : _a.email) == null ? void 0 : _b.value) || (lead == null ? void 0 : lead.email) || null };
|
|
3880
|
+
if (!audience) return { message: "No Mailchimp audience is chosen for this connection.", request: request2, skipped: true };
|
|
3881
|
+
const email = ((_d = (_c = lead == null ? void 0 : lead.canonical) == null ? void 0 : _c.email) == null ? void 0 : _d.value) || (lead == null ? void 0 : lead.email);
|
|
3882
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3869
3883
|
const hash = subscriberHash(email);
|
|
3870
3884
|
const [firstName, ...restOfName] = String((lead == null ? void 0 : lead.name) || "").trim().split(/\s+/).filter(Boolean);
|
|
3871
3885
|
const lastName = restOfName.join(" ");
|
|
3872
|
-
const phone = ((
|
|
3886
|
+
const phone = ((_e = lead == null ? void 0 : lead.phone) == null ? void 0 : _e.number) || null;
|
|
3873
3887
|
const mergeFields = {
|
|
3874
3888
|
...firstName && { FNAME: firstName },
|
|
3875
3889
|
...lastName && { LNAME: lastName },
|
|
@@ -3903,14 +3917,17 @@ var mailchimp_default2 = {
|
|
|
3903
3917
|
}
|
|
3904
3918
|
},
|
|
3905
3919
|
{
|
|
3906
|
-
|
|
3920
|
+
// THE ID AS WELL AS THE TITLE, because the tag name carries it —
|
|
3921
|
+
// see tagName. Without it every name here would end in the
|
|
3922
|
+
// string 'undefined' and match nothing register wrote.
|
|
3923
|
+
$project: { _id: 0, id: 1, title: 1 }
|
|
3907
3924
|
}
|
|
3908
3925
|
]
|
|
3909
3926
|
});
|
|
3910
|
-
const joined = new Set(segments.map((entry) => entry.
|
|
3911
|
-
const tags = (owned || []).
|
|
3912
|
-
name: tagName(title),
|
|
3913
|
-
status: joined.has(
|
|
3927
|
+
const joined = new Set(segments.map((entry) => entry.id));
|
|
3928
|
+
const tags = (owned || []).filter((entry) => entry.id && entry.title).map((entry) => ({
|
|
3929
|
+
name: tagName(entry.title, entry.id),
|
|
3930
|
+
status: joined.has(entry.id) ? "active" : "inactive"
|
|
3914
3931
|
}));
|
|
3915
3932
|
if (tags.length > 0) {
|
|
3916
3933
|
await api3("/lists/" + audience + "/members/" + hash + "/tags", {
|
|
@@ -3926,6 +3943,7 @@ var mailchimp_default2 = {
|
|
|
3926
3943
|
// Merged into `context` for later steps in this run.
|
|
3927
3944
|
context: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash },
|
|
3928
3945
|
message: suppressed ? "Synced to Mailchimp as unsubscribed \u2014 this contact has opted out." : "Synced to the Mailchimp audience.",
|
|
3946
|
+
request: request2,
|
|
3929
3947
|
// Recorded on the run for support to read back, not a write
|
|
3930
3948
|
// instruction — the hook has already written what it needed to.
|
|
3931
3949
|
response: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash }
|
|
@@ -3961,7 +3979,7 @@ var mailchimp_default2 = {
|
|
|
3961
3979
|
if (!audience) return { message: "No Mailchimp audience is chosen for this connection.", skipped: true };
|
|
3962
3980
|
const segment = await currentSegment({ read, segment: context == null ? void 0 : context.segment });
|
|
3963
3981
|
if (!(segment == null ? void 0 : segment.id) || segment.system) return { message: "That segment is not one this connection publishes.", skipped: true };
|
|
3964
|
-
const name = tagName(segment.title);
|
|
3982
|
+
const name = tagName(segment.title, segment.id);
|
|
3965
3983
|
const existing = segmentRowFor({ connection: connection2, segment });
|
|
3966
3984
|
let id = null;
|
|
3967
3985
|
if (existing == null ? void 0 : existing.id) {
|
|
@@ -4533,10 +4551,6 @@ var shopify_default2 = {
|
|
|
4533
4551
|
if (!(context == null ? void 0 : context.lead)) return { message: "Lead id is missing.", request: request2, response: { skipped: true }, skipped: true };
|
|
4534
4552
|
if (!(discount == null ? void 0 : discount.id)) return { message: "Discount is not configured on this step.", request: request2, response: { skipped: true }, skipped: true };
|
|
4535
4553
|
const adminAccessToken = await adminToken();
|
|
4536
|
-
if (!context.shopifyCustomerId) {
|
|
4537
|
-
const customer = await shopify.admin.getOrCreateCustomer({ adminAccessToken, domain: connection2.shop, email: context.email });
|
|
4538
|
-
if (!(customer == null ? void 0 : customer.id)) return { message: "Shopify did not return a customer id \u2014 create/lookup failed.", request: request2, response: { skipped: true }, skipped: true };
|
|
4539
|
-
}
|
|
4540
4554
|
const discountCode = await shopify.admin.createDiscountCode({
|
|
4541
4555
|
adminAccessToken,
|
|
4542
4556
|
code: "DB-" + generateDiscountCode(),
|
|
@@ -4980,7 +4994,7 @@ var shopify_default2 = {
|
|
|
4980
4994
|
//
|
|
4981
4995
|
// The shell has already refused a missing or inactive Shopify connection,
|
|
4982
4996
|
// so what is left is the two things only this hook can know are wrong.
|
|
4983
|
-
product: async ({ connection: connection2, context, workflow }, { mintId } = {}) => {
|
|
4997
|
+
product: async ({ connection: connection2, context, workflow }, { mintId, read } = {}) => {
|
|
4984
4998
|
const request2 = {
|
|
4985
4999
|
numericId: (context == null ? void 0 : context.id) || null,
|
|
4986
5000
|
organizationId: workflow.organization,
|
|
@@ -4989,7 +5003,13 @@ var shopify_default2 = {
|
|
|
4989
5003
|
if (!(context == null ? void 0 : context.id)) return { message: "Skipped \u2014 product webhook payload had no id.", request: request2, response: { skipped: true }, skipped: true };
|
|
4990
5004
|
if (!connection2.shop) return { message: "Skipped \u2014 Shopify connection is missing shop domain.", request: request2, response: { skipped: true }, skipped: true };
|
|
4991
5005
|
const providerId = "gid://shopify/Product/" + context.id;
|
|
4992
|
-
const
|
|
5006
|
+
const query = {
|
|
5007
|
+
"provider.id": providerId,
|
|
5008
|
+
"provider.slug": "shopify",
|
|
5009
|
+
"source.domain": connection2.shop
|
|
5010
|
+
};
|
|
5011
|
+
const existing = (read == null ? void 0 : read.get) ? await read.get({ collection: "product", query }) : null;
|
|
5012
|
+
const productId = (existing == null ? void 0 : existing.id) || mintId();
|
|
4993
5013
|
return {
|
|
4994
5014
|
enqueues: [{
|
|
4995
5015
|
data: { product: productId, providerId, shop: connection2.shop },
|
|
@@ -5019,11 +5039,7 @@ var shopify_default2 = {
|
|
|
5019
5039
|
},
|
|
5020
5040
|
operation: "update",
|
|
5021
5041
|
options: { upsert: true },
|
|
5022
|
-
query
|
|
5023
|
-
"provider.id": providerId,
|
|
5024
|
-
"provider.slug": "shopify",
|
|
5025
|
-
"source.domain": connection2.shop
|
|
5026
|
-
}
|
|
5042
|
+
query
|
|
5027
5043
|
}]
|
|
5028
5044
|
};
|
|
5029
5045
|
}
|
|
@@ -5293,7 +5309,7 @@ var shopify_default2 = {
|
|
|
5293
5309
|
options: { jobId: "connection.update.register." + workflow.connection + "." + randomUUID2() },
|
|
5294
5310
|
queue: "connection"
|
|
5295
5311
|
}],
|
|
5296
|
-
message: ((scopesMissing == null ? void 0 : scopesMissing.length) ? "Health check: ping ok,
|
|
5312
|
+
message: ((scopesMissing == null ? void 0 : scopesMissing.length) ? "Health check: ping ok, scopes reconciled \u2014 connection errored, granted scopes are missing: " + scopesMissing.join(", ") + "." : refreshTokenRotated ? "Health check passed \u2014 refresh token rotated, ping ok, scopes reconciled." : "Health check passed \u2014 ping ok, scopes reconciled.") + (sourceRepaired ? " Billing source repaired from the live shop." : "") + (metered === null ? " No usage line on the store's approved plan \u2014 order billing needs the merchant to approve the updated plan." : ""),
|
|
5297
5313
|
request: request2,
|
|
5298
5314
|
response: {
|
|
5299
5315
|
billingProbeFailed,
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"tinycolor2": "1.6.0"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
|
-
"@drawbridge/drawbridge-agents": "0.1.
|
|
16
|
+
"@drawbridge/drawbridge-agents": "0.1.59",
|
|
17
17
|
"@node-oauth/oauth2-server": "5.3.0",
|
|
18
18
|
"tsup": "8.5.1",
|
|
19
19
|
"typescript": "5.9.3",
|
|
@@ -226,5 +226,5 @@
|
|
|
226
226
|
"prepublishOnly": ". \"$HOME/.nvm/nvm.sh\" && nvm use && tsup && node --test"
|
|
227
227
|
},
|
|
228
228
|
"types": "dist/index.d.ts",
|
|
229
|
-
"version": "0.0.
|
|
229
|
+
"version": "0.0.172"
|
|
230
230
|
}
|