@agent-pattern-labs/leads-rig 0.1.7 → 0.1.8
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/docs/ARCHITECTURE.md
CHANGED
|
@@ -82,4 +82,8 @@ The package also carries the `@agent-pattern-labs/iso-*` helper ecosystem for tr
|
|
|
82
82
|
- page visits
|
|
83
83
|
- ingest requests
|
|
84
84
|
|
|
85
|
+
Lead records preserve optional enrichment fields such as `linkedinUrl`, `phone`,
|
|
86
|
+
`address`, `streetAddress`, `location`, `city`, `region`, `postalCode`, and
|
|
87
|
+
`country` through normalization, deduplication, and ingest.
|
|
88
|
+
|
|
85
89
|
The validator intentionally accepts the same defaults the local runtime normalizes, but it fails missing source evidence, invalid URL fields, invalid `emailType`, invalid `verificationStatus`, invalid confidence, person leads without email, and generic catch-all inboxes such as `info@`, `hello@`, or similar organizational aliases. Any email record must be a named `person` lead with a non-generic email and a human owner visible in the evidence; role inboxes, unknown-owner emails, blocked emails, unsupported emails, and unnamed person-like emails fail validation. Summaries expose `goodLeadCount`, which only counts those high-quality named human email records.
|
|
@@ -162,7 +162,15 @@ export function normalizeLead(input, { now = new Date().toISOString() } = {}) {
|
|
|
162
162
|
const verificationStatus = normalizeVerificationStatus(input.verificationStatus, emailType);
|
|
163
163
|
const confidence = clampInteger(input.confidence, 0, 100, 0);
|
|
164
164
|
const foundAt = normalizeDateTime(input.foundAt, now);
|
|
165
|
+
const linkedinUrl = normalizeLinkedInUrl(input.linkedinUrl);
|
|
165
166
|
const phone = stringValue(input.phone);
|
|
167
|
+
const address = compactWhitespace(input.address);
|
|
168
|
+
const streetAddress = compactWhitespace(input.streetAddress);
|
|
169
|
+
const location = compactWhitespace(input.location);
|
|
170
|
+
const city = compactWhitespace(input.city);
|
|
171
|
+
const region = compactWhitespace(input.region);
|
|
172
|
+
const postalCode = compactWhitespace(input.postalCode);
|
|
173
|
+
const country = compactWhitespace(input.country);
|
|
166
174
|
const socialUrls = normalizeUrlList(input.socialUrls);
|
|
167
175
|
const contactUrls = normalizeUrlList(input.contactUrls);
|
|
168
176
|
|
|
@@ -187,7 +195,15 @@ export function normalizeLead(input, { now = new Date().toISOString() } = {}) {
|
|
|
187
195
|
verificationStatus,
|
|
188
196
|
confidence,
|
|
189
197
|
warnings: normalizeWarnings(input.warnings),
|
|
198
|
+
linkedinUrl,
|
|
190
199
|
phone,
|
|
200
|
+
address,
|
|
201
|
+
streetAddress,
|
|
202
|
+
location,
|
|
203
|
+
city,
|
|
204
|
+
region,
|
|
205
|
+
postalCode,
|
|
206
|
+
country,
|
|
191
207
|
socialUrls,
|
|
192
208
|
contactUrls,
|
|
193
209
|
foundAt,
|
|
@@ -547,6 +563,15 @@ function normalizeLeadRecord(lead) {
|
|
|
547
563
|
sources,
|
|
548
564
|
evidence: sources[0]?.evidence || compactWhitespace(lead?.evidence),
|
|
549
565
|
warnings: normalizeWarnings(lead?.warnings),
|
|
566
|
+
linkedinUrl: normalizeLinkedInUrl(lead?.linkedinUrl),
|
|
567
|
+
phone: stringValue(lead?.phone),
|
|
568
|
+
address: compactWhitespace(lead?.address),
|
|
569
|
+
streetAddress: compactWhitespace(lead?.streetAddress),
|
|
570
|
+
location: compactWhitespace(lead?.location),
|
|
571
|
+
city: compactWhitespace(lead?.city),
|
|
572
|
+
region: compactWhitespace(lead?.region),
|
|
573
|
+
postalCode: compactWhitespace(lead?.postalCode),
|
|
574
|
+
country: compactWhitespace(lead?.country),
|
|
550
575
|
socialUrls: normalizeUrlList(lead?.socialUrls),
|
|
551
576
|
contactUrls: normalizeUrlList(lead?.contactUrls),
|
|
552
577
|
};
|
|
@@ -574,7 +599,15 @@ export function mergeLeadRecords(current, candidate) {
|
|
|
574
599
|
? secondary.verificationStatus
|
|
575
600
|
: primary.verificationStatus,
|
|
576
601
|
confidence: Math.max(primary.confidence || 0, secondary.confidence || 0),
|
|
602
|
+
linkedinUrl: primary.linkedinUrl || secondary.linkedinUrl,
|
|
577
603
|
phone: primary.phone || secondary.phone,
|
|
604
|
+
address: longerString(primary.address, secondary.address),
|
|
605
|
+
streetAddress: primary.streetAddress || secondary.streetAddress,
|
|
606
|
+
location: longerString(primary.location, secondary.location),
|
|
607
|
+
city: primary.city || secondary.city,
|
|
608
|
+
region: primary.region || secondary.region,
|
|
609
|
+
postalCode: primary.postalCode || secondary.postalCode,
|
|
610
|
+
country: primary.country || secondary.country,
|
|
578
611
|
socialUrls: normalizeUrlList([...(primary.socialUrls || []), ...(secondary.socialUrls || [])]),
|
|
579
612
|
contactUrls: normalizeUrlList([...(primary.contactUrls || []), ...(secondary.contactUrls || [])]),
|
|
580
613
|
warnings: normalizeWarnings([...(primary.warnings || []), ...(secondary.warnings || [])]),
|
|
@@ -842,6 +875,25 @@ function normalizeUrlList(value) {
|
|
|
842
875
|
return urls;
|
|
843
876
|
}
|
|
844
877
|
|
|
878
|
+
function normalizeLinkedInUrl(value) {
|
|
879
|
+
let raw = stringValue(value);
|
|
880
|
+
if (!raw) return '';
|
|
881
|
+
if (!raw.includes('://')) raw = `https://${raw}`;
|
|
882
|
+
|
|
883
|
+
try {
|
|
884
|
+
const url = new URL(raw);
|
|
885
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') return '';
|
|
886
|
+
const host = url.hostname.toLowerCase();
|
|
887
|
+
if (host !== 'linkedin.com' && !host.endsWith('.linkedin.com')) return '';
|
|
888
|
+
|
|
889
|
+
const path = url.pathname.replace(/\/+$/, '');
|
|
890
|
+
if (!/^\/(in|pub|company|school|showcase)\//i.test(path)) return '';
|
|
891
|
+
return `https://www.linkedin.com${path}`;
|
|
892
|
+
} catch {
|
|
893
|
+
return '';
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
845
897
|
function normalizeDateTime(value, fallback) {
|
|
846
898
|
const raw = stringValue(value);
|
|
847
899
|
if (!raw) return fallback;
|
|
@@ -876,6 +928,12 @@ function compactWhitespace(value) {
|
|
|
876
928
|
return stringValue(value).replace(/\s+/g, ' ');
|
|
877
929
|
}
|
|
878
930
|
|
|
931
|
+
function longerString(left, right) {
|
|
932
|
+
const first = stringValue(left);
|
|
933
|
+
const second = stringValue(right);
|
|
934
|
+
return second.length > first.length ? second : first;
|
|
935
|
+
}
|
|
936
|
+
|
|
879
937
|
function displayCompany(domain) {
|
|
880
938
|
const clean = normalizeDomain(domain);
|
|
881
939
|
if (!clean) return '';
|
package/package.json
CHANGED
package/templates/contracts.json
CHANGED
|
@@ -30,7 +30,15 @@
|
|
|
30
30
|
},
|
|
31
31
|
{ "name": "confidence", "type": "integer", "required": true, "min": 0, "max": 100 },
|
|
32
32
|
{ "name": "warnings", "type": "json" },
|
|
33
|
+
{ "name": "linkedinUrl", "type": "url" },
|
|
33
34
|
{ "name": "phone", "type": "string" },
|
|
35
|
+
{ "name": "address", "type": "string" },
|
|
36
|
+
{ "name": "streetAddress", "type": "string" },
|
|
37
|
+
{ "name": "location", "type": "string" },
|
|
38
|
+
{ "name": "city", "type": "string" },
|
|
39
|
+
{ "name": "region", "type": "string" },
|
|
40
|
+
{ "name": "postalCode", "type": "string" },
|
|
41
|
+
{ "name": "country", "type": "string" },
|
|
34
42
|
{ "name": "socialUrls", "type": "json" },
|
|
35
43
|
{ "name": "contactUrls", "type": "json" },
|
|
36
44
|
{ "name": "foundAt", "type": "datetime" }
|
|
@@ -74,10 +74,30 @@
|
|
|
74
74
|
"items": { "type": "string" },
|
|
75
75
|
"default": []
|
|
76
76
|
},
|
|
77
|
+
"linkedinUrl": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"description": "Optional canonical public LinkedIn profile or organization URL."
|
|
80
|
+
},
|
|
77
81
|
"phone": {
|
|
78
82
|
"type": "string",
|
|
79
83
|
"description": "Optional normalized phone number found near the public contact evidence."
|
|
80
84
|
},
|
|
85
|
+
"address": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"description": "Optional complete public business address."
|
|
88
|
+
},
|
|
89
|
+
"streetAddress": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"description": "Optional street-address component."
|
|
92
|
+
},
|
|
93
|
+
"location": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"description": "Optional display location for the lead."
|
|
96
|
+
},
|
|
97
|
+
"city": { "type": "string" },
|
|
98
|
+
"region": { "type": "string" },
|
|
99
|
+
"postalCode": { "type": "string" },
|
|
100
|
+
"country": { "type": "string" },
|
|
81
101
|
"socialUrls": {
|
|
82
102
|
"type": "array",
|
|
83
103
|
"items": { "type": "string" },
|