@drawbridge/drawbridge-utils 0.0.169 → 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 +49 -32
- package/dist/connections/index.d.cts +109 -34
- package/dist/connections/index.d.ts +109 -34
- package/dist/connections/index.js +49 -32
- package/dist/providers.cjs +49 -32
- package/dist/providers.js +49 -32
- package/package.json +1 -1
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,13 +3246,15 @@ 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) || {};
|
|
3257
|
+
const count = (value) => typeof value === "number" ? value : (value == null ? void 0 : value.total) || 0;
|
|
3249
3258
|
const profile = await api2("/profile-import", {
|
|
3250
3259
|
fetcher,
|
|
3251
3260
|
method: "POST",
|
|
@@ -3259,13 +3268,13 @@ var klaviyo_default2 = {
|
|
|
3259
3268
|
drawbridge_campaigns: (person.campaigns || []).length,
|
|
3260
3269
|
drawbridge_draws: totals.draws || 0,
|
|
3261
3270
|
drawbridge_entries: totals.entries || 0,
|
|
3262
|
-
drawbridge_orders: totals.orders
|
|
3271
|
+
drawbridge_orders: count(totals.orders),
|
|
3263
3272
|
// Campaign-attributed, NOT lifetime. A merchant running
|
|
3264
3273
|
// Shopify already has lifetime revenue in Klaviyo through
|
|
3265
3274
|
// Klaviyo's own integration; what only we can say is how
|
|
3266
3275
|
// much a campaign drove. Named so the two cannot be
|
|
3267
3276
|
// mistaken for one another in a segment builder.
|
|
3268
|
-
drawbridge_revenue: totals.gross
|
|
3277
|
+
drawbridge_revenue: count(totals.gross)
|
|
3269
3278
|
},
|
|
3270
3279
|
// THE DRAWBRIDGE SEGMENTS THEY ARE IN, as a list property the
|
|
3271
3280
|
// merchant builds Klaviyo segments on top of. Klaviyo owns no
|
|
@@ -3299,8 +3308,8 @@ var klaviyo_default2 = {
|
|
|
3299
3308
|
},
|
|
3300
3309
|
token
|
|
3301
3310
|
});
|
|
3302
|
-
const profileId = (
|
|
3303
|
-
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 };
|
|
3304
3313
|
await api2("/profile-subscription-bulk-create-jobs/", {
|
|
3305
3314
|
fetcher,
|
|
3306
3315
|
method: "POST",
|
|
@@ -3329,6 +3338,7 @@ var klaviyo_default2 = {
|
|
|
3329
3338
|
// Merged into `context` for later steps in this run.
|
|
3330
3339
|
context: { klaviyoProfileId: profileId },
|
|
3331
3340
|
message: suppressed ? "Synced to Klaviyo as unsubscribed \u2014 this contact has opted out." : "Synced to the Klaviyo list.",
|
|
3341
|
+
request: request2,
|
|
3332
3342
|
// Recorded on the run for support to read back, not a write
|
|
3333
3343
|
// instruction — the hook has already written what it needed to.
|
|
3334
3344
|
response: { klaviyoProfileId: profileId }
|
|
@@ -3770,7 +3780,8 @@ var api3 = async (path, { dc, fetcher = fetch, method = "GET", payload, token })
|
|
|
3770
3780
|
return response.status === 204 ? null : response.json();
|
|
3771
3781
|
};
|
|
3772
3782
|
var subscriberHash = (email) => (0, import_node_crypto5.createHash)("md5").update(String(email).trim().toLowerCase()).digest("hex");
|
|
3773
|
-
var
|
|
3783
|
+
var TAG_NAME_LIMIT = 100;
|
|
3784
|
+
var tagName = (title) => ("Drawbridge: " + title).slice(0, TAG_NAME_LIMIT);
|
|
3774
3785
|
var mailchimp_default2 = {
|
|
3775
3786
|
// OAUTH 2, authorization code. Every url below is quoted from
|
|
3776
3787
|
// mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/ rather
|
|
@@ -3908,15 +3919,16 @@ var mailchimp_default2 = {
|
|
|
3908
3919
|
// why there is no create-or-update branch here. Quoted from Mailchimp's
|
|
3909
3920
|
// Marketing API reference for the list-members resource.
|
|
3910
3921
|
sync: async ({ connection: connection2, lead, segments, settings, suppressed, token }, { fetcher, read } = {}) => {
|
|
3911
|
-
var _a, _b, _c;
|
|
3922
|
+
var _a, _b, _c, _d, _e;
|
|
3912
3923
|
const audience = settings == null ? void 0 : settings.audience;
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
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 };
|
|
3916
3928
|
const hash = subscriberHash(email);
|
|
3917
3929
|
const [firstName, ...restOfName] = String((lead == null ? void 0 : lead.name) || "").trim().split(/\s+/).filter(Boolean);
|
|
3918
3930
|
const lastName = restOfName.join(" ");
|
|
3919
|
-
const phone = ((
|
|
3931
|
+
const phone = ((_e = lead == null ? void 0 : lead.phone) == null ? void 0 : _e.number) || null;
|
|
3920
3932
|
const mergeFields = {
|
|
3921
3933
|
...firstName && { FNAME: firstName },
|
|
3922
3934
|
...lastName && { LNAME: lastName },
|
|
@@ -3973,6 +3985,7 @@ var mailchimp_default2 = {
|
|
|
3973
3985
|
// Merged into `context` for later steps in this run.
|
|
3974
3986
|
context: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash },
|
|
3975
3987
|
message: suppressed ? "Synced to Mailchimp as unsubscribed \u2014 this contact has opted out." : "Synced to the Mailchimp audience.",
|
|
3988
|
+
request: request2,
|
|
3976
3989
|
// Recorded on the run for support to read back, not a write
|
|
3977
3990
|
// instruction — the hook has already written what it needed to.
|
|
3978
3991
|
response: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash }
|
|
@@ -3995,10 +4008,12 @@ var mailchimp_default2 = {
|
|
|
3995
4008
|
// on attaching people to it by name. Both address the same object. The
|
|
3996
4009
|
// segment schema says it outright: "The type of segment. Static segments
|
|
3997
4010
|
// are now known as tags"
|
|
3998
|
-
// (api.mailchimp.com/schema/3.0/
|
|
4011
|
+
// (api.mailchimp.com/schema/3.0/Definitions/Lists/Segments/Response.json,
|
|
4012
|
+
// fetched 2026-09-12 — the root Swagger.json carries no prose, only $refs
|
|
4013
|
+
// into fragment files like this one).
|
|
3999
4014
|
//
|
|
4000
|
-
// IDEMPOTENT ON EVERY PATH: called on create, on rename, on
|
|
4001
|
-
//
|
|
4015
|
+
// IDEMPOTENT ON EVERY PATH: called on create, on rename, on a connection
|
|
4016
|
+
// finishing its configuration, and on the backfill migration, it converges. That is what lets one hook serve
|
|
4002
4017
|
// all four without a create-vs-update branch anywhere else.
|
|
4003
4018
|
register: async ({ connection: connection2, context, manifest, settings, token, workflow }, { fetcher, read } = {}) => {
|
|
4004
4019
|
var _a;
|
|
@@ -5025,7 +5040,7 @@ var shopify_default2 = {
|
|
|
5025
5040
|
//
|
|
5026
5041
|
// The shell has already refused a missing or inactive Shopify connection,
|
|
5027
5042
|
// so what is left is the two things only this hook can know are wrong.
|
|
5028
|
-
product: async ({ connection: connection2, context, workflow }, { mintId } = {}) => {
|
|
5043
|
+
product: async ({ connection: connection2, context, workflow }, { mintId, read } = {}) => {
|
|
5029
5044
|
const request2 = {
|
|
5030
5045
|
numericId: (context == null ? void 0 : context.id) || null,
|
|
5031
5046
|
organizationId: workflow.organization,
|
|
@@ -5034,7 +5049,13 @@ var shopify_default2 = {
|
|
|
5034
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 };
|
|
5035
5050
|
if (!connection2.shop) return { message: "Skipped \u2014 Shopify connection is missing shop domain.", request: request2, response: { skipped: true }, skipped: true };
|
|
5036
5051
|
const providerId = "gid://shopify/Product/" + context.id;
|
|
5037
|
-
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();
|
|
5038
5059
|
return {
|
|
5039
5060
|
enqueues: [{
|
|
5040
5061
|
data: { product: productId, providerId, shop: connection2.shop },
|
|
@@ -5064,11 +5085,7 @@ var shopify_default2 = {
|
|
|
5064
5085
|
},
|
|
5065
5086
|
operation: "update",
|
|
5066
5087
|
options: { upsert: true },
|
|
5067
|
-
query
|
|
5068
|
-
"provider.id": providerId,
|
|
5069
|
-
"provider.slug": "shopify",
|
|
5070
|
-
"source.domain": connection2.shop
|
|
5071
|
-
}
|
|
5088
|
+
query
|
|
5072
5089
|
}]
|
|
5073
5090
|
};
|
|
5074
5091
|
}
|
|
@@ -5338,7 +5355,7 @@ var shopify_default2 = {
|
|
|
5338
5355
|
options: { jobId: "connection.update.register." + workflow.connection + "." + (0, import_node_crypto6.randomUUID)() },
|
|
5339
5356
|
queue: "connection"
|
|
5340
5357
|
}],
|
|
5341
|
-
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." : ""),
|
|
5342
5359
|
request: request2,
|
|
5343
5360
|
response: {
|
|
5344
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,13 +3197,15 @@ 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) || {};
|
|
3208
|
+
const count = (value) => typeof value === "number" ? value : (value == null ? void 0 : value.total) || 0;
|
|
3200
3209
|
const profile = await api2("/profile-import", {
|
|
3201
3210
|
fetcher,
|
|
3202
3211
|
method: "POST",
|
|
@@ -3210,13 +3219,13 @@ var klaviyo_default2 = {
|
|
|
3210
3219
|
drawbridge_campaigns: (person.campaigns || []).length,
|
|
3211
3220
|
drawbridge_draws: totals.draws || 0,
|
|
3212
3221
|
drawbridge_entries: totals.entries || 0,
|
|
3213
|
-
drawbridge_orders: totals.orders
|
|
3222
|
+
drawbridge_orders: count(totals.orders),
|
|
3214
3223
|
// Campaign-attributed, NOT lifetime. A merchant running
|
|
3215
3224
|
// Shopify already has lifetime revenue in Klaviyo through
|
|
3216
3225
|
// Klaviyo's own integration; what only we can say is how
|
|
3217
3226
|
// much a campaign drove. Named so the two cannot be
|
|
3218
3227
|
// mistaken for one another in a segment builder.
|
|
3219
|
-
drawbridge_revenue: totals.gross
|
|
3228
|
+
drawbridge_revenue: count(totals.gross)
|
|
3220
3229
|
},
|
|
3221
3230
|
// THE DRAWBRIDGE SEGMENTS THEY ARE IN, as a list property the
|
|
3222
3231
|
// merchant builds Klaviyo segments on top of. Klaviyo owns no
|
|
@@ -3250,8 +3259,8 @@ var klaviyo_default2 = {
|
|
|
3250
3259
|
},
|
|
3251
3260
|
token
|
|
3252
3261
|
});
|
|
3253
|
-
const profileId = (
|
|
3254
|
-
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 };
|
|
3255
3264
|
await api2("/profile-subscription-bulk-create-jobs/", {
|
|
3256
3265
|
fetcher,
|
|
3257
3266
|
method: "POST",
|
|
@@ -3280,6 +3289,7 @@ var klaviyo_default2 = {
|
|
|
3280
3289
|
// Merged into `context` for later steps in this run.
|
|
3281
3290
|
context: { klaviyoProfileId: profileId },
|
|
3282
3291
|
message: suppressed ? "Synced to Klaviyo as unsubscribed \u2014 this contact has opted out." : "Synced to the Klaviyo list.",
|
|
3292
|
+
request: request2,
|
|
3283
3293
|
// Recorded on the run for support to read back, not a write
|
|
3284
3294
|
// instruction — the hook has already written what it needed to.
|
|
3285
3295
|
response: { klaviyoProfileId: profileId }
|
|
@@ -3721,7 +3731,8 @@ var api3 = async (path, { dc, fetcher = fetch, method = "GET", payload, token })
|
|
|
3721
3731
|
return response.status === 204 ? null : response.json();
|
|
3722
3732
|
};
|
|
3723
3733
|
var subscriberHash = (email) => createHash2("md5").update(String(email).trim().toLowerCase()).digest("hex");
|
|
3724
|
-
var
|
|
3734
|
+
var TAG_NAME_LIMIT = 100;
|
|
3735
|
+
var tagName = (title) => ("Drawbridge: " + title).slice(0, TAG_NAME_LIMIT);
|
|
3725
3736
|
var mailchimp_default2 = {
|
|
3726
3737
|
// OAUTH 2, authorization code. Every url below is quoted from
|
|
3727
3738
|
// mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/ rather
|
|
@@ -3859,15 +3870,16 @@ var mailchimp_default2 = {
|
|
|
3859
3870
|
// why there is no create-or-update branch here. Quoted from Mailchimp's
|
|
3860
3871
|
// Marketing API reference for the list-members resource.
|
|
3861
3872
|
sync: async ({ connection: connection2, lead, segments, settings, suppressed, token }, { fetcher, read } = {}) => {
|
|
3862
|
-
var _a, _b, _c;
|
|
3873
|
+
var _a, _b, _c, _d, _e;
|
|
3863
3874
|
const audience = settings == null ? void 0 : settings.audience;
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
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 };
|
|
3867
3879
|
const hash = subscriberHash(email);
|
|
3868
3880
|
const [firstName, ...restOfName] = String((lead == null ? void 0 : lead.name) || "").trim().split(/\s+/).filter(Boolean);
|
|
3869
3881
|
const lastName = restOfName.join(" ");
|
|
3870
|
-
const phone = ((
|
|
3882
|
+
const phone = ((_e = lead == null ? void 0 : lead.phone) == null ? void 0 : _e.number) || null;
|
|
3871
3883
|
const mergeFields = {
|
|
3872
3884
|
...firstName && { FNAME: firstName },
|
|
3873
3885
|
...lastName && { LNAME: lastName },
|
|
@@ -3924,6 +3936,7 @@ var mailchimp_default2 = {
|
|
|
3924
3936
|
// Merged into `context` for later steps in this run.
|
|
3925
3937
|
context: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash },
|
|
3926
3938
|
message: suppressed ? "Synced to Mailchimp as unsubscribed \u2014 this contact has opted out." : "Synced to the Mailchimp audience.",
|
|
3939
|
+
request: request2,
|
|
3927
3940
|
// Recorded on the run for support to read back, not a write
|
|
3928
3941
|
// instruction — the hook has already written what it needed to.
|
|
3929
3942
|
response: { mailchimpMemberId: (member == null ? void 0 : member.id) || hash }
|
|
@@ -3946,10 +3959,12 @@ var mailchimp_default2 = {
|
|
|
3946
3959
|
// on attaching people to it by name. Both address the same object. The
|
|
3947
3960
|
// segment schema says it outright: "The type of segment. Static segments
|
|
3948
3961
|
// are now known as tags"
|
|
3949
|
-
// (api.mailchimp.com/schema/3.0/
|
|
3962
|
+
// (api.mailchimp.com/schema/3.0/Definitions/Lists/Segments/Response.json,
|
|
3963
|
+
// fetched 2026-09-12 — the root Swagger.json carries no prose, only $refs
|
|
3964
|
+
// into fragment files like this one).
|
|
3950
3965
|
//
|
|
3951
|
-
// IDEMPOTENT ON EVERY PATH: called on create, on rename, on
|
|
3952
|
-
//
|
|
3966
|
+
// IDEMPOTENT ON EVERY PATH: called on create, on rename, on a connection
|
|
3967
|
+
// finishing its configuration, and on the backfill migration, it converges. That is what lets one hook serve
|
|
3953
3968
|
// all four without a create-vs-update branch anywhere else.
|
|
3954
3969
|
register: async ({ connection: connection2, context, manifest, settings, token, workflow }, { fetcher, read } = {}) => {
|
|
3955
3970
|
var _a;
|
|
@@ -4976,7 +4991,7 @@ var shopify_default2 = {
|
|
|
4976
4991
|
//
|
|
4977
4992
|
// The shell has already refused a missing or inactive Shopify connection,
|
|
4978
4993
|
// so what is left is the two things only this hook can know are wrong.
|
|
4979
|
-
product: async ({ connection: connection2, context, workflow }, { mintId } = {}) => {
|
|
4994
|
+
product: async ({ connection: connection2, context, workflow }, { mintId, read } = {}) => {
|
|
4980
4995
|
const request2 = {
|
|
4981
4996
|
numericId: (context == null ? void 0 : context.id) || null,
|
|
4982
4997
|
organizationId: workflow.organization,
|
|
@@ -4985,7 +5000,13 @@ var shopify_default2 = {
|
|
|
4985
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 };
|
|
4986
5001
|
if (!connection2.shop) return { message: "Skipped \u2014 Shopify connection is missing shop domain.", request: request2, response: { skipped: true }, skipped: true };
|
|
4987
5002
|
const providerId = "gid://shopify/Product/" + context.id;
|
|
4988
|
-
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();
|
|
4989
5010
|
return {
|
|
4990
5011
|
enqueues: [{
|
|
4991
5012
|
data: { product: productId, providerId, shop: connection2.shop },
|
|
@@ -5015,11 +5036,7 @@ var shopify_default2 = {
|
|
|
5015
5036
|
},
|
|
5016
5037
|
operation: "update",
|
|
5017
5038
|
options: { upsert: true },
|
|
5018
|
-
query
|
|
5019
|
-
"provider.id": providerId,
|
|
5020
|
-
"provider.slug": "shopify",
|
|
5021
|
-
"source.domain": connection2.shop
|
|
5022
|
-
}
|
|
5039
|
+
query
|
|
5023
5040
|
}]
|
|
5024
5041
|
};
|
|
5025
5042
|
}
|
|
@@ -5289,7 +5306,7 @@ var shopify_default2 = {
|
|
|
5289
5306
|
options: { jobId: "connection.update.register." + workflow.connection + "." + randomUUID2() },
|
|
5290
5307
|
queue: "connection"
|
|
5291
5308
|
}],
|
|
5292
|
-
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." : ""),
|
|
5293
5310
|
request: request2,
|
|
5294
5311
|
response: {
|
|
5295
5312
|
billingProbeFailed,
|
package/package.json
CHANGED