@aglyn/aglyn 1.0.0-beta.152 → 1.0.0-beta.153

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 (42) hide show
  1. package/package.json +10 -10
  2. package/src/lib/app-utils/crm-csv.d.ts +10 -3
  3. package/src/lib/app-utils/crm-csv.js +37 -2
  4. package/src/lib/app-utils/crm-csv.js.map +1 -1
  5. package/src/lib/app-utils/crm-email-templates.js +15 -0
  6. package/src/lib/app-utils/crm-email-templates.js.map +1 -1
  7. package/src/lib/app-utils/crm.d.ts +69 -2
  8. package/src/lib/app-utils/crm.js +126 -1
  9. package/src/lib/app-utils/crm.js.map +1 -1
  10. package/src/lib/app-utils/docs-help.generated.d.ts +1 -1
  11. package/src/lib/app-utils/docs-help.generated.js +2 -0
  12. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  13. package/src/lib/app-utils/docs-index.generated.js +25 -11
  14. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  15. package/src/lib/app-utils/email-starter-policy.js +5 -3
  16. package/src/lib/app-utils/email-starter-policy.js.map +1 -1
  17. package/src/lib/app-utils/plan-entitlements.d.ts +34 -0
  18. package/src/lib/app-utils/plan-entitlements.js +40 -0
  19. package/src/lib/app-utils/plan-entitlements.js.map +1 -1
  20. package/src/lib/app-utils/server.d.ts +0 -1
  21. package/src/lib/app-utils/server.js +0 -1
  22. package/src/lib/app-utils/server.js.map +1 -1
  23. package/src/lib/plugin-manager/first-party-plugins.generated.js +1 -0
  24. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  25. package/src/lib/plugin-manager/plugin-artifact-inventory.d.ts +145 -0
  26. package/src/lib/plugin-manager/plugin-artifact-inventory.js +126 -0
  27. package/src/lib/plugin-manager/plugin-artifact-inventory.js.map +1 -0
  28. package/src/lib/plugin-manager/plugin-contact-capture.d.ts +32 -1
  29. package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -1
  30. package/src/lib/plugin-manager/plugin-lead-conversion.d.ts +67 -0
  31. package/src/lib/plugin-manager/plugin-lead-conversion.js +93 -0
  32. package/src/lib/plugin-manager/plugin-lead-conversion.js.map +1 -0
  33. package/src/lib/plugin-manager/plugin-org-capacity.d.ts +12 -0
  34. package/src/lib/plugin-manager/plugin-org-capacity.js.map +1 -1
  35. package/src/lib/plugin-manager/plugin-record-timeline.d.ts +8 -1
  36. package/src/lib/plugin-manager/plugin-record-timeline.js.map +1 -1
  37. package/src/lib/app-utils/marketplace-verification.d.ts +0 -95
  38. package/src/lib/app-utils/marketplace-verification.js +0 -81
  39. package/src/lib/app-utils/marketplace-verification.js.map +0 -1
  40. package/src/lib/app-utils/publisher-attestation.d.ts +0 -99
  41. package/src/lib/app-utils/publisher-attestation.js +0 -158
  42. package/src/lib/app-utils/publisher-attestation.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
1
2
  /**
2
3
  * @license
3
4
  * Copyright 2026 Aglyn LLC
@@ -41,7 +42,7 @@
41
42
  *
42
43
  * Pure data module: types, constants and the small helpers every surface
43
44
  * would otherwise write for itself. No Firestore, no React.
44
- */ import { _ as _extends } from "@swc/helpers/_/_extends";
45
+ */ import { normalizeAddress, normalizePhone } from "../foundation/index.js";
45
46
  import { consentGroupScope } from "./consent-groups.js";
46
47
  import { CONTACT_LIFECYCLE_STAGES, isContactLifecycleStage } from "./crm-kinds.js";
47
48
  import { CONTACT_FACETS_FIELD, CONTACT_SOURCE_LABELS, normalizeContactEmail, readContactFacet } from "./contacts.js";
