@aglyn/tenant-data-admin 1.0.0-beta.221 → 1.0.0-beta.223

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.
Files changed (121) hide show
  1. package/package.json +7 -7
  2. package/src/lib/server/api-keys.js +3 -3
  3. package/src/lib/server/api-keys.js.map +1 -1
  4. package/src/lib/server/api-v1-resources.d.ts +44 -3
  5. package/src/lib/server/api-v1-resources.js +82 -5
  6. package/src/lib/server/api-v1-resources.js.map +1 -1
  7. package/src/lib/server/collection-live-pages.d.ts +2 -2
  8. package/src/lib/server/collection-live-pages.js +1 -1
  9. package/src/lib/server/collection-live-pages.js.map +1 -1
  10. package/src/lib/server/email-delivery-log.d.ts +17 -73
  11. package/src/lib/server/email-delivery-log.js +44 -222
  12. package/src/lib/server/email-delivery-log.js.map +1 -1
  13. package/src/lib/server/email-flow-gate.js +27 -38
  14. package/src/lib/server/email-flow-gate.js.map +1 -1
  15. package/src/lib/server/erase-person.d.ts +34 -54
  16. package/src/lib/server/erase-person.js +44 -239
  17. package/src/lib/server/erase-person.js.map +1 -1
  18. package/src/lib/server/ga4-measurement-protocol.js +41 -28
  19. package/src/lib/server/ga4-measurement-protocol.js.map +1 -1
  20. package/src/lib/server/host-visitor-records.d.ts +25 -4
  21. package/src/lib/server/host-visitor-records.js +39 -11
  22. package/src/lib/server/host-visitor-records.js.map +1 -1
  23. package/src/lib/server/hosted-page-review.js +52 -36
  24. package/src/lib/server/hosted-page-review.js.map +1 -1
  25. package/src/lib/server/link-reputation-review.d.ts +6 -2
  26. package/src/lib/server/link-reputation-review.js +61 -20
  27. package/src/lib/server/link-reputation-review.js.map +1 -1
  28. package/src/lib/server/live-page-drops.d.ts +67 -0
  29. package/src/lib/server/live-page-drops.js +57 -0
  30. package/src/lib/server/live-page-drops.js.map +1 -0
  31. package/src/lib/server/media-bandwidth-cap.d.ts +78 -0
  32. package/src/lib/server/media-bandwidth-cap.js +180 -0
  33. package/src/lib/server/media-bandwidth-cap.js.map +1 -0
  34. package/src/lib/server/media-cdn-icon.d.ts +77 -0
  35. package/src/lib/server/media-cdn-icon.js +140 -0
  36. package/src/lib/server/media-cdn-icon.js.map +1 -0
  37. package/src/lib/server/media-delivery-regeneration.d.ts +132 -0
  38. package/src/lib/server/media-delivery-regeneration.js +216 -0
  39. package/src/lib/server/media-delivery-regeneration.js.map +1 -0
  40. package/src/lib/server/media-delivery.d.ts +2 -0
  41. package/src/lib/server/media-delivery.js +2 -1
  42. package/src/lib/server/media-delivery.js.map +1 -1
  43. package/src/lib/server/media-serve-count.d.ts +50 -0
  44. package/src/lib/server/media-serve-count.js +62 -0
  45. package/src/lib/server/media-serve-count.js.map +1 -0
  46. package/src/lib/server/media-signing.d.ts +13 -2
  47. package/src/lib/server/media-signing.js +12 -8
  48. package/src/lib/server/media-signing.js.map +1 -1
  49. package/src/lib/server/media-tombstone.js +7 -5
  50. package/src/lib/server/media-tombstone.js.map +1 -1
  51. package/src/lib/server/media-variants.d.ts +151 -20
  52. package/src/lib/server/media-variants.js +204 -27
  53. package/src/lib/server/media-variants.js.map +1 -1
  54. package/src/lib/server/organizations.d.ts +102 -2
  55. package/src/lib/server/organizations.js +388 -58
  56. package/src/lib/server/organizations.js.map +1 -1
  57. package/src/lib/server/outbound-send-review.js +11 -7
  58. package/src/lib/server/outbound-send-review.js.map +1 -1
  59. package/src/lib/server/paged-sweeps.d.ts +51 -0
  60. package/src/lib/server/paged-sweeps.js +79 -0
  61. package/src/lib/server/paged-sweeps.js.map +1 -0
  62. package/src/lib/server/payment-fraud-signal.d.ts +49 -9
  63. package/src/lib/server/payment-fraud-signal.js +192 -48
  64. package/src/lib/server/payment-fraud-signal.js.map +1 -1
  65. package/src/lib/server/platform-marketing-consent.d.ts +0 -49
  66. package/src/lib/server/platform-marketing-consent.js +27 -11
  67. package/src/lib/server/platform-marketing-consent.js.map +1 -1
  68. package/src/lib/server/render-monitor.d.ts +166 -0
  69. package/src/lib/server/render-monitor.js +412 -0
  70. package/src/lib/server/render-monitor.js.map +1 -0
  71. package/src/lib/server/risk-notice.d.ts +7 -0
  72. package/src/lib/server/risk-notice.js +6 -0
  73. package/src/lib/server/risk-notice.js.map +1 -1
  74. package/src/lib/server/serve-media-cdn.d.ts +69 -1
  75. package/src/lib/server/serve-media-cdn.js +424 -70
  76. package/src/lib/server/serve-media-cdn.js.map +1 -1
  77. package/src/lib/server/site-icon-render.d.ts +68 -0
  78. package/src/lib/server/site-icon-render.js +157 -0
  79. package/src/lib/server/site-icon-render.js.map +1 -0
  80. package/src/lib/server/staff-seat.d.ts +50 -0
  81. package/src/lib/server/staff-seat.js +118 -0
  82. package/src/lib/server/staff-seat.js.map +1 -0
  83. package/src/lib/server/transfer-export.d.ts +46 -0
  84. package/src/lib/server/transfer-export.js +221 -0
  85. package/src/lib/server/transfer-export.js.map +1 -0
  86. package/src/lib/server/transfer-jobs.d.ts +385 -0
  87. package/src/lib/server/transfer-jobs.js +2368 -0
  88. package/src/lib/server/transfer-jobs.js.map +1 -0
  89. package/src/lib/server/transfer-packages.d.ts +127 -0
  90. package/src/lib/server/transfer-packages.js +1036 -0
  91. package/src/lib/server/transfer-packages.js.map +1 -0
  92. package/src/lib/server/upgrade-proposal.d.ts +97 -0
  93. package/src/lib/server/upgrade-proposal.js +312 -0
  94. package/src/lib/server/upgrade-proposal.js.map +1 -0
  95. package/src/lib/server/upsert-contact.d.ts +17 -8
  96. package/src/lib/server/upsert-contact.js +82 -16
  97. package/src/lib/server/upsert-contact.js.map +1 -1
  98. package/src/lib/server/web-risk.d.ts +50 -9
  99. package/src/lib/server/web-risk.js +290 -155
  100. package/src/lib/server/web-risk.js.map +1 -1
  101. package/src/tenancy.d.ts +3 -3
  102. package/src/tenancy.js +5 -3
  103. package/src/tenancy.js.map +1 -1
  104. package/src/lib/server/campaign-attribution-store.d.ts +0 -55
  105. package/src/lib/server/campaign-attribution-store.js +0 -95
  106. package/src/lib/server/campaign-attribution-store.js.map +0 -1
  107. package/src/lib/server/campaign-conversion-attribution.d.ts +0 -283
  108. package/src/lib/server/campaign-conversion-attribution.js +0 -450
  109. package/src/lib/server/campaign-conversion-attribution.js.map +0 -1
  110. package/src/lib/server/crm-email-activity.d.ts +0 -118
  111. package/src/lib/server/crm-email-activity.js +0 -148
  112. package/src/lib/server/crm-email-activity.js.map +0 -1
  113. package/src/lib/server/crm-next-activity.d.ts +0 -74
  114. package/src/lib/server/crm-next-activity.js +0 -194
  115. package/src/lib/server/crm-next-activity.js.map +0 -1
  116. package/src/lib/server/dataset-live-pages.d.ts +0 -179
  117. package/src/lib/server/dataset-live-pages.js +0 -421
  118. package/src/lib/server/dataset-live-pages.js.map +0 -1
  119. package/src/lib/server/email-revenue-attribution.d.ts +0 -185
  120. package/src/lib/server/email-revenue-attribution.js +0 -321
  121. package/src/lib/server/email-revenue-attribution.js.map +0 -1
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import { type ConsentGroup, type ContactFacet, type ContactInteraction, type ContactLifecycleStage, type ContactSource } from '@aglyn/aglyn/server';
18
- import { type ResolvedCampaignTouch } from './campaign-conversion-attribution';
18
+ import { type PluginConversionTouch } from '@aglyn/aglyn/plugin-manager/plugin-conversion-credit';
19
19
  import { type MarketingConsentSource } from '@aglyn/aglyn/app-utils/marketing-consent';
