@drawbridge/drawbridge-utils 0.0.170 → 0.0.171
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 +39 -26
- package/dist/connections/index.d.cts +74 -27
- package/dist/connections/index.d.ts +74 -27
- package/dist/connections/index.js +39 -26
- package/dist/providers.cjs +39 -26
- package/dist/providers.js +39 -26
- package/package.json +1 -1
|
@@ -865,10 +865,11 @@ var attentive_default2 = {
|
|
|
865
865
|
sync: async ({ lead, settings, suppressed, token }, { fetcher } = {}) => {
|
|
866
866
|
var _a, _b, _c, _d;
|
|
867
867
|
const segment = settings == null ? void 0 : settings.segment;
|
|
868
|
-
if (!segment) return { message: "No Attentive segment is chosen for this connection.", skipped: true };
|
|
869
868
|
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);
|
|
870
869
|
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));
|
|
871
|
-
|
|
870
|
+
const request2 = { email: email || null, phone: phone || null, segment: (settings == null ? void 0 : settings.segment) || null };
|
|
871
|
+
if (!segment) return { message: "No Attentive segment is chosen for this connection.", request: request2, skipped: true };
|
|
872
|
+
if (!email && !phone) return { message: "That lead has no email address or phone number to sync.", request: request2, skipped: true };
|
|
872
873
|
const user = {
|
|
873
874
|
...email && { email },
|
|
874
875
|
...phone && { phone }
|
|
@@ -890,6 +891,7 @@ var attentive_default2 = {
|
|
|
890
891
|
});
|
|
891
892
|
return {
|
|
892
893
|
message: "Attentive accepted an unsubscribe for this contact \u2014 they have opted out.",
|
|
894
|
+
request: request2,
|
|
893
895
|
response: { accepted: true, unsubscribed: true }
|
|
894
896
|
};
|
|
895
897
|
}
|
|
@@ -930,6 +932,7 @@ var attentive_default2 = {
|
|
|
930
932
|
// queued them — a merchant who reads "synced" and looks for the person
|
|
931
933
|
// in Attentive a second later has been told the wrong thing.
|
|
932
934
|
message: "Attentive accepted this contact for the segment. Attentive processes these asynchronously, so it appears there shortly.",
|
|
935
|
+
request: request2,
|
|
933
936
|
response: {
|
|
934
937
|
accepted: true,
|
|
935
938
|
...(membership == null ? void 0 : membership.batchJobId) && { batchJobId: membership.batchJobId }
|
|
@@ -2946,7 +2949,7 @@ var drawbridge_default2 = {
|
|
|
2946
2949
|
text: { max: 2e3, type: "string" }
|
|
2947
2950
|
},
|
|
2948
2951
|
triggers: ["schedule.day", "schedule.week", "schedule.month"],
|
|
2949
|
-
usage: { actions:
|
|
2952
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2950
2953
|
}),
|
|
2951
2954
|
// To organization MEMBERS. Never suppressed — an entrant's opt-out must
|
|
2952
2955
|
// not silence an alert to staff — and not billed.
|
|
@@ -2971,10 +2974,14 @@ var drawbridge_default2 = {
|
|
|
2971
2974
|
subject: { max: 150, type: "string" }
|
|
2972
2975
|
},
|
|
2973
2976
|
triggers: ["lead.insert"],
|
|
2974
|
-
//
|
|
2975
|
-
//
|
|
2976
|
-
//
|
|
2977
|
-
|
|
2977
|
+
// EVERY STEP IN A BILLABLE WORKFLOW COUNTS, team mail included. This
|
|
2978
|
+
// leaves our SendGrid account and costs the same to send as any other
|
|
2979
|
+
// email, so it carries the same weight. Whether anything is charged at
|
|
2980
|
+
// all is the workflow's `billable` tag, not the step's audience — and
|
|
2981
|
+
// this step triggers on lead.insert, so a merchant notifying
|
|
2982
|
+
// themselves on every entry spends against their allowance twice per
|
|
2983
|
+
// lead, once for the entry and once for the notification.
|
|
2984
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2978
2985
|
}),
|
|
2979
2986
|
// To a LEAD. Suppression applies and the send is billed.
|
|
2980
2987
|
send: () => ({
|
|
@@ -3330,11 +3337,12 @@ var klaviyo_default2 = {
|
|
|
3330
3337
|
// different thing from deleting the profile.
|
|
3331
3338
|
remove: false,
|
|
3332
3339
|
sync: async ({ context, lead, segments, settings, suppressed, token }, { fetcher } = {}) => {
|
|
3333
|
-
var _a, _b, _c;
|
|
3340
|
+
var _a, _b, _c, _d, _e;
|
|
3334
3341
|
const list = settings == null ? void 0 : settings.list;
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3342
|
+
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 };
|
|
3343
|
+
if (!list) return { message: "No Klaviyo list is chosen for this connection.", request: request2, skipped: true };
|
|
3344
|
+
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);
|
|
3345
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3338
3346
|
const person = (context == null ? void 0 : context.contact) || null;
|
|
3339
3347
|
const totals = (person == null ? void 0 : person.totals) || {};
|
|
3340
3348
|
const count = (value) => typeof value === "number" ? value : (value == null ? void 0 : value.total) || 0;
|
|
@@ -3391,8 +3399,8 @@ var klaviyo_default2 = {
|
|
|
3391
3399
|
},
|
|
3392
3400
|
token
|
|
3393
3401
|
});
|
|
3394
|
-
const profileId = (
|
|
3395
|
-
if (!profileId) return { message: "Klaviyo returned no profile id.", skipped: true };
|
|
3402
|
+
const profileId = (_e = profile == null ? void 0 : profile.data) == null ? void 0 : _e.id;
|
|
3403
|
+
if (!profileId) return { message: "Klaviyo returned no profile id.", request: request2, skipped: true };
|
|
3396
3404
|
await api2("/profile-subscription-bulk-create-jobs/", {
|
|
3397
3405
|
fetcher,
|
|
3398
3406
|
method: "POST",
|
|
@@ -3421,6 +3429,7 @@ var klaviyo_default2 = {
|
|
|
3421
3429
|
// Merged into `context` for later steps in this run.
|
|
3422
3430
|
context: { klaviyoProfileId: profileId },
|
|
3423
3431
|
message: suppressed ? "Synced to Klaviyo as unsubscribed \u2014 this contact has opted out." : "Synced to the Klaviyo list.",
|
|
3432
|
+
request: request2,
|
|
3424
3433
|
// Recorded on the run for support to read back, not a write
|
|
3425
3434
|
// instruction — the hook has already written what it needed to.
|
|
3426
3435
|
response: { klaviyoProfileId: profileId }
|
|
@@ -4001,15 +4010,16 @@ var mailchimp_default2 = {
|
|
|
4001
4010
|
// why there is no create-or-update branch here. Quoted from Mailchimp's
|
|
4002
4011
|
// Marketing API reference for the list-members resource.
|
|
4003
4012
|
sync: async ({ connection: connection2, lead, segments, settings, suppressed, token }, { fetcher, read } = {}) => {
|
|
4004
|
-
var _a, _b, _c;
|
|
4013
|
+
var _a, _b, _c, _d, _e;
|
|
4005
4014
|
const audience = settings == null ? void 0 : settings.audience;
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4015
|
+
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 };
|
|
4016
|
+
if (!audience) return { message: "No Mailchimp audience is chosen for this connection.", request: request2, skipped: true };
|
|
4017
|
+
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);
|
|
4018
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
4009
4019
|
const hash = subscriberHash(email);
|
|
4010
4020
|
const [firstName, ...restOfName] = String((lead == null ? void 0 : lead.name) || "").trim().split(/\s+/).filter(Boolean);
|
|
4011
4021
|
const lastName = restOfName.join(" ");
|
|
4012
|
-
const phone = ((
|
|
4022
|
+
const phone = ((_e = lead == null ? void 0 : lead.phone) == null ? void 0 : _e.number) || null;
|
|
4013
4023
|
const mergeFields = {
|
|
4014
4024
|
...firstName && { FNAME: firstName },
|
|
4015
4025
|
...lastName && { LNAME: lastName },
|
|
@@ -4066,6 +4076,7 @@ var mailchimp_default2 = {
|
|
|
4066
4076
|
// Merged into `context` for later steps in this run.
|
|
4067
4077
|
context: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash },
|
|
4068
4078
|
message: suppressed ? "Synced to Mailchimp as unsubscribed \u2014 this contact has opted out." : "Synced to the Mailchimp audience.",
|
|
4079
|
+
request: request2,
|
|
4069
4080
|
// Recorded on the run for support to read back, not a write
|
|
4070
4081
|
// instruction — the hook has already written what it needed to.
|
|
4071
4082
|
response: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash }
|
|
@@ -5101,7 +5112,7 @@ var shopify_default2 = {
|
|
|
5101
5112
|
//
|
|
5102
5113
|
// The shell has already refused a missing or inactive Shopify connection,
|
|
5103
5114
|
// so what is left is the two things only this hook can know are wrong.
|
|
5104
|
-
product: async ({ connection: connection2, context, workflow }, { mintId } = {}) => {
|
|
5115
|
+
product: async ({ connection: connection2, context, workflow }, { mintId, read } = {}) => {
|
|
5105
5116
|
const request2 = {
|
|
5106
5117
|
numericId: (context == null ? void 0 : context.id) || null,
|
|
5107
5118
|
organizationId: workflow.organization,
|
|
@@ -5110,7 +5121,13 @@ var shopify_default2 = {
|
|
|
5110
5121
|
if (!(context == null ? void 0 : context.id)) return { message: "Skipped \u2014 product webhook payload had no id.", request: request2, response: { skipped: true }, skipped: true };
|
|
5111
5122
|
if (!connection2.shop) return { message: "Skipped \u2014 Shopify connection is missing shop domain.", request: request2, response: { skipped: true }, skipped: true };
|
|
5112
5123
|
const providerId = "gid://shopify/Product/" + context.id;
|
|
5113
|
-
const
|
|
5124
|
+
const query = {
|
|
5125
|
+
"provider.id": providerId,
|
|
5126
|
+
"provider.slug": "shopify",
|
|
5127
|
+
"source.domain": connection2.shop
|
|
5128
|
+
};
|
|
5129
|
+
const existing = (read == null ? void 0 : read.get) ? await read.get({ collection: "product", query }) : null;
|
|
5130
|
+
const productId = (existing == null ? void 0 : existing.id) || mintId();
|
|
5114
5131
|
return {
|
|
5115
5132
|
enqueues: [{
|
|
5116
5133
|
data: { product: productId, providerId, shop: connection2.shop },
|
|
@@ -5140,11 +5157,7 @@ var shopify_default2 = {
|
|
|
5140
5157
|
},
|
|
5141
5158
|
operation: "update",
|
|
5142
5159
|
options: { upsert: true },
|
|
5143
|
-
query
|
|
5144
|
-
"provider.id": providerId,
|
|
5145
|
-
"provider.slug": "shopify",
|
|
5146
|
-
"source.domain": connection2.shop
|
|
5147
|
-
}
|
|
5160
|
+
query
|
|
5148
5161
|
}]
|
|
5149
5162
|
};
|
|
5150
5163
|
}
|
|
@@ -5414,7 +5427,7 @@ var shopify_default2 = {
|
|
|
5414
5427
|
options: { jobId: "connection.update.register." + workflow.connection + "." + (0, import_node_crypto6.randomUUID)() },
|
|
5415
5428
|
queue: "connection"
|
|
5416
5429
|
}],
|
|
5417
|
-
message: ((scopesMissing == null ? void 0 : scopesMissing.length) ? "Health check: ping ok,
|
|
5430
|
+
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." : ""),
|
|
5418
5431
|
request: request2,
|
|
5419
5432
|
response: {
|
|
5420
5433
|
billingProbeFailed,
|
|
@@ -1234,8 +1234,6 @@ var attentive = {
|
|
|
1234
1234
|
// status() already stops a connection reaching Active without a
|
|
1235
1235
|
// segment; this is the belt to that braces. A workflow saved before the
|
|
1236
1236
|
// segment was chosen must not silently write into nothing.
|
|
1237
|
-
if( ! segment ) return { message : 'No Attentive segment is chosen for this connection.', skipped : true };
|
|
1238
|
-
|
|
1239
1237
|
const email = lead?.canonical?.email?.value || lead?.email;
|
|
1240
1238
|
|
|
1241
1239
|
// E.164 OR NOTHING. Attentive requires it, and a national-format number
|
|
@@ -1243,9 +1241,22 @@ var attentive = {
|
|
|
1243
1241
|
// and the email carries the sync instead.
|
|
1244
1242
|
const phone = toE164( lead?.canonical?.phone?.value || lead?.phone );
|
|
1245
1243
|
|
|
1244
|
+
// WHAT WENT TO THE VENDOR, so a sync run in the activity feed can
|
|
1245
|
+
// answer what was actually sent. The three contact syncs were the only
|
|
1246
|
+
// hook family carrying none, and a merchant opening one saw
|
|
1247
|
+
// request : null. Attentive identifies on either channel, so both are
|
|
1248
|
+
// recorded — which of the two carried the sync is the first question
|
|
1249
|
+
// anyone debugging one of these asks.
|
|
1250
|
+
const request = { email : email || null, phone : phone || null, segment : settings?.segment || null };
|
|
1251
|
+
|
|
1252
|
+
// status() already stops a connection reaching Active without a
|
|
1253
|
+
// segment; this is the belt to that braces. A workflow saved before the
|
|
1254
|
+
// segment was chosen must not silently write into nothing.
|
|
1255
|
+
if( ! segment ) return { message : 'No Attentive segment is chosen for this connection.', request, skipped : true };
|
|
1256
|
+
|
|
1246
1257
|
// Their `user` requires phone OR email. With neither there is nobody to
|
|
1247
1258
|
// subscribe, so this is a skip rather than a failure.
|
|
1248
|
-
if( ! email && ! phone ) return { message : 'That lead has no email address or phone number to sync.', skipped : true };
|
|
1259
|
+
if( ! email && ! phone ) return { message : 'That lead has no email address or phone number to sync.', request, skipped : true };
|
|
1249
1260
|
|
|
1250
1261
|
const user = {
|
|
1251
1262
|
...( email && { email }),
|
|
@@ -1285,6 +1296,7 @@ var attentive = {
|
|
|
1285
1296
|
|
|
1286
1297
|
return {
|
|
1287
1298
|
message : 'Attentive accepted an unsubscribe for this contact — they have opted out.',
|
|
1299
|
+
request,
|
|
1288
1300
|
response : { accepted : true, unsubscribed : true }
|
|
1289
1301
|
};
|
|
1290
1302
|
|
|
@@ -1332,6 +1344,7 @@ var attentive = {
|
|
|
1332
1344
|
// queued them — a merchant who reads "synced" and looks for the person
|
|
1333
1345
|
// in Attentive a second later has been told the wrong thing.
|
|
1334
1346
|
message : 'Attentive accepted this contact for the segment. Attentive processes these asynchronously, so it appears there shortly.',
|
|
1347
|
+
request,
|
|
1335
1348
|
response : {
|
|
1336
1349
|
accepted : true,
|
|
1337
1350
|
...( membership?.batchJobId && { batchJobId : membership.batchJobId })
|
|
@@ -3353,7 +3366,7 @@ var drawbridge = {
|
|
|
3353
3366
|
text : { max : 2000, type : 'string' }
|
|
3354
3367
|
},
|
|
3355
3368
|
triggers : [ 'schedule.day', 'schedule.week', 'schedule.month' ],
|
|
3356
|
-
usage : { actions :
|
|
3369
|
+
usage : { actions : channels.email.actionsPerSend }
|
|
3357
3370
|
}),
|
|
3358
3371
|
|
|
3359
3372
|
// To organization MEMBERS. Never suppressed — an entrant's opt-out must
|
|
@@ -3379,10 +3392,14 @@ var drawbridge = {
|
|
|
3379
3392
|
subject : { max : 150, type : 'string' }
|
|
3380
3393
|
},
|
|
3381
3394
|
triggers : [ 'lead.insert' ],
|
|
3382
|
-
//
|
|
3383
|
-
//
|
|
3384
|
-
//
|
|
3385
|
-
|
|
3395
|
+
// EVERY STEP IN A BILLABLE WORKFLOW COUNTS, team mail included. This
|
|
3396
|
+
// leaves our SendGrid account and costs the same to send as any other
|
|
3397
|
+
// email, so it carries the same weight. Whether anything is charged at
|
|
3398
|
+
// all is the workflow's `billable` tag, not the step's audience — and
|
|
3399
|
+
// this step triggers on lead.insert, so a merchant notifying
|
|
3400
|
+
// themselves on every entry spends against their allowance twice per
|
|
3401
|
+
// lead, once for the entry and once for the notification.
|
|
3402
|
+
usage : { actions : channels.email.actionsPerSend }
|
|
3386
3403
|
}),
|
|
3387
3404
|
|
|
3388
3405
|
// To a LEAD. Suppression applies and the send is billed.
|
|
@@ -3835,11 +3852,16 @@ var klaviyo = {
|
|
|
3835
3852
|
// incomplete() already stops a connection reaching Active without a
|
|
3836
3853
|
// list; this is the belt to that braces. A workflow saved before the
|
|
3837
3854
|
// list was chosen must not silently write into nothing.
|
|
3838
|
-
|
|
3855
|
+
// WHAT WENT TO THE VENDOR, so a sync run in the activity feed can answer
|
|
3856
|
+
// what was actually sent. The three contact syncs were the only hook
|
|
3857
|
+
// family that carried none, so a merchant opening one saw request : null.
|
|
3858
|
+
const request = { email : lead?.canonical?.email?.value || lead?.email || null, list : settings?.list || null };
|
|
3859
|
+
|
|
3860
|
+
if( ! list ) return { message : 'No Klaviyo list is chosen for this connection.', request, skipped : true };
|
|
3839
3861
|
|
|
3840
3862
|
const email = lead?.canonical?.email?.value || lead?.email;
|
|
3841
3863
|
|
|
3842
|
-
if( ! email ) return { message : 'That lead has no email address to sync.', skipped : true };
|
|
3864
|
+
if( ! email ) return { message : 'That lead has no email address to sync.', request, skipped : true };
|
|
3843
3865
|
|
|
3844
3866
|
// A Klaviyo profile is keyed on an email, so IDENTITY comes from the
|
|
3845
3867
|
// lead. RANKING comes from the contact behind it, because only a
|
|
@@ -3934,7 +3956,7 @@ var klaviyo = {
|
|
|
3934
3956
|
|
|
3935
3957
|
const profileId = profile?.data?.id;
|
|
3936
3958
|
|
|
3937
|
-
if( ! profileId ) return { message : 'Klaviyo returned no profile id.', skipped : true };
|
|
3959
|
+
if( ! profileId ) return { message : 'Klaviyo returned no profile id.', request, skipped : true };
|
|
3938
3960
|
|
|
3939
3961
|
// SUPPRESSED PEOPLE ARE SYNCED AS UNSUBSCRIBED, NEVER OMITTED.
|
|
3940
3962
|
//
|
|
@@ -3977,6 +3999,7 @@ var klaviyo = {
|
|
|
3977
3999
|
message : suppressed
|
|
3978
4000
|
? 'Synced to Klaviyo as unsubscribed — this contact has opted out.'
|
|
3979
4001
|
: 'Synced to the Klaviyo list.',
|
|
4002
|
+
request,
|
|
3980
4003
|
// Recorded on the run for support to read back, not a write
|
|
3981
4004
|
// instruction — the hook has already written what it needed to.
|
|
3982
4005
|
response : { klaviyoProfileId : profileId }
|
|
@@ -4797,16 +4820,24 @@ var mailchimp = {
|
|
|
4797
4820
|
|
|
4798
4821
|
const audience = settings?.audience;
|
|
4799
4822
|
|
|
4823
|
+
// WHAT WENT TO THE VENDOR, recorded on the step so the activity feed
|
|
4824
|
+
// can answer "what did we actually send". Every other hook family
|
|
4825
|
+
// carries one; the three contact syncs did not, so a merchant opening
|
|
4826
|
+
// a sync run saw `request : null` and learned nothing — which is
|
|
4827
|
+
// exactly how it was reported. The audience and the address are the
|
|
4828
|
+
// whole identity of this write.
|
|
4829
|
+
const request = { audience : settings?.audience || null, email : lead?.canonical?.email?.value || lead?.email || null };
|
|
4830
|
+
|
|
4800
4831
|
// status() already stops a connection reaching Active without an
|
|
4801
4832
|
// audience; this is the belt to that braces. A workflow saved before
|
|
4802
4833
|
// the audience was chosen must not silently write into nothing.
|
|
4803
|
-
if( ! audience ) return { message : 'No Mailchimp audience is chosen for this connection.', skipped : true };
|
|
4834
|
+
if( ! audience ) return { message : 'No Mailchimp audience is chosen for this connection.', request, skipped : true };
|
|
4804
4835
|
|
|
4805
4836
|
const email = lead?.canonical?.email?.value || lead?.email;
|
|
4806
4837
|
|
|
4807
4838
|
// A Mailchimp member IS an email address — there is no other identity
|
|
4808
4839
|
// to write, so a lead without one is skipped rather than failed.
|
|
4809
|
-
if( ! email ) return { message : 'That lead has no email address to sync.', skipped : true };
|
|
4840
|
+
if( ! email ) return { message : 'That lead has no email address to sync.', request, skipped : true };
|
|
4810
4841
|
|
|
4811
4842
|
const hash = subscriberHash( email );
|
|
4812
4843
|
|
|
@@ -4962,6 +4993,7 @@ var mailchimp = {
|
|
|
4962
4993
|
message : suppressed
|
|
4963
4994
|
? 'Synced to Mailchimp as unsubscribed — this contact has opted out.'
|
|
4964
4995
|
: 'Synced to the Mailchimp audience.',
|
|
4996
|
+
request,
|
|
4965
4997
|
// Recorded on the run for support to read back, not a write
|
|
4966
4998
|
// instruction — the hook has already written what it needed to.
|
|
4967
4999
|
response : { mailchimpMemberId : member?.id || hash }
|
|
@@ -6413,7 +6445,7 @@ var shopify = {
|
|
|
6413
6445
|
//
|
|
6414
6446
|
// The shell has already refused a missing or inactive Shopify connection,
|
|
6415
6447
|
// so what is left is the two things only this hook can know are wrong.
|
|
6416
|
-
product : async ( { connection, context, workflow }, { mintId } = {} ) => {
|
|
6448
|
+
product : async ( { connection, context, workflow }, { mintId, read } = {} ) => {
|
|
6417
6449
|
|
|
6418
6450
|
const request = {
|
|
6419
6451
|
numericId : context?.id || null,
|
|
@@ -6429,11 +6461,30 @@ var shopify = {
|
|
|
6429
6461
|
|
|
6430
6462
|
const providerId = 'gid://shopify/Product/' + context.id;
|
|
6431
6463
|
|
|
6432
|
-
//
|
|
6433
|
-
//
|
|
6434
|
-
//
|
|
6435
|
-
//
|
|
6436
|
-
|
|
6464
|
+
// THE ROW'S OWN ID WHEN IT ALREADY EXISTS, a fresh one only when it
|
|
6465
|
+
// does not.
|
|
6466
|
+
//
|
|
6467
|
+
// The write below is an upsert whose `id` sits in $setOnInsert, so a
|
|
6468
|
+
// product we have seen before keeps the id it was first given and a
|
|
6469
|
+
// newly minted one is silently discarded. Minting unconditionally
|
|
6470
|
+
// therefore published an id belonging to no row: the enqueue named a
|
|
6471
|
+
// product the worker then had to fall back off, and the step's
|
|
6472
|
+
// response showed QA a value that matched nothing they could look up
|
|
6473
|
+
// — which is exactly how it was reported.
|
|
6474
|
+
//
|
|
6475
|
+
// Reading first costs one indexed lookup on the same key the upsert
|
|
6476
|
+
// matches. A concurrent redelivery can still have both runs miss and
|
|
6477
|
+
// mint, which is why the job carries the PROVIDER identity too and the
|
|
6478
|
+
// worker falls back to provider + shop — stable either way.
|
|
6479
|
+
const query = {
|
|
6480
|
+
'provider.id' : providerId,
|
|
6481
|
+
'provider.slug' : 'shopify',
|
|
6482
|
+
'source.domain' : connection.shop
|
|
6483
|
+
};
|
|
6484
|
+
|
|
6485
|
+
const existing = read?.get ? await read.get({ collection : 'product', query }) : null;
|
|
6486
|
+
|
|
6487
|
+
const productId = existing?.id || mintId();
|
|
6437
6488
|
|
|
6438
6489
|
return {
|
|
6439
6490
|
enqueues : [ {
|
|
@@ -6464,11 +6515,7 @@ var shopify = {
|
|
|
6464
6515
|
},
|
|
6465
6516
|
operation : 'update',
|
|
6466
6517
|
options : { upsert : true },
|
|
6467
|
-
query
|
|
6468
|
-
'provider.id' : providerId,
|
|
6469
|
-
'provider.slug' : 'shopify',
|
|
6470
|
-
'source.domain' : connection.shop
|
|
6471
|
-
}
|
|
6518
|
+
query
|
|
6472
6519
|
} ]
|
|
6473
6520
|
};
|
|
6474
6521
|
|
|
@@ -6941,10 +6988,10 @@ var shopify = {
|
|
|
6941
6988
|
queue : 'connection'
|
|
6942
6989
|
} ],
|
|
6943
6990
|
message : ( scopesMissing?.length
|
|
6944
|
-
? 'Health check: ping ok,
|
|
6991
|
+
? 'Health check: ping ok, scopes reconciled — connection errored, granted scopes are missing: ' + scopesMissing.join( ', ' ) + '.'
|
|
6945
6992
|
: refreshTokenRotated
|
|
6946
|
-
? 'Health check passed — refresh token rotated, ping ok,
|
|
6947
|
-
: 'Health check passed — ping ok,
|
|
6993
|
+
? 'Health check passed — refresh token rotated, ping ok, scopes reconciled.'
|
|
6994
|
+
: 'Health check passed — ping ok, scopes reconciled.'
|
|
6948
6995
|
)
|
|
6949
6996
|
+ ( sourceRepaired ? ' Billing source repaired from the live shop.' : '' )
|
|
6950
6997
|
+ ( metered === null ? ' No usage line on the store\'s approved plan — order billing needs the merchant to approve the updated plan.' : '' ),
|
|
@@ -1234,8 +1234,6 @@ var attentive = {
|
|
|
1234
1234
|
// status() already stops a connection reaching Active without a
|
|
1235
1235
|
// segment; this is the belt to that braces. A workflow saved before the
|
|
1236
1236
|
// segment was chosen must not silently write into nothing.
|
|
1237
|
-
if( ! segment ) return { message : 'No Attentive segment is chosen for this connection.', skipped : true };
|
|
1238
|
-
|
|
1239
1237
|
const email = lead?.canonical?.email?.value || lead?.email;
|
|
1240
1238
|
|
|
1241
1239
|
// E.164 OR NOTHING. Attentive requires it, and a national-format number
|
|
@@ -1243,9 +1241,22 @@ var attentive = {
|
|
|
1243
1241
|
// and the email carries the sync instead.
|
|
1244
1242
|
const phone = toE164( lead?.canonical?.phone?.value || lead?.phone );
|
|
1245
1243
|
|
|
1244
|
+
// WHAT WENT TO THE VENDOR, so a sync run in the activity feed can
|
|
1245
|
+
// answer what was actually sent. The three contact syncs were the only
|
|
1246
|
+
// hook family carrying none, and a merchant opening one saw
|
|
1247
|
+
// request : null. Attentive identifies on either channel, so both are
|
|
1248
|
+
// recorded — which of the two carried the sync is the first question
|
|
1249
|
+
// anyone debugging one of these asks.
|
|
1250
|
+
const request = { email : email || null, phone : phone || null, segment : settings?.segment || null };
|
|
1251
|
+
|
|
1252
|
+
// status() already stops a connection reaching Active without a
|
|
1253
|
+
// segment; this is the belt to that braces. A workflow saved before the
|
|
1254
|
+
// segment was chosen must not silently write into nothing.
|
|
1255
|
+
if( ! segment ) return { message : 'No Attentive segment is chosen for this connection.', request, skipped : true };
|
|
1256
|
+
|
|
1246
1257
|
// Their `user` requires phone OR email. With neither there is nobody to
|
|
1247
1258
|
// subscribe, so this is a skip rather than a failure.
|
|
1248
|
-
if( ! email && ! phone ) return { message : 'That lead has no email address or phone number to sync.', skipped : true };
|
|
1259
|
+
if( ! email && ! phone ) return { message : 'That lead has no email address or phone number to sync.', request, skipped : true };
|
|
1249
1260
|
|
|
1250
1261
|
const user = {
|
|
1251
1262
|
...( email && { email }),
|
|
@@ -1285,6 +1296,7 @@ var attentive = {
|
|
|
1285
1296
|
|
|
1286
1297
|
return {
|
|
1287
1298
|
message : 'Attentive accepted an unsubscribe for this contact — they have opted out.',
|
|
1299
|
+
request,
|
|
1288
1300
|
response : { accepted : true, unsubscribed : true }
|
|
1289
1301
|
};
|
|
1290
1302
|
|
|
@@ -1332,6 +1344,7 @@ var attentive = {
|
|
|
1332
1344
|
// queued them — a merchant who reads "synced" and looks for the person
|
|
1333
1345
|
// in Attentive a second later has been told the wrong thing.
|
|
1334
1346
|
message : 'Attentive accepted this contact for the segment. Attentive processes these asynchronously, so it appears there shortly.',
|
|
1347
|
+
request,
|
|
1335
1348
|
response : {
|
|
1336
1349
|
accepted : true,
|
|
1337
1350
|
...( membership?.batchJobId && { batchJobId : membership.batchJobId })
|
|
@@ -3353,7 +3366,7 @@ var drawbridge = {
|
|
|
3353
3366
|
text : { max : 2000, type : 'string' }
|
|
3354
3367
|
},
|
|
3355
3368
|
triggers : [ 'schedule.day', 'schedule.week', 'schedule.month' ],
|
|
3356
|
-
usage : { actions :
|
|
3369
|
+
usage : { actions : channels.email.actionsPerSend }
|
|
3357
3370
|
}),
|
|
3358
3371
|
|
|
3359
3372
|
// To organization MEMBERS. Never suppressed — an entrant's opt-out must
|
|
@@ -3379,10 +3392,14 @@ var drawbridge = {
|
|
|
3379
3392
|
subject : { max : 150, type : 'string' }
|
|
3380
3393
|
},
|
|
3381
3394
|
triggers : [ 'lead.insert' ],
|
|
3382
|
-
//
|
|
3383
|
-
//
|
|
3384
|
-
//
|
|
3385
|
-
|
|
3395
|
+
// EVERY STEP IN A BILLABLE WORKFLOW COUNTS, team mail included. This
|
|
3396
|
+
// leaves our SendGrid account and costs the same to send as any other
|
|
3397
|
+
// email, so it carries the same weight. Whether anything is charged at
|
|
3398
|
+
// all is the workflow's `billable` tag, not the step's audience — and
|
|
3399
|
+
// this step triggers on lead.insert, so a merchant notifying
|
|
3400
|
+
// themselves on every entry spends against their allowance twice per
|
|
3401
|
+
// lead, once for the entry and once for the notification.
|
|
3402
|
+
usage : { actions : channels.email.actionsPerSend }
|
|
3386
3403
|
}),
|
|
3387
3404
|
|
|
3388
3405
|
// To a LEAD. Suppression applies and the send is billed.
|
|
@@ -3835,11 +3852,16 @@ var klaviyo = {
|
|
|
3835
3852
|
// incomplete() already stops a connection reaching Active without a
|
|
3836
3853
|
// list; this is the belt to that braces. A workflow saved before the
|
|
3837
3854
|
// list was chosen must not silently write into nothing.
|
|
3838
|
-
|
|
3855
|
+
// WHAT WENT TO THE VENDOR, so a sync run in the activity feed can answer
|
|
3856
|
+
// what was actually sent. The three contact syncs were the only hook
|
|
3857
|
+
// family that carried none, so a merchant opening one saw request : null.
|
|
3858
|
+
const request = { email : lead?.canonical?.email?.value || lead?.email || null, list : settings?.list || null };
|
|
3859
|
+
|
|
3860
|
+
if( ! list ) return { message : 'No Klaviyo list is chosen for this connection.', request, skipped : true };
|
|
3839
3861
|
|
|
3840
3862
|
const email = lead?.canonical?.email?.value || lead?.email;
|
|
3841
3863
|
|
|
3842
|
-
if( ! email ) return { message : 'That lead has no email address to sync.', skipped : true };
|
|
3864
|
+
if( ! email ) return { message : 'That lead has no email address to sync.', request, skipped : true };
|
|
3843
3865
|
|
|
3844
3866
|
// A Klaviyo profile is keyed on an email, so IDENTITY comes from the
|
|
3845
3867
|
// lead. RANKING comes from the contact behind it, because only a
|
|
@@ -3934,7 +3956,7 @@ var klaviyo = {
|
|
|
3934
3956
|
|
|
3935
3957
|
const profileId = profile?.data?.id;
|
|
3936
3958
|
|
|
3937
|
-
if( ! profileId ) return { message : 'Klaviyo returned no profile id.', skipped : true };
|
|
3959
|
+
if( ! profileId ) return { message : 'Klaviyo returned no profile id.', request, skipped : true };
|
|
3938
3960
|
|
|
3939
3961
|
// SUPPRESSED PEOPLE ARE SYNCED AS UNSUBSCRIBED, NEVER OMITTED.
|
|
3940
3962
|
//
|
|
@@ -3977,6 +3999,7 @@ var klaviyo = {
|
|
|
3977
3999
|
message : suppressed
|
|
3978
4000
|
? 'Synced to Klaviyo as unsubscribed — this contact has opted out.'
|
|
3979
4001
|
: 'Synced to the Klaviyo list.',
|
|
4002
|
+
request,
|
|
3980
4003
|
// Recorded on the run for support to read back, not a write
|
|
3981
4004
|
// instruction — the hook has already written what it needed to.
|
|
3982
4005
|
response : { klaviyoProfileId : profileId }
|
|
@@ -4797,16 +4820,24 @@ var mailchimp = {
|
|
|
4797
4820
|
|
|
4798
4821
|
const audience = settings?.audience;
|
|
4799
4822
|
|
|
4823
|
+
// WHAT WENT TO THE VENDOR, recorded on the step so the activity feed
|
|
4824
|
+
// can answer "what did we actually send". Every other hook family
|
|
4825
|
+
// carries one; the three contact syncs did not, so a merchant opening
|
|
4826
|
+
// a sync run saw `request : null` and learned nothing — which is
|
|
4827
|
+
// exactly how it was reported. The audience and the address are the
|
|
4828
|
+
// whole identity of this write.
|
|
4829
|
+
const request = { audience : settings?.audience || null, email : lead?.canonical?.email?.value || lead?.email || null };
|
|
4830
|
+
|
|
4800
4831
|
// status() already stops a connection reaching Active without an
|
|
4801
4832
|
// audience; this is the belt to that braces. A workflow saved before
|
|
4802
4833
|
// the audience was chosen must not silently write into nothing.
|
|
4803
|
-
if( ! audience ) return { message : 'No Mailchimp audience is chosen for this connection.', skipped : true };
|
|
4834
|
+
if( ! audience ) return { message : 'No Mailchimp audience is chosen for this connection.', request, skipped : true };
|
|
4804
4835
|
|
|
4805
4836
|
const email = lead?.canonical?.email?.value || lead?.email;
|
|
4806
4837
|
|
|
4807
4838
|
// A Mailchimp member IS an email address — there is no other identity
|
|
4808
4839
|
// to write, so a lead without one is skipped rather than failed.
|
|
4809
|
-
if( ! email ) return { message : 'That lead has no email address to sync.', skipped : true };
|
|
4840
|
+
if( ! email ) return { message : 'That lead has no email address to sync.', request, skipped : true };
|
|
4810
4841
|
|
|
4811
4842
|
const hash = subscriberHash( email );
|
|
4812
4843
|
|
|
@@ -4962,6 +4993,7 @@ var mailchimp = {
|
|
|
4962
4993
|
message : suppressed
|
|
4963
4994
|
? 'Synced to Mailchimp as unsubscribed — this contact has opted out.'
|
|
4964
4995
|
: 'Synced to the Mailchimp audience.',
|
|
4996
|
+
request,
|
|
4965
4997
|
// Recorded on the run for support to read back, not a write
|
|
4966
4998
|
// instruction — the hook has already written what it needed to.
|
|
4967
4999
|
response : { mailchimpMemberId : member?.id || hash }
|
|
@@ -6413,7 +6445,7 @@ var shopify = {
|
|
|
6413
6445
|
//
|
|
6414
6446
|
// The shell has already refused a missing or inactive Shopify connection,
|
|
6415
6447
|
// so what is left is the two things only this hook can know are wrong.
|
|
6416
|
-
product : async ( { connection, context, workflow }, { mintId } = {} ) => {
|
|
6448
|
+
product : async ( { connection, context, workflow }, { mintId, read } = {} ) => {
|
|
6417
6449
|
|
|
6418
6450
|
const request = {
|
|
6419
6451
|
numericId : context?.id || null,
|
|
@@ -6429,11 +6461,30 @@ var shopify = {
|
|
|
6429
6461
|
|
|
6430
6462
|
const providerId = 'gid://shopify/Product/' + context.id;
|
|
6431
6463
|
|
|
6432
|
-
//
|
|
6433
|
-
//
|
|
6434
|
-
//
|
|
6435
|
-
//
|
|
6436
|
-
|
|
6464
|
+
// THE ROW'S OWN ID WHEN IT ALREADY EXISTS, a fresh one only when it
|
|
6465
|
+
// does not.
|
|
6466
|
+
//
|
|
6467
|
+
// The write below is an upsert whose `id` sits in $setOnInsert, so a
|
|
6468
|
+
// product we have seen before keeps the id it was first given and a
|
|
6469
|
+
// newly minted one is silently discarded. Minting unconditionally
|
|
6470
|
+
// therefore published an id belonging to no row: the enqueue named a
|
|
6471
|
+
// product the worker then had to fall back off, and the step's
|
|
6472
|
+
// response showed QA a value that matched nothing they could look up
|
|
6473
|
+
// — which is exactly how it was reported.
|
|
6474
|
+
//
|
|
6475
|
+
// Reading first costs one indexed lookup on the same key the upsert
|
|
6476
|
+
// matches. A concurrent redelivery can still have both runs miss and
|
|
6477
|
+
// mint, which is why the job carries the PROVIDER identity too and the
|
|
6478
|
+
// worker falls back to provider + shop — stable either way.
|
|
6479
|
+
const query = {
|
|
6480
|
+
'provider.id' : providerId,
|
|
6481
|
+
'provider.slug' : 'shopify',
|
|
6482
|
+
'source.domain' : connection.shop
|
|
6483
|
+
};
|
|
6484
|
+
|
|
6485
|
+
const existing = read?.get ? await read.get({ collection : 'product', query }) : null;
|
|
6486
|
+
|
|
6487
|
+
const productId = existing?.id || mintId();
|
|
6437
6488
|
|
|
6438
6489
|
return {
|
|
6439
6490
|
enqueues : [ {
|
|
@@ -6464,11 +6515,7 @@ var shopify = {
|
|
|
6464
6515
|
},
|
|
6465
6516
|
operation : 'update',
|
|
6466
6517
|
options : { upsert : true },
|
|
6467
|
-
query
|
|
6468
|
-
'provider.id' : providerId,
|
|
6469
|
-
'provider.slug' : 'shopify',
|
|
6470
|
-
'source.domain' : connection.shop
|
|
6471
|
-
}
|
|
6518
|
+
query
|
|
6472
6519
|
} ]
|
|
6473
6520
|
};
|
|
6474
6521
|
|
|
@@ -6941,10 +6988,10 @@ var shopify = {
|
|
|
6941
6988
|
queue : 'connection'
|
|
6942
6989
|
} ],
|
|
6943
6990
|
message : ( scopesMissing?.length
|
|
6944
|
-
? 'Health check: ping ok,
|
|
6991
|
+
? 'Health check: ping ok, scopes reconciled — connection errored, granted scopes are missing: ' + scopesMissing.join( ', ' ) + '.'
|
|
6945
6992
|
: refreshTokenRotated
|
|
6946
|
-
? 'Health check passed — refresh token rotated, ping ok,
|
|
6947
|
-
: 'Health check passed — ping ok,
|
|
6993
|
+
? 'Health check passed — refresh token rotated, ping ok, scopes reconciled.'
|
|
6994
|
+
: 'Health check passed — ping ok, scopes reconciled.'
|
|
6948
6995
|
)
|
|
6949
6996
|
+ ( sourceRepaired ? ' Billing source repaired from the live shop.' : '' )
|
|
6950
6997
|
+ ( metered === null ? ' No usage line on the store\'s approved plan — order billing needs the merchant to approve the updated plan.' : '' ),
|
|
@@ -789,10 +789,11 @@ var attentive_default2 = {
|
|
|
789
789
|
sync: async ({ lead, settings, suppressed, token }, { fetcher } = {}) => {
|
|
790
790
|
var _a, _b, _c, _d;
|
|
791
791
|
const segment = settings == null ? void 0 : settings.segment;
|
|
792
|
-
if (!segment) return { message: "No Attentive segment is chosen for this connection.", skipped: true };
|
|
793
792
|
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);
|
|
794
793
|
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));
|
|
795
|
-
|
|
794
|
+
const request2 = { email: email || null, phone: phone || null, segment: (settings == null ? void 0 : settings.segment) || null };
|
|
795
|
+
if (!segment) return { message: "No Attentive segment is chosen for this connection.", request: request2, skipped: true };
|
|
796
|
+
if (!email && !phone) return { message: "That lead has no email address or phone number to sync.", request: request2, skipped: true };
|
|
796
797
|
const user = {
|
|
797
798
|
...email && { email },
|
|
798
799
|
...phone && { phone }
|
|
@@ -814,6 +815,7 @@ var attentive_default2 = {
|
|
|
814
815
|
});
|
|
815
816
|
return {
|
|
816
817
|
message: "Attentive accepted an unsubscribe for this contact \u2014 they have opted out.",
|
|
818
|
+
request: request2,
|
|
817
819
|
response: { accepted: true, unsubscribed: true }
|
|
818
820
|
};
|
|
819
821
|
}
|
|
@@ -854,6 +856,7 @@ var attentive_default2 = {
|
|
|
854
856
|
// queued them — a merchant who reads "synced" and looks for the person
|
|
855
857
|
// in Attentive a second later has been told the wrong thing.
|
|
856
858
|
message: "Attentive accepted this contact for the segment. Attentive processes these asynchronously, so it appears there shortly.",
|
|
859
|
+
request: request2,
|
|
857
860
|
response: {
|
|
858
861
|
accepted: true,
|
|
859
862
|
...(membership == null ? void 0 : membership.batchJobId) && { batchJobId: membership.batchJobId }
|
|
@@ -2870,7 +2873,7 @@ var drawbridge_default2 = {
|
|
|
2870
2873
|
text: { max: 2e3, type: "string" }
|
|
2871
2874
|
},
|
|
2872
2875
|
triggers: ["schedule.day", "schedule.week", "schedule.month"],
|
|
2873
|
-
usage: { actions:
|
|
2876
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2874
2877
|
}),
|
|
2875
2878
|
// To organization MEMBERS. Never suppressed — an entrant's opt-out must
|
|
2876
2879
|
// not silence an alert to staff — and not billed.
|
|
@@ -2895,10 +2898,14 @@ var drawbridge_default2 = {
|
|
|
2895
2898
|
subject: { max: 150, type: "string" }
|
|
2896
2899
|
},
|
|
2897
2900
|
triggers: ["lead.insert"],
|
|
2898
|
-
//
|
|
2899
|
-
//
|
|
2900
|
-
//
|
|
2901
|
-
|
|
2901
|
+
// EVERY STEP IN A BILLABLE WORKFLOW COUNTS, team mail included. This
|
|
2902
|
+
// leaves our SendGrid account and costs the same to send as any other
|
|
2903
|
+
// email, so it carries the same weight. Whether anything is charged at
|
|
2904
|
+
// all is the workflow's `billable` tag, not the step's audience — and
|
|
2905
|
+
// this step triggers on lead.insert, so a merchant notifying
|
|
2906
|
+
// themselves on every entry spends against their allowance twice per
|
|
2907
|
+
// lead, once for the entry and once for the notification.
|
|
2908
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2902
2909
|
}),
|
|
2903
2910
|
// To a LEAD. Suppression applies and the send is billed.
|
|
2904
2911
|
send: () => ({
|
|
@@ -3254,11 +3261,12 @@ var klaviyo_default2 = {
|
|
|
3254
3261
|
// different thing from deleting the profile.
|
|
3255
3262
|
remove: false,
|
|
3256
3263
|
sync: async ({ context, lead, segments, settings, suppressed, token }, { fetcher } = {}) => {
|
|
3257
|
-
var _a, _b, _c;
|
|
3264
|
+
var _a, _b, _c, _d, _e;
|
|
3258
3265
|
const list = settings == null ? void 0 : settings.list;
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3266
|
+
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 };
|
|
3267
|
+
if (!list) return { message: "No Klaviyo list is chosen for this connection.", request: request2, skipped: true };
|
|
3268
|
+
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);
|
|
3269
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3262
3270
|
const person = (context == null ? void 0 : context.contact) || null;
|
|
3263
3271
|
const totals = (person == null ? void 0 : person.totals) || {};
|
|
3264
3272
|
const count = (value) => typeof value === "number" ? value : (value == null ? void 0 : value.total) || 0;
|
|
@@ -3315,8 +3323,8 @@ var klaviyo_default2 = {
|
|
|
3315
3323
|
},
|
|
3316
3324
|
token
|
|
3317
3325
|
});
|
|
3318
|
-
const profileId = (
|
|
3319
|
-
if (!profileId) return { message: "Klaviyo returned no profile id.", skipped: true };
|
|
3326
|
+
const profileId = (_e = profile == null ? void 0 : profile.data) == null ? void 0 : _e.id;
|
|
3327
|
+
if (!profileId) return { message: "Klaviyo returned no profile id.", request: request2, skipped: true };
|
|
3320
3328
|
await api2("/profile-subscription-bulk-create-jobs/", {
|
|
3321
3329
|
fetcher,
|
|
3322
3330
|
method: "POST",
|
|
@@ -3345,6 +3353,7 @@ var klaviyo_default2 = {
|
|
|
3345
3353
|
// Merged into `context` for later steps in this run.
|
|
3346
3354
|
context: { klaviyoProfileId: profileId },
|
|
3347
3355
|
message: suppressed ? "Synced to Klaviyo as unsubscribed \u2014 this contact has opted out." : "Synced to the Klaviyo list.",
|
|
3356
|
+
request: request2,
|
|
3348
3357
|
// Recorded on the run for support to read back, not a write
|
|
3349
3358
|
// instruction — the hook has already written what it needed to.
|
|
3350
3359
|
response: { klaviyoProfileId: profileId }
|
|
@@ -3925,15 +3934,16 @@ var mailchimp_default2 = {
|
|
|
3925
3934
|
// why there is no create-or-update branch here. Quoted from Mailchimp's
|
|
3926
3935
|
// Marketing API reference for the list-members resource.
|
|
3927
3936
|
sync: async ({ connection: connection2, lead, segments, settings, suppressed, token }, { fetcher, read } = {}) => {
|
|
3928
|
-
var _a, _b, _c;
|
|
3937
|
+
var _a, _b, _c, _d, _e;
|
|
3929
3938
|
const audience = settings == null ? void 0 : settings.audience;
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3939
|
+
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 };
|
|
3940
|
+
if (!audience) return { message: "No Mailchimp audience is chosen for this connection.", request: request2, skipped: true };
|
|
3941
|
+
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);
|
|
3942
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3933
3943
|
const hash = subscriberHash(email);
|
|
3934
3944
|
const [firstName, ...restOfName] = String((lead == null ? void 0 : lead.name) || "").trim().split(/\s+/).filter(Boolean);
|
|
3935
3945
|
const lastName = restOfName.join(" ");
|
|
3936
|
-
const phone = ((
|
|
3946
|
+
const phone = ((_e = lead == null ? void 0 : lead.phone) == null ? void 0 : _e.number) || null;
|
|
3937
3947
|
const mergeFields = {
|
|
3938
3948
|
...firstName && { FNAME: firstName },
|
|
3939
3949
|
...lastName && { LNAME: lastName },
|
|
@@ -3990,6 +4000,7 @@ var mailchimp_default2 = {
|
|
|
3990
4000
|
// Merged into `context` for later steps in this run.
|
|
3991
4001
|
context: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash },
|
|
3992
4002
|
message: suppressed ? "Synced to Mailchimp as unsubscribed \u2014 this contact has opted out." : "Synced to the Mailchimp audience.",
|
|
4003
|
+
request: request2,
|
|
3993
4004
|
// Recorded on the run for support to read back, not a write
|
|
3994
4005
|
// instruction — the hook has already written what it needed to.
|
|
3995
4006
|
response: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash }
|
|
@@ -5025,7 +5036,7 @@ var shopify_default2 = {
|
|
|
5025
5036
|
//
|
|
5026
5037
|
// The shell has already refused a missing or inactive Shopify connection,
|
|
5027
5038
|
// so what is left is the two things only this hook can know are wrong.
|
|
5028
|
-
product: async ({ connection: connection2, context, workflow }, { mintId } = {}) => {
|
|
5039
|
+
product: async ({ connection: connection2, context, workflow }, { mintId, read } = {}) => {
|
|
5029
5040
|
const request2 = {
|
|
5030
5041
|
numericId: (context == null ? void 0 : context.id) || null,
|
|
5031
5042
|
organizationId: workflow.organization,
|
|
@@ -5034,7 +5045,13 @@ var shopify_default2 = {
|
|
|
5034
5045
|
if (!(context == null ? void 0 : context.id)) return { message: "Skipped \u2014 product webhook payload had no id.", request: request2, response: { skipped: true }, skipped: true };
|
|
5035
5046
|
if (!connection2.shop) return { message: "Skipped \u2014 Shopify connection is missing shop domain.", request: request2, response: { skipped: true }, skipped: true };
|
|
5036
5047
|
const providerId = "gid://shopify/Product/" + context.id;
|
|
5037
|
-
const
|
|
5048
|
+
const query = {
|
|
5049
|
+
"provider.id": providerId,
|
|
5050
|
+
"provider.slug": "shopify",
|
|
5051
|
+
"source.domain": connection2.shop
|
|
5052
|
+
};
|
|
5053
|
+
const existing = (read == null ? void 0 : read.get) ? await read.get({ collection: "product", query }) : null;
|
|
5054
|
+
const productId = (existing == null ? void 0 : existing.id) || mintId();
|
|
5038
5055
|
return {
|
|
5039
5056
|
enqueues: [{
|
|
5040
5057
|
data: { product: productId, providerId, shop: connection2.shop },
|
|
@@ -5064,11 +5081,7 @@ var shopify_default2 = {
|
|
|
5064
5081
|
},
|
|
5065
5082
|
operation: "update",
|
|
5066
5083
|
options: { upsert: true },
|
|
5067
|
-
query
|
|
5068
|
-
"provider.id": providerId,
|
|
5069
|
-
"provider.slug": "shopify",
|
|
5070
|
-
"source.domain": connection2.shop
|
|
5071
|
-
}
|
|
5084
|
+
query
|
|
5072
5085
|
}]
|
|
5073
5086
|
};
|
|
5074
5087
|
}
|
|
@@ -5338,7 +5351,7 @@ var shopify_default2 = {
|
|
|
5338
5351
|
options: { jobId: "connection.update.register." + workflow.connection + "." + randomUUID2() },
|
|
5339
5352
|
queue: "connection"
|
|
5340
5353
|
}],
|
|
5341
|
-
message: ((scopesMissing == null ? void 0 : scopesMissing.length) ? "Health check: ping ok,
|
|
5354
|
+
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." : ""),
|
|
5342
5355
|
request: request2,
|
|
5343
5356
|
response: {
|
|
5344
5357
|
billingProbeFailed,
|
package/dist/providers.cjs
CHANGED
|
@@ -774,10 +774,11 @@ var attentive_default2 = {
|
|
|
774
774
|
sync: async ({ lead, settings, suppressed, token }, { fetcher } = {}) => {
|
|
775
775
|
var _a, _b, _c, _d;
|
|
776
776
|
const segment = settings == null ? void 0 : settings.segment;
|
|
777
|
-
if (!segment) return { message: "No Attentive segment is chosen for this connection.", skipped: true };
|
|
778
777
|
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
778
|
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
|
-
|
|
779
|
+
const request2 = { email: email || null, phone: phone || null, segment: (settings == null ? void 0 : settings.segment) || null };
|
|
780
|
+
if (!segment) return { message: "No Attentive segment is chosen for this connection.", request: request2, skipped: true };
|
|
781
|
+
if (!email && !phone) return { message: "That lead has no email address or phone number to sync.", request: request2, skipped: true };
|
|
781
782
|
const user = {
|
|
782
783
|
...email && { email },
|
|
783
784
|
...phone && { phone }
|
|
@@ -799,6 +800,7 @@ var attentive_default2 = {
|
|
|
799
800
|
});
|
|
800
801
|
return {
|
|
801
802
|
message: "Attentive accepted an unsubscribe for this contact \u2014 they have opted out.",
|
|
803
|
+
request: request2,
|
|
802
804
|
response: { accepted: true, unsubscribed: true }
|
|
803
805
|
};
|
|
804
806
|
}
|
|
@@ -839,6 +841,7 @@ var attentive_default2 = {
|
|
|
839
841
|
// queued them — a merchant who reads "synced" and looks for the person
|
|
840
842
|
// in Attentive a second later has been told the wrong thing.
|
|
841
843
|
message: "Attentive accepted this contact for the segment. Attentive processes these asynchronously, so it appears there shortly.",
|
|
844
|
+
request: request2,
|
|
842
845
|
response: {
|
|
843
846
|
accepted: true,
|
|
844
847
|
...(membership == null ? void 0 : membership.batchJobId) && { batchJobId: membership.batchJobId }
|
|
@@ -2855,7 +2858,7 @@ var drawbridge_default2 = {
|
|
|
2855
2858
|
text: { max: 2e3, type: "string" }
|
|
2856
2859
|
},
|
|
2857
2860
|
triggers: ["schedule.day", "schedule.week", "schedule.month"],
|
|
2858
|
-
usage: { actions:
|
|
2861
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2859
2862
|
}),
|
|
2860
2863
|
// To organization MEMBERS. Never suppressed — an entrant's opt-out must
|
|
2861
2864
|
// not silence an alert to staff — and not billed.
|
|
@@ -2880,10 +2883,14 @@ var drawbridge_default2 = {
|
|
|
2880
2883
|
subject: { max: 150, type: "string" }
|
|
2881
2884
|
},
|
|
2882
2885
|
triggers: ["lead.insert"],
|
|
2883
|
-
//
|
|
2884
|
-
//
|
|
2885
|
-
//
|
|
2886
|
-
|
|
2886
|
+
// EVERY STEP IN A BILLABLE WORKFLOW COUNTS, team mail included. This
|
|
2887
|
+
// leaves our SendGrid account and costs the same to send as any other
|
|
2888
|
+
// email, so it carries the same weight. Whether anything is charged at
|
|
2889
|
+
// all is the workflow's `billable` tag, not the step's audience — and
|
|
2890
|
+
// this step triggers on lead.insert, so a merchant notifying
|
|
2891
|
+
// themselves on every entry spends against their allowance twice per
|
|
2892
|
+
// lead, once for the entry and once for the notification.
|
|
2893
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2887
2894
|
}),
|
|
2888
2895
|
// To a LEAD. Suppression applies and the send is billed.
|
|
2889
2896
|
send: () => ({
|
|
@@ -3239,11 +3246,12 @@ var klaviyo_default2 = {
|
|
|
3239
3246
|
// different thing from deleting the profile.
|
|
3240
3247
|
remove: false,
|
|
3241
3248
|
sync: async ({ context, lead, segments, settings, suppressed, token }, { fetcher } = {}) => {
|
|
3242
|
-
var _a, _b, _c;
|
|
3249
|
+
var _a, _b, _c, _d, _e;
|
|
3243
3250
|
const list = settings == null ? void 0 : settings.list;
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3251
|
+
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 };
|
|
3252
|
+
if (!list) return { message: "No Klaviyo list is chosen for this connection.", request: request2, skipped: true };
|
|
3253
|
+
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);
|
|
3254
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3247
3255
|
const person = (context == null ? void 0 : context.contact) || null;
|
|
3248
3256
|
const totals = (person == null ? void 0 : person.totals) || {};
|
|
3249
3257
|
const count = (value) => typeof value === "number" ? value : (value == null ? void 0 : value.total) || 0;
|
|
@@ -3300,8 +3308,8 @@ var klaviyo_default2 = {
|
|
|
3300
3308
|
},
|
|
3301
3309
|
token
|
|
3302
3310
|
});
|
|
3303
|
-
const profileId = (
|
|
3304
|
-
if (!profileId) return { message: "Klaviyo returned no profile id.", skipped: true };
|
|
3311
|
+
const profileId = (_e = profile == null ? void 0 : profile.data) == null ? void 0 : _e.id;
|
|
3312
|
+
if (!profileId) return { message: "Klaviyo returned no profile id.", request: request2, skipped: true };
|
|
3305
3313
|
await api2("/profile-subscription-bulk-create-jobs/", {
|
|
3306
3314
|
fetcher,
|
|
3307
3315
|
method: "POST",
|
|
@@ -3330,6 +3338,7 @@ var klaviyo_default2 = {
|
|
|
3330
3338
|
// Merged into `context` for later steps in this run.
|
|
3331
3339
|
context: { klaviyoProfileId: profileId },
|
|
3332
3340
|
message: suppressed ? "Synced to Klaviyo as unsubscribed \u2014 this contact has opted out." : "Synced to the Klaviyo list.",
|
|
3341
|
+
request: request2,
|
|
3333
3342
|
// Recorded on the run for support to read back, not a write
|
|
3334
3343
|
// instruction — the hook has already written what it needed to.
|
|
3335
3344
|
response: { klaviyoProfileId: profileId }
|
|
@@ -3910,15 +3919,16 @@ var mailchimp_default2 = {
|
|
|
3910
3919
|
// why there is no create-or-update branch here. Quoted from Mailchimp's
|
|
3911
3920
|
// Marketing API reference for the list-members resource.
|
|
3912
3921
|
sync: async ({ connection: connection2, lead, segments, settings, suppressed, token }, { fetcher, read } = {}) => {
|
|
3913
|
-
var _a, _b, _c;
|
|
3922
|
+
var _a, _b, _c, _d, _e;
|
|
3914
3923
|
const audience = settings == null ? void 0 : settings.audience;
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3924
|
+
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 };
|
|
3925
|
+
if (!audience) return { message: "No Mailchimp audience is chosen for this connection.", request: request2, skipped: true };
|
|
3926
|
+
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);
|
|
3927
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3918
3928
|
const hash = subscriberHash(email);
|
|
3919
3929
|
const [firstName, ...restOfName] = String((lead == null ? void 0 : lead.name) || "").trim().split(/\s+/).filter(Boolean);
|
|
3920
3930
|
const lastName = restOfName.join(" ");
|
|
3921
|
-
const phone = ((
|
|
3931
|
+
const phone = ((_e = lead == null ? void 0 : lead.phone) == null ? void 0 : _e.number) || null;
|
|
3922
3932
|
const mergeFields = {
|
|
3923
3933
|
...firstName && { FNAME: firstName },
|
|
3924
3934
|
...lastName && { LNAME: lastName },
|
|
@@ -3975,6 +3985,7 @@ var mailchimp_default2 = {
|
|
|
3975
3985
|
// Merged into `context` for later steps in this run.
|
|
3976
3986
|
context: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash },
|
|
3977
3987
|
message: suppressed ? "Synced to Mailchimp as unsubscribed \u2014 this contact has opted out." : "Synced to the Mailchimp audience.",
|
|
3988
|
+
request: request2,
|
|
3978
3989
|
// Recorded on the run for support to read back, not a write
|
|
3979
3990
|
// instruction — the hook has already written what it needed to.
|
|
3980
3991
|
response: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash }
|
|
@@ -5029,7 +5040,7 @@ var shopify_default2 = {
|
|
|
5029
5040
|
//
|
|
5030
5041
|
// The shell has already refused a missing or inactive Shopify connection,
|
|
5031
5042
|
// so what is left is the two things only this hook can know are wrong.
|
|
5032
|
-
product: async ({ connection: connection2, context, workflow }, { mintId } = {}) => {
|
|
5043
|
+
product: async ({ connection: connection2, context, workflow }, { mintId, read } = {}) => {
|
|
5033
5044
|
const request2 = {
|
|
5034
5045
|
numericId: (context == null ? void 0 : context.id) || null,
|
|
5035
5046
|
organizationId: workflow.organization,
|
|
@@ -5038,7 +5049,13 @@ var shopify_default2 = {
|
|
|
5038
5049
|
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
5050
|
if (!connection2.shop) return { message: "Skipped \u2014 Shopify connection is missing shop domain.", request: request2, response: { skipped: true }, skipped: true };
|
|
5040
5051
|
const providerId = "gid://shopify/Product/" + context.id;
|
|
5041
|
-
const
|
|
5052
|
+
const query = {
|
|
5053
|
+
"provider.id": providerId,
|
|
5054
|
+
"provider.slug": "shopify",
|
|
5055
|
+
"source.domain": connection2.shop
|
|
5056
|
+
};
|
|
5057
|
+
const existing = (read == null ? void 0 : read.get) ? await read.get({ collection: "product", query }) : null;
|
|
5058
|
+
const productId = (existing == null ? void 0 : existing.id) || mintId();
|
|
5042
5059
|
return {
|
|
5043
5060
|
enqueues: [{
|
|
5044
5061
|
data: { product: productId, providerId, shop: connection2.shop },
|
|
@@ -5068,11 +5085,7 @@ var shopify_default2 = {
|
|
|
5068
5085
|
},
|
|
5069
5086
|
operation: "update",
|
|
5070
5087
|
options: { upsert: true },
|
|
5071
|
-
query
|
|
5072
|
-
"provider.id": providerId,
|
|
5073
|
-
"provider.slug": "shopify",
|
|
5074
|
-
"source.domain": connection2.shop
|
|
5075
|
-
}
|
|
5088
|
+
query
|
|
5076
5089
|
}]
|
|
5077
5090
|
};
|
|
5078
5091
|
}
|
|
@@ -5342,7 +5355,7 @@ var shopify_default2 = {
|
|
|
5342
5355
|
options: { jobId: "connection.update.register." + workflow.connection + "." + (0, import_node_crypto6.randomUUID)() },
|
|
5343
5356
|
queue: "connection"
|
|
5344
5357
|
}],
|
|
5345
|
-
message: ((scopesMissing == null ? void 0 : scopesMissing.length) ? "Health check: ping ok,
|
|
5358
|
+
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
5359
|
request: request2,
|
|
5347
5360
|
response: {
|
|
5348
5361
|
billingProbeFailed,
|
package/dist/providers.js
CHANGED
|
@@ -725,10 +725,11 @@ var attentive_default2 = {
|
|
|
725
725
|
sync: async ({ lead, settings, suppressed, token }, { fetcher } = {}) => {
|
|
726
726
|
var _a, _b, _c, _d;
|
|
727
727
|
const segment = settings == null ? void 0 : settings.segment;
|
|
728
|
-
if (!segment) return { message: "No Attentive segment is chosen for this connection.", skipped: true };
|
|
729
728
|
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
729
|
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
|
-
|
|
730
|
+
const request2 = { email: email || null, phone: phone || null, segment: (settings == null ? void 0 : settings.segment) || null };
|
|
731
|
+
if (!segment) return { message: "No Attentive segment is chosen for this connection.", request: request2, skipped: true };
|
|
732
|
+
if (!email && !phone) return { message: "That lead has no email address or phone number to sync.", request: request2, skipped: true };
|
|
732
733
|
const user = {
|
|
733
734
|
...email && { email },
|
|
734
735
|
...phone && { phone }
|
|
@@ -750,6 +751,7 @@ var attentive_default2 = {
|
|
|
750
751
|
});
|
|
751
752
|
return {
|
|
752
753
|
message: "Attentive accepted an unsubscribe for this contact \u2014 they have opted out.",
|
|
754
|
+
request: request2,
|
|
753
755
|
response: { accepted: true, unsubscribed: true }
|
|
754
756
|
};
|
|
755
757
|
}
|
|
@@ -790,6 +792,7 @@ var attentive_default2 = {
|
|
|
790
792
|
// queued them — a merchant who reads "synced" and looks for the person
|
|
791
793
|
// in Attentive a second later has been told the wrong thing.
|
|
792
794
|
message: "Attentive accepted this contact for the segment. Attentive processes these asynchronously, so it appears there shortly.",
|
|
795
|
+
request: request2,
|
|
793
796
|
response: {
|
|
794
797
|
accepted: true,
|
|
795
798
|
...(membership == null ? void 0 : membership.batchJobId) && { batchJobId: membership.batchJobId }
|
|
@@ -2806,7 +2809,7 @@ var drawbridge_default2 = {
|
|
|
2806
2809
|
text: { max: 2e3, type: "string" }
|
|
2807
2810
|
},
|
|
2808
2811
|
triggers: ["schedule.day", "schedule.week", "schedule.month"],
|
|
2809
|
-
usage: { actions:
|
|
2812
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2810
2813
|
}),
|
|
2811
2814
|
// To organization MEMBERS. Never suppressed — an entrant's opt-out must
|
|
2812
2815
|
// not silence an alert to staff — and not billed.
|
|
@@ -2831,10 +2834,14 @@ var drawbridge_default2 = {
|
|
|
2831
2834
|
subject: { max: 150, type: "string" }
|
|
2832
2835
|
},
|
|
2833
2836
|
triggers: ["lead.insert"],
|
|
2834
|
-
//
|
|
2835
|
-
//
|
|
2836
|
-
//
|
|
2837
|
-
|
|
2837
|
+
// EVERY STEP IN A BILLABLE WORKFLOW COUNTS, team mail included. This
|
|
2838
|
+
// leaves our SendGrid account and costs the same to send as any other
|
|
2839
|
+
// email, so it carries the same weight. Whether anything is charged at
|
|
2840
|
+
// all is the workflow's `billable` tag, not the step's audience — and
|
|
2841
|
+
// this step triggers on lead.insert, so a merchant notifying
|
|
2842
|
+
// themselves on every entry spends against their allowance twice per
|
|
2843
|
+
// lead, once for the entry and once for the notification.
|
|
2844
|
+
usage: { actions: channels.email.actionsPerSend }
|
|
2838
2845
|
}),
|
|
2839
2846
|
// To a LEAD. Suppression applies and the send is billed.
|
|
2840
2847
|
send: () => ({
|
|
@@ -3190,11 +3197,12 @@ var klaviyo_default2 = {
|
|
|
3190
3197
|
// different thing from deleting the profile.
|
|
3191
3198
|
remove: false,
|
|
3192
3199
|
sync: async ({ context, lead, segments, settings, suppressed, token }, { fetcher } = {}) => {
|
|
3193
|
-
var _a, _b, _c;
|
|
3200
|
+
var _a, _b, _c, _d, _e;
|
|
3194
3201
|
const list = settings == null ? void 0 : settings.list;
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3202
|
+
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 };
|
|
3203
|
+
if (!list) return { message: "No Klaviyo list is chosen for this connection.", request: request2, skipped: true };
|
|
3204
|
+
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);
|
|
3205
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3198
3206
|
const person = (context == null ? void 0 : context.contact) || null;
|
|
3199
3207
|
const totals = (person == null ? void 0 : person.totals) || {};
|
|
3200
3208
|
const count = (value) => typeof value === "number" ? value : (value == null ? void 0 : value.total) || 0;
|
|
@@ -3251,8 +3259,8 @@ var klaviyo_default2 = {
|
|
|
3251
3259
|
},
|
|
3252
3260
|
token
|
|
3253
3261
|
});
|
|
3254
|
-
const profileId = (
|
|
3255
|
-
if (!profileId) return { message: "Klaviyo returned no profile id.", skipped: true };
|
|
3262
|
+
const profileId = (_e = profile == null ? void 0 : profile.data) == null ? void 0 : _e.id;
|
|
3263
|
+
if (!profileId) return { message: "Klaviyo returned no profile id.", request: request2, skipped: true };
|
|
3256
3264
|
await api2("/profile-subscription-bulk-create-jobs/", {
|
|
3257
3265
|
fetcher,
|
|
3258
3266
|
method: "POST",
|
|
@@ -3281,6 +3289,7 @@ var klaviyo_default2 = {
|
|
|
3281
3289
|
// Merged into `context` for later steps in this run.
|
|
3282
3290
|
context: { klaviyoProfileId: profileId },
|
|
3283
3291
|
message: suppressed ? "Synced to Klaviyo as unsubscribed \u2014 this contact has opted out." : "Synced to the Klaviyo list.",
|
|
3292
|
+
request: request2,
|
|
3284
3293
|
// Recorded on the run for support to read back, not a write
|
|
3285
3294
|
// instruction — the hook has already written what it needed to.
|
|
3286
3295
|
response: { klaviyoProfileId: profileId }
|
|
@@ -3861,15 +3870,16 @@ var mailchimp_default2 = {
|
|
|
3861
3870
|
// why there is no create-or-update branch here. Quoted from Mailchimp's
|
|
3862
3871
|
// Marketing API reference for the list-members resource.
|
|
3863
3872
|
sync: async ({ connection: connection2, lead, segments, settings, suppressed, token }, { fetcher, read } = {}) => {
|
|
3864
|
-
var _a, _b, _c;
|
|
3873
|
+
var _a, _b, _c, _d, _e;
|
|
3865
3874
|
const audience = settings == null ? void 0 : settings.audience;
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3875
|
+
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 };
|
|
3876
|
+
if (!audience) return { message: "No Mailchimp audience is chosen for this connection.", request: request2, skipped: true };
|
|
3877
|
+
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);
|
|
3878
|
+
if (!email) return { message: "That lead has no email address to sync.", request: request2, skipped: true };
|
|
3869
3879
|
const hash = subscriberHash(email);
|
|
3870
3880
|
const [firstName, ...restOfName] = String((lead == null ? void 0 : lead.name) || "").trim().split(/\s+/).filter(Boolean);
|
|
3871
3881
|
const lastName = restOfName.join(" ");
|
|
3872
|
-
const phone = ((
|
|
3882
|
+
const phone = ((_e = lead == null ? void 0 : lead.phone) == null ? void 0 : _e.number) || null;
|
|
3873
3883
|
const mergeFields = {
|
|
3874
3884
|
...firstName && { FNAME: firstName },
|
|
3875
3885
|
...lastName && { LNAME: lastName },
|
|
@@ -3926,6 +3936,7 @@ var mailchimp_default2 = {
|
|
|
3926
3936
|
// Merged into `context` for later steps in this run.
|
|
3927
3937
|
context: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash },
|
|
3928
3938
|
message: suppressed ? "Synced to Mailchimp as unsubscribed \u2014 this contact has opted out." : "Synced to the Mailchimp audience.",
|
|
3939
|
+
request: request2,
|
|
3929
3940
|
// Recorded on the run for support to read back, not a write
|
|
3930
3941
|
// instruction — the hook has already written what it needed to.
|
|
3931
3942
|
response: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash }
|
|
@@ -4980,7 +4991,7 @@ var shopify_default2 = {
|
|
|
4980
4991
|
//
|
|
4981
4992
|
// The shell has already refused a missing or inactive Shopify connection,
|
|
4982
4993
|
// so what is left is the two things only this hook can know are wrong.
|
|
4983
|
-
product: async ({ connection: connection2, context, workflow }, { mintId } = {}) => {
|
|
4994
|
+
product: async ({ connection: connection2, context, workflow }, { mintId, read } = {}) => {
|
|
4984
4995
|
const request2 = {
|
|
4985
4996
|
numericId: (context == null ? void 0 : context.id) || null,
|
|
4986
4997
|
organizationId: workflow.organization,
|
|
@@ -4989,7 +5000,13 @@ var shopify_default2 = {
|
|
|
4989
5000
|
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
5001
|
if (!connection2.shop) return { message: "Skipped \u2014 Shopify connection is missing shop domain.", request: request2, response: { skipped: true }, skipped: true };
|
|
4991
5002
|
const providerId = "gid://shopify/Product/" + context.id;
|
|
4992
|
-
const
|
|
5003
|
+
const query = {
|
|
5004
|
+
"provider.id": providerId,
|
|
5005
|
+
"provider.slug": "shopify",
|
|
5006
|
+
"source.domain": connection2.shop
|
|
5007
|
+
};
|
|
5008
|
+
const existing = (read == null ? void 0 : read.get) ? await read.get({ collection: "product", query }) : null;
|
|
5009
|
+
const productId = (existing == null ? void 0 : existing.id) || mintId();
|
|
4993
5010
|
return {
|
|
4994
5011
|
enqueues: [{
|
|
4995
5012
|
data: { product: productId, providerId, shop: connection2.shop },
|
|
@@ -5019,11 +5036,7 @@ var shopify_default2 = {
|
|
|
5019
5036
|
},
|
|
5020
5037
|
operation: "update",
|
|
5021
5038
|
options: { upsert: true },
|
|
5022
|
-
query
|
|
5023
|
-
"provider.id": providerId,
|
|
5024
|
-
"provider.slug": "shopify",
|
|
5025
|
-
"source.domain": connection2.shop
|
|
5026
|
-
}
|
|
5039
|
+
query
|
|
5027
5040
|
}]
|
|
5028
5041
|
};
|
|
5029
5042
|
}
|
|
@@ -5293,7 +5306,7 @@ var shopify_default2 = {
|
|
|
5293
5306
|
options: { jobId: "connection.update.register." + workflow.connection + "." + randomUUID2() },
|
|
5294
5307
|
queue: "connection"
|
|
5295
5308
|
}],
|
|
5296
|
-
message: ((scopesMissing == null ? void 0 : scopesMissing.length) ? "Health check: ping ok,
|
|
5309
|
+
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
5310
|
request: request2,
|
|
5298
5311
|
response: {
|
|
5299
5312
|
billingProbeFailed,
|
package/package.json
CHANGED