@@ -1443,6 +1444,130 @@ export function parseCrmMemberRef(value) {
1443
1444
  export function isCrmLeadStatus(value) {
1444
1445
  return typeof value === 'string' && CRM_LEAD_STATUSES.includes(value);
1445
1446
  }
1447
+ /*==========================================
1448
+ * THE LEAD'S OWN PROFILE (AGL-3231).
1449
+ *
1450
+ * A Salesforce lead is a record of its own: it carries the person AND
1451
+ * their company as text — company, title, phone, website, address, the
1452
+ * source that produced it — and none of it is a contact until the lead
1453
+ * converts, when the convert step hands every field to the contact and the
1454
+ * account it creates. A lead that held only an address and a name could
1455
+ * not be worked without a contact beside it, which is how one person came
1456
+ * to sit in both lists; these fields are what let the lead stand alone.
1457
+ *
1458
+ * ## Text, not links
1459
+ *
1460
+ * `company` is the company's NAME, never a company id. A lead has not been
1461
+ * qualified, so the account it names may not deserve a record yet — a
1462
+ * thousand imported leads must not mint a thousand companies — and the
1463
+ * conversion is where the text becomes a link: it finds the company by
1464
+ * that name or by the address's domain, or creates it. The `companyId` on
1465
+ * `CrmLeadFields` is the conversion's stamp, written once, and never what
1466
+ * a lead carries while it is open.
1467
+ *
1468
+ * ## The same shapes the contact keeps
1469
+ *
1470
+ * The phone is E.164 through `normalizePhone`, the address is
1471
+ * `AglynPostalAddress` through `normalizeAddress`, tags are lower-cased and
1472
+ * capped as the contact's are, and the website is normalized as a
1473
+ * company's is — so the conversion copies values rather than translating
1474
+ * them, and a lead typed in the drawer and a contact typed in its drawer
1475
+ * refuse the same phone number in the same sentence.
1476
+ *
1477
+ * ## `leadSource` beside `sources`
1478
+ *
1479
+ * `sources` is the capture door's record of which SURFACES met the person
1480
+ * — `signup`, `booking`, `form:{id}`, `import` — and no file may rewrite
1481
+ * it. `leadSource` is what Salesforce calls Lead Source: free text the
1482
+ * team or the file supplies ("Sales Navigator", "Trade show"), reported on
1483
+ * and filtered by, and never derived. Two fields because they answer two
1484
+ * questions, and a file that could write the first would be rewriting the
1485
+ * site's own history.
1486
+ *=========================================*/ /** The longest text one profile field holds — the contact's own cap. */ export const CRM_LEAD_TEXT_MAX = 120;
1487
+ /** The most tags one lead keeps — the contact's own cap. */ export const CRM_LEAD_TAGS_MAX = 20;
1488
+ /** The longest note a lead keeps — the lead page's own cap. */ export const CRM_LEAD_NOTES_MAX = 4000;
1489
+ /** The profile's keys, in the order a card lists them. */ export const CRM_LEAD_PROFILE_KEYS = [
1490
+ 'company',
1491
+ 'jobTitle',
1492
+ 'phone',
1493
+ 'website',
1494
+ 'address',
1495
+ 'tags',
1496
+ 'leadSource'
1497
+ ];
1498
+ /** The sentence an unreadable phone number is refused with, under the field. */ export const CRM_LEAD_PHONE_REFUSAL = 'That phone number could not be read. Enter it with its country code, like ' + '+1 512 555 0107.';
1499
+ /** The sentence an unreadable website is refused with, under the field. */ export const CRM_LEAD_WEBSITE_REFUSAL = 'Enter a web address, like acme.com';
1500
+ /**
1501
+ * Tags as a lead stores them: lower-cased, trimmed, deduplicated, capped —
1502
+ * from an array or a comma-separated string, so a file cell and a form
1503
+ * field are one input.
1504
+ */ export function normalizeCrmLeadTags(value) {
1505
+ const items = Array.isArray(value) ? value : typeof value === 'string' ? value.split(',') : [];
1506
+ return [
1507
+ ...new Set(items.map((tag)=>String(tag != null ? tag : '').trim().slice(0, 40).toLowerCase()).filter(Boolean))
1508
+ ].slice(0, CRM_LEAD_TAGS_MAX);
1509
+ }
1510
+ /**
1511
+ * Read a profile off an untrusted body, one field at a time.
1512
+ *
1513
+ * Every key the body names is answered: as a normalized value, as `null`
1514
+ * when the body cleared it (an empty string, `null`, an empty list, a blank
1515
+ * address), or as an error under the field when the value cannot be
1516
+ * stored as typed. Keys the body does not name are not in the answer, so
1517
+ * a caller can write exactly what was asked. A `website` is normalized the
1518
+ * way a company's is; a phone the way a contact's is; both refuse rather
1519
+ * than store a value the record would then render as a link to nowhere.
1520
+ */ export function normalizeCrmLeadProfile(input) {
1521
+ const body = input != null ? input : {};
1522
+ const patch = {};
1523
+ const errors = {};
1524
+ const text = (key)=>{
1525
+ var _body_key;
1526
+ if (!(key in body)) return;
1527
+ const value = String((_body_key = body[key]) != null ? _body_key : '').trim().replace(/\s+/g, ' ').slice(0, CRM_LEAD_TEXT_MAX);
1528
+ patch[key] = value || null;
1529
+ };
1530
+ text('company');
1531
+ text('jobTitle');
1532
+ text('leadSource');
1533
+ if ('phone' in body) {
1534
+ var _body_phone;
1535
+ const raw = String((_body_phone = body['phone']) != null ? _body_phone : '').trim();
1536
+ if (!raw) patch.phone = null;
1537
+ else {
1538
+ const phone = normalizePhone(raw);
1539
+ if (phone) patch.phone = phone;
1540
+ else errors.phone = CRM_LEAD_PHONE_REFUSAL;
1541
+ }
1542
+ }
1543
+ if ('website' in body) {
1544
+ var _body_website;
1545
+ const raw = String((_body_website = body['website']) != null ? _body_website : '').trim();
1546
+ if (!raw) patch.website = null;
1547
+ else {
1548
+ const website = normalizeCompanyWebsite(raw);
1549
+ if (website) patch.website = website;
1550
+ else errors.website = CRM_LEAD_WEBSITE_REFUSAL;
1551
+ }
1552
+ }
1553
+ if ('address' in body) {
1554
+ const raw = body['address'];
1555
+ patch.address = raw && typeof raw === 'object' && !Array.isArray(raw) ? normalizeAddress(raw) : null;
1556
+ }
1557
+ if ('tags' in body) {
1558
+ const tags = normalizeCrmLeadTags(body['tags']);
1559
+ patch.tags = tags.length ? tags : null;
1560
+ }
1561
+ return {
1562
+ patch,
1563
+ errors
1564
+ };
1565
+ }
1566
+ /** The name a lead is listed under: the name it carries, else its address. */ export function crmLeadDisplayName(lead) {
1567
+ var _ref, _ref1;
1568
+ const name = String((_ref = lead == null ? void 0 : lead['name']) != null ? _ref : '').trim();
1569
+ return name || String((_ref1 = lead == null ? void 0 : lead['email']) != null ? _ref1 : '').trim();
1570
+ }
1446
1571
  /**
1447
1572
  * A lead's status as the list and the filter should read it.
1448
1573
  *