20
20
  import { getOrgForHost } from './organizations';
21
21
  /**
@@ -53,7 +53,7 @@ export type UpsertHostContactVerdict = {
53
53
  * a field the facet grows is a field this option may carry the moment the
54
54
  * pick names it, and one it does not name is refused at compile time.
55
55
  */
56
- export type UpsertHostContactFacet = Partial<Pick<ContactFacet, 'phone' | 'jobTitle' | 'leadSource' | 'companyId' | 'companyName' | 'address' | 'ownerUid' | 'lifecycleStage' | 'custom'>>;
56
+ export type UpsertHostContactFacet = Partial<Pick<ContactFacet, 'phone' | 'jobTitle' | 'leadSource' | 'companyId' | 'companyName' | 'address' | 'ownerUid' | 'lifecycleStage' | 'custom' | 'salutation' | 'firstName' | 'lastName' | 'department' | 'mobilePhone' | 'homePhone' | 'otherPhone' | 'fax' | 'birthdate' | 'assistantName' | 'assistantPhone' | 'otherAddress' | 'doNotCall'>>;
57
57
  /**
58
58
  * The profile fields a door may hand this function (AGL-2596): the parts of
59
59
  * a person's record that no capture surface collects — the console's create
@@ -82,7 +82,7 @@ export type ContactProfileInput = UpsertHostContactFacet;
82
82
  * Handed to {@link UpsertHostContactOptions.onCreated} once, on the create
83
83
  * branch only. The merge branch is a visit by somebody the org already held,
84
84
  * which is another interaction and not a new contact — the same line
85
- * `campaignTouch` draws for attribution. Scalars and one string array, so
85
+ * `conversionTouch` draws for attribution. Scalars and one string array, so
86
86
  * the runtime can flatten it into an event payload without inventing keys.
87
87
  */
