@drawbridge/drawbridge-utils 0.0.162 → 0.0.164

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.
@@ -634,10 +634,15 @@ var attentive_default2 = {
634
634
  "Attentive accepts these updates and applies them in the background, so a contact appears in your segment shortly after the sync rather than the instant it runs."
635
635
  ],
636
636
  excerpt: "Sync your Drawbridge contacts into an Attentive segment.",
637
+ // NAMES THE SEGMENT STEP, because `status` below gates on it: a grant with
638
+ // no segment chosen sits at Pending, and a guide that stops at the consent
639
+ // leaves a merchant looking at a connection they think is broken. Same
640
+ // omission Klaviyo's guide already carries a note about.
637
641
  guide: [
638
642
  "Press Connect. Drawbridge sends you to Attentive to approve access.",
639
643
  "Sign in to Attentive if you are not already, and authorize the permissions listed.",
640
- "You are returned here \u2014 choose which Attentive segment your contacts should sync into."
644
+ "You come back here to choose which Attentive segment your contacts should sync into.",
645
+ "The connection shows Pending until you choose a segment, then Active."
641
646
  ]
642
647
  },
643
648
  // A contact destination, like Klaviyo and Mailchimp — a merchant could
@@ -900,6 +905,9 @@ var attentive_default2 = {
900
905
  // merchant would recognise in a builder label.
901
906
  key: "Sync contact to Attentive",
902
907
  queue: "connection",
908
+ // Attentive applies the update in the background and returns no id we
909
+ // could hand on, so there is nothing for a later step to interpolate.
910
+ returns: [],
903
911
  // Nothing for a merchant to configure on the step itself — the segment
904
912
  // is chosen once on the connection. Declared empty rather than omitted,
905
913
  // so "this step takes no settings" and "nobody thought about settings"
@@ -2127,13 +2135,18 @@ var drawbridge_default2 = {
2127
2135
  request: request2,
2128
2136
  response: { count, notified: recipients.length },
2129
2137
  writes: recipients.map((member) => {
2130
- var _a2, _b2;
2138
+ var _a2, _b2, _c2;
2131
2139
  return queueNotification({
2132
2140
  audience: "member",
2133
2141
  message: interpolate((_a2 = step.settings) == null ? void 0 : _a2.message, values),
2134
2142
  organization: workflow.organization,
2135
2143
  send: { type: "email", email: member.email },
2136
- title: interpolate((_b2 = step.settings) == null ? void 0 : _b2.subject, values),
2144
+ // The editor's plain rendering, interpolated the same way so
2145
+ // {{count}} resolves in the text part too. Spread rather than
2146
+ // written as null: a step saved before `text` existed has none,
2147
+ // and the send path strips the markup itself for those.
2148
+ ...((_b2 = step.settings) == null ? void 0 : _b2.text) && { text: interpolate(step.settings.text, values) },
2149
+ title: interpolate((_c2 = step.settings) == null ? void 0 : _c2.subject, values),
2137
2150
  workflow: workflow.id
2138
2151
  });
2139
2152
  })
@@ -2202,7 +2215,7 @@ var drawbridge_default2 = {
2202
2215
  // unsubscribe token and the CAN-SPAM footer. The step's job is to say who
2203
2216
  // and what, correctly, and to refuse early when it must not send at all.
2204
2217
  send: async ({ context, step, workflow }, { canSend, read } = {}) => {
2205
- var _a, _b;
2218
+ var _a, _b, _c;
2206
2219
  const to = context == null ? void 0 : context.email;
2207
2220
  if (!to) throw new Error("No email address on context (context.email is required)");
2208
2221
  const request2 = { to };
@@ -2239,7 +2252,11 @@ var drawbridge_default2 = {
2239
2252
  message: interpolate((_a = step.settings) == null ? void 0 : _a.message, context),
2240
2253
  organization: workflow.organization,
2241
2254
  send: { type: "email", email: to },
2242
- title: interpolate((_b = step.settings) == null ? void 0 : _b.subject, context),
2255
+ // The editor's plain rendering, so the text/plain part of this
2256
+ // email is what the merchant wrote rather than a regex's guess at
2257
+ // it. See the digest above for why it is spread, not nulled.
2258
+ ...((_b = step.settings) == null ? void 0 : _b.text) && { text: interpolate(step.settings.text, context) },
2259
+ title: interpolate((_c = step.settings) == null ? void 0 : _c.subject, context),
2243
2260
  workflow: workflow.id
2244
2261
  })
2245
2262
  ]
@@ -2659,6 +2676,11 @@ var drawbridge_default2 = {
2659
2676
  hook: "email.digest",
2660
2677
  key: "Email \u2014 Digest",
2661
2678
  queue: "notification",
2679
+ // Nothing for a later step to interpolate. Declared empty rather than
2680
+ // omitted, so "this step leaves nothing behind" and "nobody thought
2681
+ // about it" stay different statements — the second is what emptied the
2682
+ // builder's Variables menu for every step in the product.
2683
+ returns: [],
2662
2684
  settings: {
2663
2685
  // The organization OWNER is always a recipient, resolved by the
2664
2686
  // hook, so this is additional recipients rather than the list. It
@@ -2667,7 +2689,22 @@ var drawbridge_default2 = {
2667
2689
  // pick and could never save the step.
2668
2690
  members: { of: "string", type: "array" },
2669
2691
  message: { max: 2e3, required: true, type: "string" },
2670
- subject: { max: 150, required: true, type: "string" }
2692
+ // THE LAST SPAM CHECK, stored beside the copy it measured. Shape is
2693
+ // pinned no further than the pair that proves the match, which is the
2694
+ // only part the api reads; the rest is SpamAssassin's own output and
2695
+ // is stored as it arrives. Same declaration the campaign's draw
2696
+ // notification validates against.
2697
+ //
2698
+ // Undeclared, this was silently dropped at submit — the settings
2699
+ // validator is noUnknown().strict(), so the dashboard's step form
2700
+ // mapped the key away rather than 400 — and every open of a workflow
2701
+ // step re-ran a check against a free service to be told what the last
2702
+ // open had already been told.
2703
+ score: { shape: { message: { type: "string" }, subject: { type: "string" } }, type: "object" },
2704
+ subject: { max: 150, required: true, type: "string" },
2705
+ // The editor's plain rendering of `message`, written beside it rather
2706
+ // than reconstructed by running a regex over the markup at send time.
2707
+ text: { max: 2e3, type: "string" }
2671
2708
  },
2672
2709
  triggers: ["schedule.day", "schedule.week", "schedule.month"],
2673
2710
  usage: { actions: 0 }
@@ -2688,6 +2725,7 @@ var drawbridge_default2 = {
2688
2725
  hook: "email.notify",
2689
2726
  key: "Email \u2014 Notification",
2690
2727
  queue: "notification",
2728
+ returns: [],
2691
2729
  settings: {
2692
2730
  members: { of: "string", type: "array" },
2693
2731
  message: { max: 2e3, type: "string" },
@@ -2704,9 +2742,14 @@ var drawbridge_default2 = {
2704
2742
  hook: "email.send",
2705
2743
  key: "Email \u2014 Send email",
2706
2744
  queue: "notification",
2745
+ returns: [],
2707
2746
  settings: {
2708
2747
  message: { max: 2e3, required: true, type: "string" },
2709
- subject: { max: 150, required: true, type: "string" }
2748
+ // See email.digest above the last spam check, and the editor's
2749
+ // plain rendering of the message.
2750
+ score: { shape: { message: { type: "string" }, subject: { type: "string" } }, type: "object" },
2751
+ subject: { max: 150, required: true, type: "string" },
2752
+ text: { max: 2e3, type: "string" }
2710
2753
  },
2711
2754
  triggers: ["lead.insert"],
2712
2755
  // ONE SOURCE FOR THE PRICE. lib/pricing.js is the index of every
@@ -3305,6 +3348,13 @@ var klaviyo_default2 = {
3305
3348
  // than a label that could be any of their Klaviyo accounts.
3306
3349
  key: "Sync contact to " + (((_a = data2 == null ? void 0 : data2.settings) == null ? void 0 : _a.account) || "Klaviyo"),
3307
3350
  queue: "connection",
3351
+ // The key the hook puts in `context`, which the runner merges into the
3352
+ // run so a later step can interpolate {{klaviyoProfileId}}. Declared
3353
+ // beside the hook that writes it — see the note on Shopify's
3354
+ // commerce.code, which is where this came back from.
3355
+ returns: [
3356
+ { key: "klaviyoProfileId", label: "Klaviyo Profile ID" }
3357
+ ],
3308
3358
  // Nothing for a merchant to configure on the step itself — the list
3309
3359
  // is chosen once on the connection. Declared empty rather than
3310
3360
  // omitted, so "this step takes no settings" and "nobody thought about
@@ -3420,10 +3470,15 @@ var mailchimp_default2 = {
3420
3470
  "Someone who unsubscribed inside Mailchimp keeps that choice: a resync only sets the status of a subscriber Mailchimp has never seen before."
3421
3471
  ],
3422
3472
  excerpt: "Sync your Drawbridge contacts into a Mailchimp audience.",
3473
+ // NAMES THE AUDIENCE STEP, because `status` below gates on it: a grant with
3474
+ // no audience chosen sits at Pending, and a guide that stops at the consent
3475
+ // leaves a merchant looking at a connection they think is broken.
3423
3476
  guide: [
3424
3477
  "Press Connect. Drawbridge sends you to Mailchimp to approve access.",
3425
3478
  "Sign in to Mailchimp if you are not already, and choose the account to connect.",
3426
- "You come back here to pick the audience your contacts should sync into."
3479
+ "You come back here to pick the audience your contacts should sync into.",
3480
+ "The connection shows Pending until you pick an audience, then Active.",
3481
+ "You can remove Drawbridge at any time from the Authorized Apps page in your Mailchimp account."
3427
3482
  ]
3428
3483
  },
3429
3484
  // Mailchimp and SendGrid shared a group while they were SENDERS, where an org
@@ -3665,6 +3720,11 @@ var mailchimp_default2 = {
3665
3720
  // than showing a string like a1b2c3d4e5.
3666
3721
  key: "Sync contact to Mailchimp",
3667
3722
  queue: "connection",
3723
+ // The key the hook puts in `context`, for a later step to interpolate
3724
+ // as {{mailchimpMemberId}}. Same reasoning as Klaviyo's.
3725
+ returns: [
3726
+ { key: "mailchimpMemberId", label: "Mailchimp Member ID" }
3727
+ ],
3668
3728
  // Nothing for a merchant to configure on the step itself — the audience
3669
3729
  // is chosen once on the connection. Declared empty rather than omitted,
3670
3730
  // so "this step takes no settings" and "nobody thought about settings"
@@ -4926,6 +4986,24 @@ var shopify_default2 = {
4926
4986
  hook: "commerce.code",
4927
4987
  key: "Issue a discount code",
4928
4988
  queue: "connection",
4989
+ // WHAT THIS STEP LEAVES BEHIND FOR THE ONES AFTER IT, and the only
4990
+ // reason the builder can offer a Variables menu.
4991
+ //
4992
+ // These are the exact keys the hook puts in `context` — the runner
4993
+ // merges that into the run's context, and a later step interpolates
4994
+ // {{shopifyDiscountCode}} out of it. Declared beside the hook that
4995
+ // writes them so the two cannot drift; there is nowhere else that
4996
+ // knows both.
4997
+ //
4998
+ // They existed as a hand-written list in the api's workflow catalog
4999
+ // until it was derived from these manifests, and the derivation
5000
+ // hardcoded `returns : []` for every step. Nothing has offered a
5001
+ // variable since — an email step after a discount step had no way to
5002
+ // name the code it was supposed to send.
5003
+ returns: [
5004
+ { key: "shopifyDiscountCode", label: "Shopify Discount Code" },
5005
+ { key: "shopifyDiscountId", label: "Shopify Discount ID" }
5006
+ ],
4929
5007
  settings: {
4930
5008
  discount: {
4931
5009
  required: true,
@@ -4944,6 +5022,12 @@ var shopify_default2 = {
4944
5022
  hook: "commerce.customer",
4945
5023
  key: "Create customer",
4946
5024
  queue: "connection",
5025
+ // Written on every path the hook can take — created, reused from an
5026
+ // earlier step, or reused from the lead — so a later step can rely on
5027
+ // it being there whenever this one succeeded.
5028
+ returns: [
5029
+ { key: "shopifyCustomerId", label: "Shopify Customer ID" }
5030
+ ],
4947
5031
  settings: {},
4948
5032
  triggers: ["lead.insert"],
4949
5033
  usage: { actions: 1 }
@@ -5341,6 +5425,13 @@ var webhook_default = {
5341
5425
  hook: "webhook.send",
5342
5426
  key: "Send webhook",
5343
5427
  queue: "webhook",
5428
+ // The receiver's response is buffered, not merged into the run — a
5429
+ // receiver owes us a status, not a document — so there is nothing here
5430
+ // for a later step to interpolate. Declared empty rather than omitted:
5431
+ // "leaves nothing behind" and "nobody thought about it" are different
5432
+ // statements, and the second one is what emptied the builder's
5433
+ // Variables menu for every step in the product.
5434
+ returns: [],
5344
5435
  settings: {
5345
5436
  url: { format: "url", required: true, type: "string" }
5346
5437
  },
@@ -5717,6 +5808,12 @@ var publicConnectionKeys = Object.freeze([
5717
5808
  // excerpt, guide, and any vendor redirect copy.
5718
5809
  "content",
5719
5810
  "createdAt",
5811
+ // THE MERCHANT'S OWN SWITCH. `enabled : false` is a connection the merchant
5812
+ // paused — kept, configured, and not run — as distinct from a vendor the
5813
+ // platform switched off (which the api overlays as an error). Absent means
5814
+ // on; the dashboard renders Paused from it and the sync gates refuse a paused
5815
+ // connection the way they refuse a missing one.
5816
+ "enabled",
5720
5817
  // The connection DOCUMENT's own errors array — scope-drift entries written by
5721
5818
  // drawbridge-sync. NOT the manifest's error copy, which is content.errors:
5722
5819
  // the document is spread OVER the resolved manifest downstream, so the two
@@ -914,10 +914,15 @@ var attentive = {
914
914
  'Attentive accepts these updates and applies them in the background, so a contact appears in your segment shortly after the sync rather than the instant it runs.'
915
915
  ],
916
916
  excerpt : 'Sync your Drawbridge contacts into an Attentive segment.',
917
+ // NAMES THE SEGMENT STEP, because `status` below gates on it: a grant with
918
+ // no segment chosen sits at Pending, and a guide that stops at the consent
919
+ // leaves a merchant looking at a connection they think is broken. Same
920
+ // omission Klaviyo's guide already carries a note about.
917
921
  guide : [
918
922
  'Press Connect. Drawbridge sends you to Attentive to approve access.',
919
923
  'Sign in to Attentive if you are not already, and authorize the permissions listed.',
920
- 'You are returned here choose which Attentive segment your contacts should sync into.'
924
+ 'You come back here to choose which Attentive segment your contacts should sync into.',
925
+ 'The connection shows Pending until you choose a segment, then Active.'
921
926
  ]
922
927
  },
923
928
  // A contact destination, like Klaviyo and Mailchimp — a merchant could
@@ -1239,6 +1244,10 @@ var attentive = {
1239
1244
 
1240
1245
  queue : 'connection',
1241
1246
 
1247
+ // Attentive applies the update in the background and returns no id we
1248
+ // could hand on, so there is nothing for a later step to interpolate.
1249
+ returns : [],
1250
+
1242
1251
  // Nothing for a merchant to configure on the step itself — the segment
1243
1252
  // is chosen once on the connection. Declared empty rather than omitted,
1244
1253
  // so "this step takes no settings" and "nobody thought about settings"
@@ -2236,6 +2245,11 @@ var drawbridge = {
2236
2245
  message : interpolate( step.settings?.message, values ),
2237
2246
  organization : workflow.organization,
2238
2247
  send : { type : 'email', email : member.email },
2248
+ // The editor's plain rendering, interpolated the same way so
2249
+ // {{count}} resolves in the text part too. Spread rather than
2250
+ // written as null: a step saved before `text` existed has none,
2251
+ // and the send path strips the markup itself for those.
2252
+ ...( step.settings?.text && { text : interpolate( step.settings.text, values ) }),
2239
2253
  title : interpolate( step.settings?.subject, values ),
2240
2254
  workflow : workflow.id
2241
2255
  }) )
@@ -2423,6 +2437,10 @@ var drawbridge = {
2423
2437
  message : interpolate( step.settings?.message, context ),
2424
2438
  organization : workflow.organization,
2425
2439
  send : { type : 'email', email : to },
2440
+ // The editor's plain rendering, so the text/plain part of this
2441
+ // email is what the merchant wrote rather than a regex's guess at
2442
+ // it. See the digest above for why it is spread, not nulled.
2443
+ ...( step.settings?.text && { text : interpolate( step.settings.text, context ) }),
2426
2444
  title : interpolate( step.settings?.subject, context ),
2427
2445
  workflow : workflow.id
2428
2446
  })
@@ -2978,6 +2996,11 @@ var drawbridge = {
2978
2996
  hook : 'email.digest',
2979
2997
  key : 'Email — Digest',
2980
2998
  queue : 'notification',
2999
+ // Nothing for a later step to interpolate. Declared empty rather than
3000
+ // omitted, so "this step leaves nothing behind" and "nobody thought
3001
+ // about it" stay different statements — the second is what emptied the
3002
+ // builder's Variables menu for every step in the product.
3003
+ returns : [],
2981
3004
  settings : {
2982
3005
  // The organization OWNER is always a recipient, resolved by the
2983
3006
  // hook, so this is additional recipients rather than the list. It
@@ -2986,7 +3009,22 @@ var drawbridge = {
2986
3009
  // pick and could never save the step.
2987
3010
  members : { of : 'string', type : 'array' },
2988
3011
  message : { max : 2000, required : true, type : 'string' },
2989
- subject : { max : 150, required : true, type : 'string' }
3012
+ // THE LAST SPAM CHECK, stored beside the copy it measured. Shape is
3013
+ // pinned no further than the pair that proves the match, which is the
3014
+ // only part the api reads; the rest is SpamAssassin's own output and
3015
+ // is stored as it arrives. Same declaration the campaign's draw
3016
+ // notification validates against.
3017
+ //
3018
+ // Undeclared, this was silently dropped at submit — the settings
3019
+ // validator is noUnknown().strict(), so the dashboard's step form
3020
+ // mapped the key away rather than 400 — and every open of a workflow
3021
+ // step re-ran a check against a free service to be told what the last
3022
+ // open had already been told.
3023
+ score : { shape : { message : { type : 'string' }, subject : { type : 'string' } }, type : 'object' },
3024
+ subject : { max : 150, required : true, type : 'string' },
3025
+ // The editor's plain rendering of `message`, written beside it rather
3026
+ // than reconstructed by running a regex over the markup at send time.
3027
+ text : { max : 2000, type : 'string' }
2990
3028
  },
2991
3029
  triggers : [ 'schedule.day', 'schedule.week', 'schedule.month' ],
2992
3030
  usage : { actions : 0 }
@@ -3008,6 +3046,7 @@ var drawbridge = {
3008
3046
  hook : 'email.notify',
3009
3047
  key : 'Email — Notification',
3010
3048
  queue : 'notification',
3049
+ returns : [],
3011
3050
  settings : {
3012
3051
  members : { of : 'string', type : 'array' },
3013
3052
  message : { max : 2000, type : 'string' },
@@ -3025,9 +3064,14 @@ var drawbridge = {
3025
3064
  hook : 'email.send',
3026
3065
  key : 'Email — Send email',
3027
3066
  queue : 'notification',
3067
+ returns : [],
3028
3068
  settings : {
3029
3069
  message : { max : 2000, required : true, type : 'string' },
3030
- subject : { max : 150, required : true, type : 'string' }
3070
+ // See email.digest above the last spam check, and the editor's
3071
+ // plain rendering of the message.
3072
+ score : { shape : { message : { type : 'string' }, subject : { type : 'string' } }, type : 'object' },
3073
+ subject : { max : 150, required : true, type : 'string' },
3074
+ text : { max : 2000, type : 'string' }
3031
3075
  },
3032
3076
  triggers : [ 'lead.insert' ],
3033
3077
  // ONE SOURCE FOR THE PRICE. lib/pricing.js is the index of every
@@ -3813,6 +3857,14 @@ var klaviyo = {
3813
3857
 
3814
3858
  queue : 'connection',
3815
3859
 
3860
+ // The key the hook puts in `context`, which the runner merges into the
3861
+ // run so a later step can interpolate {{klaviyoProfileId}}. Declared
3862
+ // beside the hook that writes it — see the note on Shopify's
3863
+ // commerce.code, which is where this came back from.
3864
+ returns : [
3865
+ { key : 'klaviyoProfileId', label : 'Klaviyo Profile ID' }
3866
+ ],
3867
+
3816
3868
  // Nothing for a merchant to configure on the step itself — the list
3817
3869
  // is chosen once on the connection. Declared empty rather than
3818
3870
  // omitted, so "this step takes no settings" and "nobody thought about
@@ -3978,10 +4030,15 @@ var mailchimp = {
3978
4030
  'Someone who unsubscribed inside Mailchimp keeps that choice: a resync only sets the status of a subscriber Mailchimp has never seen before.'
3979
4031
  ],
3980
4032
  excerpt : 'Sync your Drawbridge contacts into a Mailchimp audience.',
4033
+ // NAMES THE AUDIENCE STEP, because `status` below gates on it: a grant with
4034
+ // no audience chosen sits at Pending, and a guide that stops at the consent
4035
+ // leaves a merchant looking at a connection they think is broken.
3981
4036
  guide : [
3982
4037
  'Press Connect. Drawbridge sends you to Mailchimp to approve access.',
3983
4038
  'Sign in to Mailchimp if you are not already, and choose the account to connect.',
3984
- 'You come back here to pick the audience your contacts should sync into.'
4039
+ 'You come back here to pick the audience your contacts should sync into.',
4040
+ 'The connection shows Pending until you pick an audience, then Active.',
4041
+ 'You can remove Drawbridge at any time from the Authorized Apps page in your Mailchimp account.'
3985
4042
  ]
3986
4043
  },
3987
4044
  // Mailchimp and SendGrid shared a group while they were SENDERS, where an org
@@ -4332,6 +4389,12 @@ var mailchimp = {
4332
4389
 
4333
4390
  queue : 'connection',
4334
4391
 
4392
+ // The key the hook puts in `context`, for a later step to interpolate
4393
+ // as {{mailchimpMemberId}}. Same reasoning as Klaviyo's.
4394
+ returns : [
4395
+ { key : 'mailchimpMemberId', label : 'Mailchimp Member ID' }
4396
+ ],
4397
+
4335
4398
  // Nothing for a merchant to configure on the step itself — the audience
4336
4399
  // is chosen once on the connection. Declared empty rather than omitted,
4337
4400
  // so "this step takes no settings" and "nobody thought about settings"
@@ -6096,6 +6159,24 @@ var shopify = {
6096
6159
  hook : 'commerce.code',
6097
6160
  key : 'Issue a discount code',
6098
6161
  queue : 'connection',
6162
+ // WHAT THIS STEP LEAVES BEHIND FOR THE ONES AFTER IT, and the only
6163
+ // reason the builder can offer a Variables menu.
6164
+ //
6165
+ // These are the exact keys the hook puts in `context` — the runner
6166
+ // merges that into the run's context, and a later step interpolates
6167
+ // {{shopifyDiscountCode}} out of it. Declared beside the hook that
6168
+ // writes them so the two cannot drift; there is nowhere else that
6169
+ // knows both.
6170
+ //
6171
+ // They existed as a hand-written list in the api's workflow catalog
6172
+ // until it was derived from these manifests, and the derivation
6173
+ // hardcoded `returns : []` for every step. Nothing has offered a
6174
+ // variable since — an email step after a discount step had no way to
6175
+ // name the code it was supposed to send.
6176
+ returns : [
6177
+ { key : 'shopifyDiscountCode', label : 'Shopify Discount Code' },
6178
+ { key : 'shopifyDiscountId', label : 'Shopify Discount ID' }
6179
+ ],
6099
6180
  settings : {
6100
6181
  discount : {
6101
6182
  required : true,
@@ -6115,6 +6196,12 @@ var shopify = {
6115
6196
  hook : 'commerce.customer',
6116
6197
  key : 'Create customer',
6117
6198
  queue : 'connection',
6199
+ // Written on every path the hook can take — created, reused from an
6200
+ // earlier step, or reused from the lead — so a later step can rely on
6201
+ // it being there whenever this one succeeded.
6202
+ returns : [
6203
+ { key : 'shopifyCustomerId', label : 'Shopify Customer ID' }
6204
+ ],
6118
6205
  settings : {},
6119
6206
  triggers : [ 'lead.insert' ],
6120
6207
  usage : { actions : 1 }
@@ -6425,6 +6512,13 @@ var webhook = {
6425
6512
  hook : 'webhook.send',
6426
6513
  key : 'Send webhook',
6427
6514
  queue : 'webhook',
6515
+ // The receiver's response is buffered, not merged into the run — a
6516
+ // receiver owes us a status, not a document — so there is nothing here
6517
+ // for a later step to interpolate. Declared empty rather than omitted:
6518
+ // "leaves nothing behind" and "nobody thought about it" are different
6519
+ // statements, and the second one is what emptied the builder's
6520
+ // Variables menu for every step in the product.
6521
+ returns : [],
6428
6522
  settings : {
6429
6523
  url : { format : 'url', required : true, type : 'string' }
6430
6524
  },
@@ -7406,6 +7500,12 @@ const publicConnectionKeys = Object.freeze([
7406
7500
  // excerpt, guide, and any vendor redirect copy.
7407
7501
  'content',
7408
7502
  'createdAt',
7503
+ // THE MERCHANT'S OWN SWITCH. `enabled : false` is a connection the merchant
7504
+ // paused — kept, configured, and not run — as distinct from a vendor the
7505
+ // platform switched off (which the api overlays as an error). Absent means
7506
+ // on; the dashboard renders Paused from it and the sync gates refuse a paused
7507
+ // connection the way they refuse a missing one.
7508
+ 'enabled',
7409
7509
  // The connection DOCUMENT's own errors array — scope-drift entries written by
7410
7510
  // drawbridge-sync. NOT the manifest's error copy, which is content.errors:
7411
7511
  // the document is spread OVER the resolved manifest downstream, so the two