@absolutejs/engagement-apollo 0.0.12 → 0.0.14
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/README.md +18 -0
- package/dist/index.js +110 -26
- package/dist/manifest.js +2 -1
- package/dist/manifest.json +2 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -32,3 +32,21 @@ Company enrichment returns `null` for no match (404), throws `RateLimitError` fo
|
|
|
32
32
|
## License
|
|
33
33
|
|
|
34
34
|
Apache-2.0.
|
|
35
|
+
|
|
36
|
+
### Company research
|
|
37
|
+
|
|
38
|
+
`searchCompanies({ domain?, name?, limit?, signal? })` reads one bounded page
|
|
39
|
+
(default 5, maximum 100) without contact reveals or implicit enrichment. Apollo
|
|
40
|
+
charges one credit per page; meter each successful call. Errors are explicit,
|
|
41
|
+
including scope/authentication failures, so an unavailable search is not “no data.”
|
|
42
|
+
`enrichCompany` also accepts `website` and `linkedinUrl`; requests have a 30-second
|
|
43
|
+
timeout and accept cancellation.
|
|
44
|
+
|
|
45
|
+
Company `intelligence` retains provider-reported ownership, subsidiaries, retail
|
|
46
|
+
locations, industries, technologies, funding, source URL and observation time.
|
|
47
|
+
Missing fields remain null/empty, never inferred. Funding is not revenue, locations
|
|
48
|
+
are not customers, and organization revenue is not necessarily subscription ARR.
|
|
49
|
+
Apollo does not establish a vendor's pricing cadence or comparable business scope.
|
|
50
|
+
Use `companyIdentityMatch` / `selectCompanyIdentity` from the engagement contract
|
|
51
|
+
to require an independently returned exact domain and a compatible brand name;
|
|
52
|
+
ambiguous provider records must not be merged based on revenue size.
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ var APOLLO_BASE_URL = "https://api.apollo.io";
|
|
|
8
8
|
var DEFAULT_ACTIVITY_LIMIT = 50;
|
|
9
9
|
var DEFAULT_SEARCH_LIMIT = 6;
|
|
10
10
|
var LOCKED_EMAIL = /email_not_unlocked/iu;
|
|
11
|
-
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
11
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
12
|
var str = (value) => typeof value === "string" && value.length > 0 ? value : null;
|
|
13
13
|
var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
14
14
|
var companyDomain = (org) => {
|
|
@@ -29,6 +29,26 @@ var joinLocation = (parts) => {
|
|
|
29
29
|
const present = parts.filter((part) => part !== null);
|
|
30
30
|
return present.length > 0 ? present.join(", ") : null;
|
|
31
31
|
};
|
|
32
|
+
var strings = (value) => Array.isArray(value) ? value.flatMap((item) => typeof item === "string" && item.trim() ? [item.trim()] : []) : [];
|
|
33
|
+
var count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
34
|
+
var publicUrl = (value) => {
|
|
35
|
+
if (typeof value !== "string")
|
|
36
|
+
return null;
|
|
37
|
+
try {
|
|
38
|
+
const url = new URL(value);
|
|
39
|
+
return ["https:", "http:"].includes(url.protocol) && !url.username && !url.password ? url.href : null;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var reference = (value) => isRecord(value) ? {
|
|
45
|
+
providerId: str(value.id),
|
|
46
|
+
name: str(value.name),
|
|
47
|
+
domain: companyDomain(value),
|
|
48
|
+
country: str(value.country),
|
|
49
|
+
employeeCount: count(value.estimated_num_employees),
|
|
50
|
+
industries: strings(value.industries)
|
|
51
|
+
} : null;
|
|
32
52
|
var apolloSource = (options) => {
|
|
33
53
|
const baseUrl = options.baseUrl ?? APOLLO_BASE_URL;
|
|
34
54
|
const doFetch = options.fetchImpl ?? fetch;
|
|
@@ -49,19 +69,6 @@ var apolloSource = (options) => {
|
|
|
49
69
|
const json = await res.json();
|
|
50
70
|
return isRecord(json) ? json : null;
|
|
51
71
|
};
|
|
52
|
-
const get = async (path) => {
|
|
53
|
-
const res = await doFetch(`${baseUrl}${path}`, { headers, method: "GET" });
|
|
54
|
-
if (res.status === 429)
|
|
55
|
-
throw new RateLimitError("apollo");
|
|
56
|
-
if (res.status === 404)
|
|
57
|
-
return null;
|
|
58
|
-
if (!res.ok)
|
|
59
|
-
throw new EngagementProviderError("apollo", res.status, "company enrichment");
|
|
60
|
-
const json = await res.json();
|
|
61
|
-
if (!isRecord(json))
|
|
62
|
-
throw new EngagementProviderError("apollo", 502, "invalid company response");
|
|
63
|
-
return json;
|
|
64
|
-
};
|
|
65
72
|
const normalizePerson = (person) => {
|
|
66
73
|
const org = isRecord(person.organization) ? person.organization : null;
|
|
67
74
|
const contact = isRecord(person.contact) ? person.contact : null;
|
|
@@ -73,7 +80,7 @@ var apolloSource = (options) => {
|
|
|
73
80
|
].filter((value) => value !== null).filter((value) => !LOCKED_EMAIL.test(value))));
|
|
74
81
|
return {
|
|
75
82
|
company: org ? str(org.name) : null,
|
|
76
|
-
companyDomain: org ?
|
|
83
|
+
companyDomain: org ? companyDomain(org) : null,
|
|
77
84
|
emails,
|
|
78
85
|
facebookUrl: str(person.facebook_url),
|
|
79
86
|
firstName: str(person.first_name),
|
|
@@ -130,19 +137,37 @@ var apolloSource = (options) => {
|
|
|
130
137
|
searchRequests
|
|
131
138
|
};
|
|
132
139
|
};
|
|
133
|
-
const
|
|
134
|
-
if (!query.domain)
|
|
135
|
-
return null;
|
|
136
|
-
const params = new URLSearchParams({ domain: query.domain });
|
|
137
|
-
if (query.name?.trim())
|
|
138
|
-
params.set("name", query.name.trim());
|
|
139
|
-
const json = await get(`/v1/organizations/enrich?${params.toString()}`);
|
|
140
|
-
const org = json && isRecord(json.organization) ? json.organization : null;
|
|
141
|
-
if (!org)
|
|
142
|
-
return null;
|
|
140
|
+
const normalizeCompany = (org) => {
|
|
143
141
|
const revenue = num(org.annual_revenue);
|
|
144
142
|
const revenueDisplay = str(org.annual_revenue_printed);
|
|
145
143
|
return {
|
|
144
|
+
intelligence: {
|
|
145
|
+
provider: "apollo",
|
|
146
|
+
observedAt: new Date().toISOString(),
|
|
147
|
+
sourceUrl: /^[a-f0-9]{24}$/iu.test(str(org.id) ?? "") ? `https://app.apollo.io/#/organizations/${org.id}` : null,
|
|
148
|
+
websiteUrl: publicUrl(org.website_url),
|
|
149
|
+
location: joinLocation([
|
|
150
|
+
str(org.city),
|
|
151
|
+
str(org.state),
|
|
152
|
+
str(org.country)
|
|
153
|
+
]),
|
|
154
|
+
industries: strings(org.industries),
|
|
155
|
+
keywords: strings(org.keywords),
|
|
156
|
+
technologies: strings(org.technology_names),
|
|
157
|
+
retailLocationCount: count(org.retail_location_count),
|
|
158
|
+
activeRetailLocationCount: count(org.num_retail_locations),
|
|
159
|
+
parent: reference(org.owned_by_organization),
|
|
160
|
+
ultimateParent: reference(org.ultimate_parent_organization),
|
|
161
|
+
parentIds: strings(org.owned_by_chain),
|
|
162
|
+
subsidiaries: Array.isArray(org.suborganizations) ? org.suborganizations.flatMap((value) => {
|
|
163
|
+
const item = reference(value);
|
|
164
|
+
return item ? [item] : [];
|
|
165
|
+
}) : [],
|
|
166
|
+
subsidiaryCount: count(org.num_suborganizations),
|
|
167
|
+
totalFunding: count(org.total_funding),
|
|
168
|
+
latestFundingStage: str(org.latest_funding_stage),
|
|
169
|
+
latestFundingDate: str(org.latest_funding_round_date)
|
|
170
|
+
},
|
|
146
171
|
providerId: str(org.id),
|
|
147
172
|
annualRevenue: revenue !== null && revenue > 0 || revenueDisplay ? {
|
|
148
173
|
amount: revenue !== null && revenue > 0 ? revenue : null,
|
|
@@ -162,6 +187,64 @@ var apolloSource = (options) => {
|
|
|
162
187
|
raw: org
|
|
163
188
|
};
|
|
164
189
|
};
|
|
190
|
+
const companyRequest = async (path, signal, body) => {
|
|
191
|
+
signal?.throwIfAborted();
|
|
192
|
+
const res = await doFetch(`${baseUrl}${path}`, {
|
|
193
|
+
headers,
|
|
194
|
+
method: body ? "POST" : "GET",
|
|
195
|
+
...body ? { body: JSON.stringify(body) } : {},
|
|
196
|
+
signal: AbortSignal.any([
|
|
197
|
+
AbortSignal.timeout(30000),
|
|
198
|
+
...signal ? [signal] : []
|
|
199
|
+
])
|
|
200
|
+
});
|
|
201
|
+
if (res.status === 429)
|
|
202
|
+
throw new RateLimitError("apollo");
|
|
203
|
+
if (res.status === 404 && !body)
|
|
204
|
+
return null;
|
|
205
|
+
if (!res.ok)
|
|
206
|
+
throw new EngagementProviderError("apollo", res.status, body ? "company search" : "company enrichment");
|
|
207
|
+
const value = await res.json();
|
|
208
|
+
if (!isRecord(value))
|
|
209
|
+
throw new EngagementProviderError("apollo", 502, "invalid company response");
|
|
210
|
+
return value;
|
|
211
|
+
};
|
|
212
|
+
const enrichCompany = async (query) => {
|
|
213
|
+
if (!query.domain && !query.website && !query.linkedinUrl)
|
|
214
|
+
return null;
|
|
215
|
+
const params = new URLSearchParams;
|
|
216
|
+
if (query.domain)
|
|
217
|
+
params.set("domain", query.domain);
|
|
218
|
+
if (query.website)
|
|
219
|
+
params.set("website", query.website);
|
|
220
|
+
if (query.linkedinUrl)
|
|
221
|
+
params.set("linkedin_url", query.linkedinUrl);
|
|
222
|
+
if (query.name?.trim())
|
|
223
|
+
params.set("name", query.name.trim());
|
|
224
|
+
const json = await companyRequest(`/api/v1/organizations/enrich?${params}`, query.signal);
|
|
225
|
+
if (!json || json.organization === null)
|
|
226
|
+
return null;
|
|
227
|
+
if (!isRecord(json.organization))
|
|
228
|
+
throw new EngagementProviderError("apollo", 502, "invalid company enrichment response");
|
|
229
|
+
return normalizeCompany(json.organization);
|
|
230
|
+
};
|
|
231
|
+
const searchCompanies = async (query) => {
|
|
232
|
+
if (!query.domain && !query.name?.trim())
|
|
233
|
+
return { companies: [], searchRequests: 0 };
|
|
234
|
+
const limit = Number.isFinite(query.limit) ? Math.max(1, Math.min(100, Math.floor(query.limit))) : 5;
|
|
235
|
+
const json = await companyRequest("/api/v1/mixed_companies/search", query.signal, {
|
|
236
|
+
page: 1,
|
|
237
|
+
per_page: limit,
|
|
238
|
+
...query.domain ? { q_organization_domains_list: [query.domain] } : {},
|
|
239
|
+
...!query.domain && query.name ? { q_organization_name: query.name.trim() } : {}
|
|
240
|
+
});
|
|
241
|
+
if (!json || !Array.isArray(json.organizations) || json.organizations.some((value) => !isRecord(value)))
|
|
242
|
+
throw new EngagementProviderError("apollo", 502, "invalid company search response");
|
|
243
|
+
return {
|
|
244
|
+
companies: json.organizations.filter(isRecord).slice(0, limit).map(normalizeCompany),
|
|
245
|
+
searchRequests: 1
|
|
246
|
+
};
|
|
247
|
+
};
|
|
165
248
|
const listActivities = async (query) => {
|
|
166
249
|
const json = await post("/v1/emailer_messages/search", {
|
|
167
250
|
per_page: query.limit ?? DEFAULT_ACTIVITY_LIMIT,
|
|
@@ -201,7 +284,8 @@ var apolloSource = (options) => {
|
|
|
201
284
|
enrichPerson,
|
|
202
285
|
id: "apollo",
|
|
203
286
|
listActivities,
|
|
204
|
-
searchPeople
|
|
287
|
+
searchPeople,
|
|
288
|
+
searchCompanies
|
|
205
289
|
};
|
|
206
290
|
};
|
|
207
291
|
export {
|
package/dist/manifest.js
CHANGED
|
@@ -10,6 +10,7 @@ var manifest = defineManifest()({
|
|
|
10
10
|
"enrich a person with Apollo",
|
|
11
11
|
"enrich a company with Apollo",
|
|
12
12
|
"search people by role",
|
|
13
|
+
"research company revenue and ownership",
|
|
13
14
|
"read Apollo outreach activity"
|
|
14
15
|
],
|
|
15
16
|
keywords: ["apollo", "engagement", "enrichment", "outreach", "sales"],
|
|
@@ -40,7 +41,7 @@ var manifest = defineManifest()({
|
|
|
40
41
|
peers: [
|
|
41
42
|
{
|
|
42
43
|
name: "@absolutejs/engagement",
|
|
43
|
-
range: ">=0.0.
|
|
44
|
+
range: ">=0.0.10 <0.1",
|
|
44
45
|
reason: "Shared engagement contracts and rate-limit identity"
|
|
45
46
|
}
|
|
46
47
|
]
|
package/dist/manifest.json
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"enrich a person with Apollo",
|
|
10
10
|
"enrich a company with Apollo",
|
|
11
11
|
"search people by role",
|
|
12
|
+
"research company revenue and ownership",
|
|
12
13
|
"read Apollo outreach activity"
|
|
13
14
|
],
|
|
14
15
|
"keywords": [
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
"peers": [
|
|
48
49
|
{
|
|
49
50
|
"name": "@absolutejs/engagement",
|
|
50
|
-
"range": ">=0.0.
|
|
51
|
+
"range": ">=0.0.10 <0.1",
|
|
51
52
|
"reason": "Shared engagement contracts and rate-limit identity"
|
|
52
53
|
}
|
|
53
54
|
]
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"author": "Alex Kahn",
|
|
3
3
|
"description": "Apollo.io EngagementSource adapter for @absolutejs/engagement — person/company enrichment and outreach activity (emails sent/opened/replied) from the Apollo API.",
|
|
4
4
|
"devDependencies": {
|
|
5
|
-
"@absolutejs/engagement": "0.0.
|
|
5
|
+
"@absolutejs/engagement": "0.0.10",
|
|
6
6
|
"@types/bun": "1.3.14",
|
|
7
7
|
"prettier": "3.8.1",
|
|
8
8
|
"typescript": "5.9.3"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"main": "./dist/index.js",
|
|
24
24
|
"name": "@absolutejs/engagement-apollo",
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@absolutejs/engagement": ">=0.0.
|
|
26
|
+
"@absolutejs/engagement": ">=0.0.10 <0.1"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"type": "module",
|
|
46
46
|
"types": "./dist/index.d.ts",
|
|
47
|
-
"version": "0.0.
|
|
47
|
+
"version": "0.0.14",
|
|
48
48
|
"exports": {
|
|
49
49
|
".": {
|
|
50
50
|
"types": "./dist/index.d.ts",
|