88
88
  export interface HostContactCreated {
@@ -184,17 +184,19 @@ export interface UpsertHostContactOptions {
184
184
  *
185
185
  * Absent everywhere today, because no order document carries a currency and
186
186
  * every checkout door writes `currency: 'usd'` onto the Stripe line items.
187
- * `attributeOrderToEmail` defaults it on that basis and says so. The field
187
+ * The plugin that credits orders defaults it on that basis and says so. The field
188
188
  * exists so a door that ever charges in something else can pass it, and the
189
189
  * campaign revenue report keeps it in its own bucket rather than adding it
190
190
  * to the dollars.
191
191
  */
192
192
  purchaseCurrency?: string;
193
193
  /**
194
- * The campaign this person came from, already resolved by the door.
194
+ * Where this person arrived from, already resolved by the door through the
195
+ * plugin that credits outcomes (`plugin-conversion-credit`), and opaque
196
+ * here.
195
197
  *
196
198
  * ⛔ The ORDER path passes none, and must not start. An order already has
197
- * its own join one branch below — `attributeOrderToEmail`, keyed on the
199
+ * its own credit one branch below — `creditOrderConversion`, keyed on the
198
200
  * order id — and a second record for the same sale would be the same money
199
201
  * counted twice under two rules. This is the door for the moments an order
200
202
  * does NOT cover: a form submission, a membership sign-up, a booking, a
@@ -203,11 +205,11 @@ export interface UpsertHostContactOptions {
203
205
  * Resolved rather than raw, for the reason `addHostLead` states: one
204
206
  * visitor action reaches several writers and the touch lookup is paid once.
205
207
  */
206
- campaignTouch?: ResolvedCampaignTouch | null;
208
+ conversionTouch?: PluginConversionTouch | null;
207
209
  /**
208
210
  * The campaigns the CAPTURE SURFACE is filed under.
209
211
  *
210
- * ⚠️ A different fact from {@link campaignTouch} beside it, and the two must
212
+ * ⚠️ A different fact from {@link conversionTouch} beside it, and the two must
211
213
  * never be folded together. A touch is where the visitor came FROM — an ad,
212
214
  * a link, a browser-supplied label resolved through an allowlist. This is
213
215
  * which campaigns the merchant put the form itself in, which is the
@@ -233,6 +235,13 @@ export interface UpsertHostContactOptions {
233
235
  * phone and nothing else does not blank the title somebody typed.
234
236
  */
235
237
  facet?: UpsertHostContactFacet;
238
+ /**
239
+ * Profile fields written only where THIS holder's facet has none
240
+ * (AGL-3493) — what a door learned in passing, like the phone a booking
241
+ * form asked for, which must not replace a number somebody typed on the
242
+ * record. A key {@link facet} also carries is `facet`'s to write.
243
+ */
244
+ facetFill?: Pick<UpsertHostContactFacet, 'phone'>;
236
245
  /**
237
246
  * The EARLIEST stage that describes what this capture was (AGL-2612).
238
247
  *
@@ -14,14 +14,13 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
14
14
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
- */ import { CAPTURED_BY_HOST_FIELD, checkCrmRecordsQuota, consentGroupScope, CONTACT_FACETS_FIELD, CONTACT_FORM_IDS_CAP, CONTACT_FORM_IDS_FIELD, marketingConsentFieldsForGroup, mergeContactInteraction, normalizeContainerIds, readContactFacet, normalizeContactEmail, ORG_SCOPE_TOKEN } from "@aglyn/aglyn/server";
17
+ */ import { CAPTURED_BY_HOST_FIELD, checkCrmRecordsQuota, composeContactName, CONTACT_EXTRA_PHONE_FIELDS, consentGroupScope, CONTACT_FACETS_FIELD, CONTACT_FORM_IDS_CAP, CONTACT_FORM_IDS_FIELD, marketingConsentFieldsForGroup, mergeContactInteraction, normalizeContactBirthdate, normalizeContainerIds, readContactFacet, normalizeContactEmail, ORG_SCOPE_TOKEN } from "@aglyn/aglyn/server";
18
18
  import { FieldValue } from "firebase-admin/firestore";
19
19
  import { firebaseAdmin } from "./firebase-admin.js";
20
20
  import { countCrmRecords, restampCrmListFieldsAt } from "./crm-records.js";
21
- import { attributeOrderToEmail } from "./email-revenue-attribution.js";
22
21
  import { hostErasedEmails, hostRefusesCaptureForErasure } from "./email-suppression.js";
23
22
  import { scheduleCapturedEmailCheck } from "./capture-email-check.js";
24
- import { attributeCampaignConversion } from "./campaign-conversion-attribution.js";
23
+ import { creditConversion, creditOrderConversion } from "@aglyn/aglyn/plugin-manager/plugin-conversion-credit";
25
24
  import { nameSearchFields } from "@aglyn/aglyn/app-utils/name-search";
26
25
  import { consentGroupForGrant } from "@aglyn/aglyn/app-utils/consent-groups";
27
26
  import { declineMarketingConsentFields, MARKETING_CONSENT_SOURCE_FIELD } from "@aglyn/aglyn/app-utils/marketing-consent";
@@ -48,6 +47,44 @@ import { consentGroupForSite, getOrgForHost, orgDataCollectionForHost } from "./
48
47
  */ function storableProfile(input) {
49
48
  if (!input) return {};
50
49
  const out = {};
50
+ /*
51
+ * Salesforce's standard contact fields (AGL-3515), under the same rule as
52
+ * the rest: given keys only, each normalized, an unusable value dropped.
53
+ * The salutation is judged against the org's list by the door that took
54
+ * it and held here to the label's shape, like the lead source. Do not
55
+ * call is written only as `true`: a door may learn that a person asked
56
+ * not to be phoned, and no capture is the person taking it back.
57
+ */ if (typeof input.salutation === 'string') {
58
+ const salutation = input.salutation.trim().replace(/\s+/g, ' ').slice(0, 120);
59
+ if (salutation) out.salutation = salutation;
60
+ }
61
+ if (input.firstName !== undefined) {
62
+ const firstName = composeContactName(input.firstName, '');
63
+ if (firstName) out.firstName = firstName;
64
+ }
65
+ if (input.lastName !== undefined) {
66
+ const lastName = composeContactName('', input.lastName);
67
+ if (lastName) out.lastName = lastName;
68
+ }
69
+ for (const key of [
70
+ 'department',
71
+ 'assistantName'
72
+ ]){
73
+ if (typeof input[key] !== 'string') continue;
74
+ const value = input[key].trim().slice(0, 120);
75
+ if (value) out[key] = value;
76
+ }
77
+ for (const key of CONTACT_EXTRA_PHONE_FIELDS){
78
+ if (input[key] === undefined) continue;
79
+ const phone = normalizePhone(input[key]);
80
+ if (phone) out[key] = phone;
81
+ }
82
+ if (input.birthdate !== undefined) {
83
+ const birthdate = normalizeContactBirthdate(input.birthdate);
84
+ if (birthdate) out.birthdate = birthdate;
85
+ }
86
+ if (input.otherAddress !== undefined) out.otherAddress = normalizeAddress(input.otherAddress);
87
+ if (input.doNotCall === true) out.doNotCall = true;
51
88
  if (input.phone !== undefined) {
52
89
  const phone = normalizePhone(input.phone);
53
90
  if (phone) out.phone = phone;
@@ -96,6 +133,17 @@ import { consentGroupForSite, getOrgForHost, orgDataCollectionForHost } from "./
96
133
  }
97
134
  return out;
98
135
  }
136
+ /**
137
+ * A door's {@link UpsertHostContactOptions.facetFill}, folded into the
138
+ * profile it is about to write: each field only where neither the door's
139
+ * own profile nor the holder's stored facet holds one.
140
+ */ function fillProfile(profile, fill, stored) {
141
+ var _ref;
142
+ const given = storableProfile(fill);
143
+ if (given.phone && !profile.phone && !String((_ref = stored == null ? void 0 : stored.phone) != null ? _ref : '').trim()) {
144
+ profile.phone = given.phone;
145
+ }
146
+ }
99
147
  /**
100
148
  * The org's companies collection, beside its contacts one.
101
149
  *
@@ -148,7 +196,7 @@ export async function upsertHostContact(options) {
148
196
  * contact capture below it.
149
197
  *=========================================*/ if (options.source === 'order' && options.interaction.refId) {
150
198
  var _options_purchaseCents, _options_interaction_atMs1;
151
- await attributeOrderToEmail(_extends({
199
+ await creditOrderConversion(_extends({
152
200
  hostId: options.hostId,
153
201
  orderId: String(options.interaction.refId),
154
202
  email,
@@ -249,7 +297,7 @@ export async function upsertHostContact(options) {
249
297
  *=========================================*/ const prefetched = batch == null ? void 0 : batch.claim(email);
250
298
  const docSnapshot = prefetched !== undefined ? prefetched : await findContactByEmail(contactsRef, email);
251
299
  if (docSnapshot) {
252
- var _facet_name, _profile_lifecycleStage, _ref5;
300
+ var _facet_name, _profile_firstName, _profile_lastName, _profile_lifecycleStage, _ref5;
253
301
  var _contactsRef_parent1;
254
302
  /*
255
303
  * MERGED INTO THIS GROUP'S FACET, not into the top of the document.
@@ -283,6 +331,15 @@ export async function upsertHostContact(options) {
283
331
  * named no stage and found none leaves the key absent, which reads as
284
332
  * "no stage" rather than as a stage somebody picked.
285
333
  */ const profile = storableProfile(options.facet);
334
+ fillProfile(profile, options.facetFill, facet);
335
+ /*
336
+ * THE NAME FOLLOWS THE FIRST AND LAST NAMES (AGL-3515): a door that
337
+ * carried either writes the holder's name as their composition, over
338
+ * the name the facet held — the parts are the newer, structured
339
+ * statement of the same thing. A door that carried neither leaves the
340
+ * name to the rule above, where the existing name wins.
341
+ */ const composedName = profile.firstName !== undefined || profile.lastName !== undefined ? composeContactName((_profile_firstName = profile.firstName) != null ? _profile_firstName : facet.firstName, (_profile_lastName = profile.lastName) != null ? _profile_lastName : facet.lastName) : '';
342
+ const facetName = composedName || merged.name;
286
343
  const advanced = advanceContactLifecycleStage((_profile_lifecycleStage = profile.lifecycleStage) != null ? _profile_lifecycleStage : facet.lifecycleStage, options.initialLifecycleStage);
287
344
  if (advanced) profile.lifecycleStage = advanced;
288
345
  /*
@@ -308,7 +365,7 @@ export async function upsertHostContact(options) {
308
365
  * merge-set, and the counts settle after it.
309
366
  */ const link = profile.companyId !== undefined ? planContactCompanyLink(readContactCompanyLink(docSnapshot.data(), group.groupId), profile.companyId) : null;
310
367
  const mirror = link ? contactCompanyMirrorValue(link) : undefined;
311
- await docSnapshot.ref.set(_extends({}, merged.name ? nameSearchFields(merged.name) : {}, profile.phone ? {
368
+ await docSnapshot.ref.set(_extends({}, facetName ? nameSearchFields(facetName) : {}, profile.phone ? {
312
369
  phone: profile.phone
313
370
  } : {}, profile.companyName ? {
314
371
  companyName: profile.companyName
@@ -325,8 +382,8 @@ export async function upsertHostContact(options) {
325
382
  [group.groupId]: _extends({
326
383
  sources: merged.sources,
327
384
  interactions: merged.interactions
328
- }, merged.name ? {
329
- name: merged.name
385
+ }, facetName ? {
386
+ name: facetName
330
387
  } : {}, campaignIds.length ? {
331
388
  campaignIds: FieldValue.arrayUnion(...campaignIds)
332
389
  } : {}, tags.length ? {
@@ -437,9 +494,18 @@ export async function upsertHostContact(options) {
437
494
  * a door that named one writes. A `null` address is dropped rather than
438
495
  * written: there is nothing on a new document for it to clear.
439
496
  */ const profile = storableProfile(options.facet);
497
+ // A new person holds nothing yet, so the fill is written whole.
498
+ fillProfile(profile, options.facetFill, null);
440
499
  const advanced = advanceContactLifecycleStage(profile.lifecycleStage, options.initialLifecycleStage);
441
500
  if (advanced) profile.lifecycleStage = advanced;
442
501
  if (profile.address === null) delete profile.address;
502
+ if (profile.otherAddress === null) delete profile.otherAddress;
503
+ // The holder's name is the first and last names' when the door gave
504
+ // either (AGL-3515); the canonical name is the one the door gave, or
505
+ // that composition for a door that gave only the parts.
506
+ const composedName = composeContactName(profile.firstName, profile.lastName);
507
+ const canonicalName = (options.name || composedName).slice(0, 120);
508
+ const holderName = (composedName || options.name || '').slice(0, 120);
443
509
  const record = _extends({
444
510
  hostId: options.hostId,
445
511
  /*
@@ -474,7 +540,7 @@ export async function upsertHostContact(options) {
474
540
  ORG_SCOPE_TOKEN
475
541
  ] : consentGroupScope(group),
476
542
  email
477
- }, options.name ? nameSearchFields(options.name.slice(0, 120)) : {}, profile.phone ? {
543
+ }, canonicalName ? nameSearchFields(canonicalName) : {}, profile.phone ? {
478
544
  phone: profile.phone
479
545
  } : {}, profile.companyName ? {
480
546
  companyName: profile.companyName
@@ -497,8 +563,8 @@ export async function upsertHostContact(options) {
497
563
  tags
498
564
  }, campaignIds.length ? {
499
565
  campaignIds
500
- } : {}, customFacet, options.name ? {
501
- name: options.name.slice(0, 120)
566
+ } : {}, customFacet, holderName ? {
567
+ name: holderName
502
568
  } : {}, profile, options.purchaseCents ? {
503
569
  ltvCents: options.purchaseCents,
504
570
  ordersCount: 1,
@@ -542,12 +608,12 @@ export async function upsertHostContact(options) {
542
608
  * is the same rule `addHostLead` applies — an interaction appended to
543
609
  * somebody the site already held is another visit, not a new person, and
544
610
  * crediting it would let the most recent campaign re-earn the whole list.
545
- */ if (options.campaignTouch) {
546
- await attributeCampaignConversion({
611
+ */ if (options.conversionTouch) {
612
+ await creditConversion({
547
613
  hostId: options.hostId,
548
614
  kind: 'contact',
549
615
  refId: created.id,
550
- touch: options.campaignTouch,
616
+ touch: options.conversionTouch,
551
617
  convertedAtMs: interaction.atMs
552
618
  });
553
619
  }
@@ -562,8 +628,8 @@ export async function upsertHostContact(options) {
562
628
  contactId: created.id,
563
629
  hostId: options.hostId,
564
630
  email
565
- }, options.name ? {
566
- name: options.name.slice(0, 120)
631
+ }, holderName ? {
632
+ name: holderName
567
633
  } : {}, {
568
634
  source: options.source,
569
635
  campaignIds