linkedin-secret-sauce 0.10.1 → 0.11.0

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.
@@ -39,7 +39,7 @@ export { isPersonalEmail, isBusinessEmail, isPersonalDomain, PERSONAL_DOMAINS, }
39
39
  export { isDisposableEmail, isDisposableDomain, DISPOSABLE_DOMAINS, } from "./utils/disposable-domains";
40
40
  export { isValidEmailSyntax, isRoleAccount, asciiFold, cleanNamePart, hostnameFromUrl, extractLinkedInUsername, } from "./utils/validation";
41
41
  export { verifyEmailMx } from "./verification/mx";
42
- export { createConstructProvider, createLddProvider, createSmartProspectProvider, createHunterProvider, createApolloProvider, createDropcontactProvider, } from "./providers";
42
+ export { createConstructProvider, createLddProvider, createSmartProspectProvider, createHunterProvider, createApolloProvider, createDropcontactProvider, createBouncerProvider, createSnovioProvider, verifyEmailWithBouncer, checkCatchAllDomain, verifyEmailsBatch, findEmailsWithSnovio, verifyEmailWithSnovio, clearSnovioTokenCache, } from "./providers";
43
43
  export { extractNumericLinkedInId } from "./providers/ldd";
44
44
  export { createSmartProspectClient, type SmartProspectClient, type SmartProspectLocationOptions, } from "./providers/smartprospect";
45
45
  export { enrichBusinessEmail, enrichBatch, enrichAllEmails, enrichAllBatch } from "./orchestrator";
@@ -42,7 +42,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
42
42
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.salesLeadToContact = exports.getEmailsForLinkedInContactsBatch = exports.getEmailsForLinkedInContact = exports.createLinkedInEnricher = exports.enrichLinkedInContactsBatch = exports.enrichLinkedInContact = exports.parseLinkedInSearchResponse = exports.buildSmartProspectFiltersFromLinkedIn = exports.matchContacts = exports.findBestMatch = exports.classifyMatchQuality = exports.calculateMatchConfidence = exports.clearFileCache = exports.isFileCacheEnabled = exports.disableFileCache = exports.enableFileCache = exports.getSmartLeadTokenCacheStats = exports.clearAllSmartLeadTokens = exports.clearSmartLeadToken = exports.getSmartLeadUser = exports.getSmartLeadToken = exports.enrichAllBatch = exports.enrichAllEmails = exports.enrichBatch = exports.enrichBusinessEmail = exports.createSmartProspectClient = exports.extractNumericLinkedInId = exports.createDropcontactProvider = exports.createApolloProvider = exports.createHunterProvider = exports.createSmartProspectProvider = exports.createLddProvider = exports.createConstructProvider = exports.verifyEmailMx = exports.extractLinkedInUsername = exports.hostnameFromUrl = exports.cleanNamePart = exports.asciiFold = exports.isRoleAccount = exports.isValidEmailSyntax = exports.DISPOSABLE_DOMAINS = exports.isDisposableDomain = exports.isDisposableEmail = exports.PERSONAL_DOMAINS = exports.isPersonalDomain = exports.isBusinessEmail = exports.isPersonalEmail = void 0;
45
+ exports.enrichLinkedInContact = exports.parseLinkedInSearchResponse = exports.buildSmartProspectFiltersFromLinkedIn = exports.matchContacts = exports.findBestMatch = exports.classifyMatchQuality = exports.calculateMatchConfidence = exports.clearFileCache = exports.isFileCacheEnabled = exports.disableFileCache = exports.enableFileCache = exports.getSmartLeadTokenCacheStats = exports.clearAllSmartLeadTokens = exports.clearSmartLeadToken = exports.getSmartLeadUser = exports.getSmartLeadToken = exports.enrichAllBatch = exports.enrichAllEmails = exports.enrichBatch = exports.enrichBusinessEmail = exports.createSmartProspectClient = exports.extractNumericLinkedInId = exports.clearSnovioTokenCache = exports.verifyEmailWithSnovio = exports.findEmailsWithSnovio = exports.verifyEmailsBatch = exports.checkCatchAllDomain = exports.verifyEmailWithBouncer = exports.createSnovioProvider = exports.createBouncerProvider = exports.createDropcontactProvider = exports.createApolloProvider = exports.createHunterProvider = exports.createSmartProspectProvider = exports.createLddProvider = exports.createConstructProvider = exports.verifyEmailMx = exports.extractLinkedInUsername = exports.hostnameFromUrl = exports.cleanNamePart = exports.asciiFold = exports.isRoleAccount = exports.isValidEmailSyntax = exports.DISPOSABLE_DOMAINS = exports.isDisposableDomain = exports.isDisposableEmail = exports.PERSONAL_DOMAINS = exports.isPersonalDomain = exports.isBusinessEmail = exports.isPersonalEmail = void 0;
46
+ exports.salesLeadToContact = exports.getEmailsForLinkedInContactsBatch = exports.getEmailsForLinkedInContact = exports.createLinkedInEnricher = exports.enrichLinkedInContactsBatch = void 0;
46
47
  exports.createEnrichmentClient = createEnrichmentClient;
47
48
  const orchestrator_1 = require("./orchestrator");
48
49
  const construct_1 = require("./providers/construct");
@@ -51,13 +52,27 @@ const smartprospect_1 = require("./providers/smartprospect");
51
52
  const hunter_1 = require("./providers/hunter");
52
53
  const apollo_1 = require("./providers/apollo");
53
54
  const dropcontact_1 = require("./providers/dropcontact");
55
+ const bouncer_1 = require("./providers/bouncer");
56
+ const snovio_1 = require("./providers/snovio");
54
57
  /**
55
58
  * Default provider order
59
+ *
60
+ * Strategy:
61
+ * 1. construct - FREE pattern guessing with MX check
62
+ * 2. bouncer - SMTP verification of construct results ($0.006/email)
63
+ * 3. ldd - FREE LinkedIn data dump lookup
64
+ * 4. smartprospect - Paid SmartLead lookup ($0.01/email)
65
+ * 5. snovio - Email finder for catch-all domains ($0.02/email)
66
+ * 6. hunter - Hunter.io API ($0.005/email)
67
+ * 7. apollo - FREE Apollo.io lookup
68
+ * 8. dropcontact - Dropcontact API ($0.01/email)
56
69
  */
57
70
  const DEFAULT_ORDER = [
58
71
  "construct",
72
+ "bouncer",
59
73
  "ldd",
60
74
  "smartprospect",
75
+ "snovio",
61
76
  "hunter",
62
77
  "apollo",
63
78
  "dropcontact",
@@ -90,6 +105,12 @@ function createEnrichmentClient(config) {
90
105
  if (providerConfigs.dropcontact) {
91
106
  providerFuncs.set("dropcontact", (0, dropcontact_1.createDropcontactProvider)(providerConfigs.dropcontact));
92
107
  }
108
+ if (providerConfigs.bouncer) {
109
+ providerFuncs.set("bouncer", (0, bouncer_1.createBouncerProvider)(providerConfigs.bouncer));
110
+ }
111
+ if (providerConfigs.snovio) {
112
+ providerFuncs.set("snovio", (0, snovio_1.createSnovioProvider)(providerConfigs.snovio));
113
+ }
93
114
  // Build ordered provider list
94
115
  const providerOrder = options.providerOrder ?? DEFAULT_ORDER;
95
116
  const orderedProviders = [];
@@ -250,6 +271,16 @@ Object.defineProperty(exports, "createSmartProspectProvider", { enumerable: true
250
271
  Object.defineProperty(exports, "createHunterProvider", { enumerable: true, get: function () { return providers_1.createHunterProvider; } });
251
272
  Object.defineProperty(exports, "createApolloProvider", { enumerable: true, get: function () { return providers_1.createApolloProvider; } });
252
273
  Object.defineProperty(exports, "createDropcontactProvider", { enumerable: true, get: function () { return providers_1.createDropcontactProvider; } });
274
+ Object.defineProperty(exports, "createBouncerProvider", { enumerable: true, get: function () { return providers_1.createBouncerProvider; } });
275
+ Object.defineProperty(exports, "createSnovioProvider", { enumerable: true, get: function () { return providers_1.createSnovioProvider; } });
276
+ // Bouncer utilities
277
+ Object.defineProperty(exports, "verifyEmailWithBouncer", { enumerable: true, get: function () { return providers_1.verifyEmailWithBouncer; } });
278
+ Object.defineProperty(exports, "checkCatchAllDomain", { enumerable: true, get: function () { return providers_1.checkCatchAllDomain; } });
279
+ Object.defineProperty(exports, "verifyEmailsBatch", { enumerable: true, get: function () { return providers_1.verifyEmailsBatch; } });
280
+ // Snov.io utilities
281
+ Object.defineProperty(exports, "findEmailsWithSnovio", { enumerable: true, get: function () { return providers_1.findEmailsWithSnovio; } });
282
+ Object.defineProperty(exports, "verifyEmailWithSnovio", { enumerable: true, get: function () { return providers_1.verifyEmailWithSnovio; } });
283
+ Object.defineProperty(exports, "clearSnovioTokenCache", { enumerable: true, get: function () { return providers_1.clearSnovioTokenCache; } });
253
284
  // Re-export LDD utilities
254
285
  var ldd_2 = require("./providers/ldd");
255
286
  Object.defineProperty(exports, "extractNumericLinkedInId", { enumerable: true, get: function () { return ldd_2.extractNumericLinkedInId; } });
@@ -15,6 +15,13 @@ const disposable_domains_1 = require("./utils/disposable-domains");
15
15
  const validation_1 = require("./utils/validation");
16
16
  /**
17
17
  * Default provider costs in USD per lookup
18
+ *
19
+ * Costs based on 2025 pricing:
20
+ * - Bouncer: $0.006/email at scale (best accuracy 99%+)
21
+ * - Snov.io: $0.02/email (email finding + verification)
22
+ * - Hunter: $0.005/email
23
+ * - SmartProspect: $0.01/email
24
+ * - Dropcontact: $0.01/email
18
25
  */
19
26
  const _PROVIDER_COSTS = {
20
27
  construct: 0,
@@ -23,6 +30,8 @@ const _PROVIDER_COSTS = {
23
30
  hunter: 0.005,
24
31
  apollo: 0,
25
32
  dropcontact: 0.01,
33
+ bouncer: 0.006,
34
+ snovio: 0.02,
26
35
  };
27
36
  /**
28
37
  * Normalize provider result to canonical format
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Bouncer.io Email Verification Provider
3
+ *
4
+ * Provides SMTP-level email verification with 99%+ accuracy.
5
+ * Best for verifying pattern-guessed emails on non-catch-all domains.
6
+ *
7
+ * Features:
8
+ * - SMTP verification (checks if mailbox exists)
9
+ * - Catch-all domain detection
10
+ * - Disposable email detection
11
+ * - Role account detection
12
+ * - Toxicity scoring (0-5)
13
+ *
14
+ * @see https://docs.usebouncer.com
15
+ */
16
+ import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, BouncerConfig, BouncerVerifyResponse } from '../types';
17
+ /**
18
+ * Create the Bouncer verification provider
19
+ *
20
+ * NOTE: This provider is a VERIFIER, not a FINDER. It verifies emails
21
+ * that were generated by the construct provider or passed in the candidate.
22
+ *
23
+ * Usage in the enrichment flow:
24
+ * 1. construct provider generates email patterns
25
+ * 2. bouncer provider verifies which patterns are deliverable
26
+ * 3. Only verified emails are returned
27
+ */
28
+ export declare function createBouncerProvider(config: BouncerConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | ProviderMultiResult | null>;
29
+ /**
30
+ * Standalone function to verify a single email via Bouncer
31
+ *
32
+ * Useful for ad-hoc verification outside the enrichment flow.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const result = await verifyEmailWithBouncer('test@example.com', {
37
+ * apiKey: process.env.BOUNCER_API_KEY,
38
+ * });
39
+ * console.log(result.status); // 'deliverable' | 'undeliverable' | 'risky' | 'unknown'
40
+ * ```
41
+ */
42
+ export declare function verifyEmailWithBouncer(email: string, config: BouncerConfig): Promise<BouncerVerifyResponse | null>;
43
+ /**
44
+ * Check if a domain is catch-all via Bouncer
45
+ *
46
+ * Sends a verification request for a random email and checks acceptAll flag.
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const isCatchAll = await checkCatchAllDomain('example.com', {
51
+ * apiKey: process.env.BOUNCER_API_KEY,
52
+ * });
53
+ * ```
54
+ */
55
+ export declare function checkCatchAllDomain(domain: string, config: BouncerConfig): Promise<boolean | null>;
56
+ /**
57
+ * Verify multiple emails in batch via Bouncer
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const results = await verifyEmailsBatch(
62
+ * ['john@example.com', 'jane@example.com'],
63
+ * { apiKey: process.env.BOUNCER_API_KEY }
64
+ * );
65
+ * ```
66
+ */
67
+ export declare function verifyEmailsBatch(emails: string[], config: BouncerConfig): Promise<Map<string, BouncerVerifyResponse | null>>;
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ /**
3
+ * Bouncer.io Email Verification Provider
4
+ *
5
+ * Provides SMTP-level email verification with 99%+ accuracy.
6
+ * Best for verifying pattern-guessed emails on non-catch-all domains.
7
+ *
8
+ * Features:
9
+ * - SMTP verification (checks if mailbox exists)
10
+ * - Catch-all domain detection
11
+ * - Disposable email detection
12
+ * - Role account detection
13
+ * - Toxicity scoring (0-5)
14
+ *
15
+ * @see https://docs.usebouncer.com
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.createBouncerProvider = createBouncerProvider;
19
+ exports.verifyEmailWithBouncer = verifyEmailWithBouncer;
20
+ exports.checkCatchAllDomain = checkCatchAllDomain;
21
+ exports.verifyEmailsBatch = verifyEmailsBatch;
22
+ const DEFAULT_API_URL = 'https://api.usebouncer.com/v1.1';
23
+ const DEFAULT_TIMEOUT_MS = 30000;
24
+ /**
25
+ * Map Bouncer status to confidence score
26
+ */
27
+ function statusToConfidence(status, response) {
28
+ switch (status) {
29
+ case 'deliverable':
30
+ // High confidence - email verified as deliverable
31
+ // Reduce slightly if catch-all or high toxicity
32
+ let score = 95;
33
+ if (response.acceptAll)
34
+ score -= 20; // Catch-all reduces confidence
35
+ if (response.toxicity && response.toxicity >= 3)
36
+ score -= 10;
37
+ if (response.role)
38
+ score -= 5; // Role accounts slightly lower
39
+ return Math.max(50, score);
40
+ case 'risky':
41
+ // Medium confidence - risky but might work
42
+ return response.acceptAll ? 40 : 50;
43
+ case 'undeliverable':
44
+ // Email does not exist
45
+ return 0;
46
+ case 'unknown':
47
+ // Could not verify
48
+ return 30;
49
+ default:
50
+ return 0;
51
+ }
52
+ }
53
+ /**
54
+ * Extract email candidates from the enrichment candidate
55
+ * Bouncer is a VERIFICATION provider, so it needs emails to verify
56
+ */
57
+ function extractEmailsToVerify(candidate) {
58
+ const emails = [];
59
+ // Check if candidate has pre-generated email patterns in metadata
60
+ const metadata = candidate._emailCandidates;
61
+ if (Array.isArray(metadata)) {
62
+ emails.push(...metadata.filter((e) => typeof e === 'string'));
63
+ }
64
+ return emails;
65
+ }
66
+ /**
67
+ * Verify a single email via Bouncer API
68
+ */
69
+ async function verifyEmail(email, apiKey, apiUrl, timeoutMs) {
70
+ const controller = new AbortController();
71
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
72
+ try {
73
+ const url = `${apiUrl}/email/verify?email=${encodeURIComponent(email)}`;
74
+ const response = await fetch(url, {
75
+ method: 'GET',
76
+ headers: {
77
+ 'x-api-key': apiKey,
78
+ 'Accept': 'application/json',
79
+ },
80
+ signal: controller.signal,
81
+ });
82
+ if (!response.ok) {
83
+ // All HTTP errors return null - let caller decide how to handle
84
+ return null;
85
+ }
86
+ const data = await response.json();
87
+ return data;
88
+ }
89
+ catch {
90
+ // All errors (network, timeout, etc.) return null
91
+ return null;
92
+ }
93
+ finally {
94
+ clearTimeout(timeoutId);
95
+ }
96
+ }
97
+ /**
98
+ * Create the Bouncer verification provider
99
+ *
100
+ * NOTE: This provider is a VERIFIER, not a FINDER. It verifies emails
101
+ * that were generated by the construct provider or passed in the candidate.
102
+ *
103
+ * Usage in the enrichment flow:
104
+ * 1. construct provider generates email patterns
105
+ * 2. bouncer provider verifies which patterns are deliverable
106
+ * 3. Only verified emails are returned
107
+ */
108
+ function createBouncerProvider(config) {
109
+ const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS } = config;
110
+ if (!apiKey) {
111
+ // Return no-op provider if not configured
112
+ const noop = async () => null;
113
+ noop.__name = 'bouncer';
114
+ return noop;
115
+ }
116
+ async function verifyEmails(candidate) {
117
+ const emailsToVerify = extractEmailsToVerify(candidate);
118
+ // If no emails to verify, return null
119
+ // The orchestrator will need to pass emails from construct
120
+ if (emailsToVerify.length === 0) {
121
+ return null;
122
+ }
123
+ const verifiedEmails = [];
124
+ // Verify each email candidate
125
+ for (const email of emailsToVerify) {
126
+ try {
127
+ const result = await verifyEmail(email, apiKey, apiUrl, timeoutMs);
128
+ if (result) {
129
+ const confidence = statusToConfidence(result.status, result);
130
+ // Only include emails that are potentially deliverable
131
+ if (result.status === 'deliverable' || result.status === 'risky') {
132
+ verifiedEmails.push({
133
+ email: result.email,
134
+ verified: result.status === 'deliverable',
135
+ confidence,
136
+ isCatchAll: result.acceptAll,
137
+ metadata: {
138
+ bouncerStatus: result.status,
139
+ bouncerReason: result.reason,
140
+ toxicity: result.toxicity,
141
+ isDisposable: result.disposable,
142
+ isRole: result.role,
143
+ isFreeProvider: result.free,
144
+ },
145
+ });
146
+ }
147
+ }
148
+ }
149
+ catch {
150
+ // Continue with other emails on error
151
+ continue;
152
+ }
153
+ }
154
+ if (verifiedEmails.length === 0) {
155
+ return null;
156
+ }
157
+ // Sort by confidence
158
+ verifiedEmails.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
159
+ return { emails: verifiedEmails };
160
+ }
161
+ // Mark provider name for orchestrator
162
+ verifyEmails.__name = 'bouncer';
163
+ return verifyEmails;
164
+ }
165
+ /**
166
+ * Standalone function to verify a single email via Bouncer
167
+ *
168
+ * Useful for ad-hoc verification outside the enrichment flow.
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * const result = await verifyEmailWithBouncer('test@example.com', {
173
+ * apiKey: process.env.BOUNCER_API_KEY,
174
+ * });
175
+ * console.log(result.status); // 'deliverable' | 'undeliverable' | 'risky' | 'unknown'
176
+ * ```
177
+ */
178
+ async function verifyEmailWithBouncer(email, config) {
179
+ const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS } = config;
180
+ return verifyEmail(email, apiKey, apiUrl, timeoutMs);
181
+ }
182
+ /**
183
+ * Check if a domain is catch-all via Bouncer
184
+ *
185
+ * Sends a verification request for a random email and checks acceptAll flag.
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * const isCatchAll = await checkCatchAllDomain('example.com', {
190
+ * apiKey: process.env.BOUNCER_API_KEY,
191
+ * });
192
+ * ```
193
+ */
194
+ async function checkCatchAllDomain(domain, config) {
195
+ const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS } = config;
196
+ // Generate a random email that almost certainly doesn't exist
197
+ const randomLocal = `bounce-test-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
198
+ const testEmail = `${randomLocal}@${domain}`;
199
+ const result = await verifyEmail(testEmail, apiKey, apiUrl, timeoutMs);
200
+ if (!result) {
201
+ return null; // Could not determine
202
+ }
203
+ // If acceptAll is true, the domain accepts all emails (catch-all)
204
+ return result.acceptAll;
205
+ }
206
+ /**
207
+ * Verify multiple emails in batch via Bouncer
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * const results = await verifyEmailsBatch(
212
+ * ['john@example.com', 'jane@example.com'],
213
+ * { apiKey: process.env.BOUNCER_API_KEY }
214
+ * );
215
+ * ```
216
+ */
217
+ async function verifyEmailsBatch(emails, config) {
218
+ const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS } = config;
219
+ const results = new Map();
220
+ // Verify in parallel with concurrency limit
221
+ const CONCURRENCY = 5;
222
+ for (let i = 0; i < emails.length; i += CONCURRENCY) {
223
+ const batch = emails.slice(i, i + CONCURRENCY);
224
+ const batchResults = await Promise.all(batch.map(async (email) => {
225
+ const result = await verifyEmail(email, apiKey, apiUrl, timeoutMs);
226
+ return { email, result };
227
+ }));
228
+ for (const { email, result } of batchResults) {
229
+ results.set(email, result);
230
+ }
231
+ }
232
+ return results;
233
+ }
@@ -7,3 +7,5 @@ export { createSmartProspectProvider } from './smartprospect';
7
7
  export { createHunterProvider } from './hunter';
8
8
  export { createApolloProvider } from './apollo';
9
9
  export { createDropcontactProvider } from './dropcontact';
10
+ export { createBouncerProvider, verifyEmailWithBouncer, checkCatchAllDomain, verifyEmailsBatch } from './bouncer';
11
+ export { createSnovioProvider, findEmailsWithSnovio, verifyEmailWithSnovio, clearSnovioTokenCache } from './snovio';
@@ -3,7 +3,7 @@
3
3
  * Email Enrichment Providers
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createDropcontactProvider = exports.createApolloProvider = exports.createHunterProvider = exports.createSmartProspectProvider = exports.createLddProvider = exports.createConstructProvider = void 0;
6
+ exports.clearSnovioTokenCache = exports.verifyEmailWithSnovio = exports.findEmailsWithSnovio = exports.createSnovioProvider = exports.verifyEmailsBatch = exports.checkCatchAllDomain = exports.verifyEmailWithBouncer = exports.createBouncerProvider = exports.createDropcontactProvider = exports.createApolloProvider = exports.createHunterProvider = exports.createSmartProspectProvider = exports.createLddProvider = exports.createConstructProvider = void 0;
7
7
  var construct_1 = require("./construct");
8
8
  Object.defineProperty(exports, "createConstructProvider", { enumerable: true, get: function () { return construct_1.createConstructProvider; } });
9
9
  var ldd_1 = require("./ldd");
@@ -16,3 +16,13 @@ var apollo_1 = require("./apollo");
16
16
  Object.defineProperty(exports, "createApolloProvider", { enumerable: true, get: function () { return apollo_1.createApolloProvider; } });
17
17
  var dropcontact_1 = require("./dropcontact");
18
18
  Object.defineProperty(exports, "createDropcontactProvider", { enumerable: true, get: function () { return dropcontact_1.createDropcontactProvider; } });
19
+ var bouncer_1 = require("./bouncer");
20
+ Object.defineProperty(exports, "createBouncerProvider", { enumerable: true, get: function () { return bouncer_1.createBouncerProvider; } });
21
+ Object.defineProperty(exports, "verifyEmailWithBouncer", { enumerable: true, get: function () { return bouncer_1.verifyEmailWithBouncer; } });
22
+ Object.defineProperty(exports, "checkCatchAllDomain", { enumerable: true, get: function () { return bouncer_1.checkCatchAllDomain; } });
23
+ Object.defineProperty(exports, "verifyEmailsBatch", { enumerable: true, get: function () { return bouncer_1.verifyEmailsBatch; } });
24
+ var snovio_1 = require("./snovio");
25
+ Object.defineProperty(exports, "createSnovioProvider", { enumerable: true, get: function () { return snovio_1.createSnovioProvider; } });
26
+ Object.defineProperty(exports, "findEmailsWithSnovio", { enumerable: true, get: function () { return snovio_1.findEmailsWithSnovio; } });
27
+ Object.defineProperty(exports, "verifyEmailWithSnovio", { enumerable: true, get: function () { return snovio_1.verifyEmailWithSnovio; } });
28
+ Object.defineProperty(exports, "clearSnovioTokenCache", { enumerable: true, get: function () { return snovio_1.clearSnovioTokenCache; } });
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Snov.io Email Finder Provider
3
+ *
4
+ * Provides email finding with 98% delivery rate and built-in verification.
5
+ * Best for finding emails when pattern guessing fails or on catch-all domains.
6
+ *
7
+ * Features:
8
+ * - Email finding by name + domain
9
+ * - Built-in email verification
10
+ * - Database of 70M+ contacts
11
+ * - LinkedIn profile support
12
+ *
13
+ * @see https://snov.io/api
14
+ */
15
+ import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, SnovioConfig, SnovioGetEmailsResponse, SnovioVerificationStatus } from '../types';
16
+ /**
17
+ * Create the Snov.io email finder provider
18
+ *
19
+ * This provider uses Snov.io's database to find verified emails
20
+ * when pattern guessing fails or for catch-all domains.
21
+ */
22
+ export declare function createSnovioProvider(config: SnovioConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | ProviderMultiResult | null>;
23
+ /**
24
+ * Standalone function to find emails via Snov.io
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const result = await findEmailsWithSnovio(
29
+ * 'John',
30
+ * 'Doe',
31
+ * 'example.com',
32
+ * {
33
+ * userId: process.env.SNOVIO_USER_ID,
34
+ * apiSecret: process.env.SNOVIO_API_SECRET,
35
+ * }
36
+ * );
37
+ * ```
38
+ */
39
+ export declare function findEmailsWithSnovio(firstName: string, lastName: string, domain: string, config: SnovioConfig): Promise<SnovioGetEmailsResponse | null>;
40
+ /**
41
+ * Verify a single email via Snov.io
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const result = await verifyEmailWithSnovio('test@example.com', {
46
+ * userId: process.env.SNOVIO_USER_ID,
47
+ * apiSecret: process.env.SNOVIO_API_SECRET,
48
+ * });
49
+ * ```
50
+ */
51
+ export declare function verifyEmailWithSnovio(email: string, config: SnovioConfig): Promise<{
52
+ email: string;
53
+ status: SnovioVerificationStatus;
54
+ } | null>;
55
+ /**
56
+ * Clear the cached access token (useful for testing)
57
+ */
58
+ export declare function clearSnovioTokenCache(): void;
@@ -0,0 +1,286 @@
1
+ "use strict";
2
+ /**
3
+ * Snov.io Email Finder Provider
4
+ *
5
+ * Provides email finding with 98% delivery rate and built-in verification.
6
+ * Best for finding emails when pattern guessing fails or on catch-all domains.
7
+ *
8
+ * Features:
9
+ * - Email finding by name + domain
10
+ * - Built-in email verification
11
+ * - Database of 70M+ contacts
12
+ * - LinkedIn profile support
13
+ *
14
+ * @see https://snov.io/api
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createSnovioProvider = createSnovioProvider;
18
+ exports.findEmailsWithSnovio = findEmailsWithSnovio;
19
+ exports.verifyEmailWithSnovio = verifyEmailWithSnovio;
20
+ exports.clearSnovioTokenCache = clearSnovioTokenCache;
21
+ const DEFAULT_API_URL = 'https://api.snov.io';
22
+ const DEFAULT_TIMEOUT_MS = 30000;
23
+ // Token cache
24
+ let cachedAccessToken = null;
25
+ let tokenExpiresAt = 0;
26
+ /**
27
+ * Map Snov.io verification status to confidence score
28
+ */
29
+ function statusToConfidence(status) {
30
+ switch (status) {
31
+ case 'valid':
32
+ return 95; // High confidence - verified email
33
+ case 'catch_all':
34
+ return 60; // Medium confidence - catch-all domain
35
+ case 'unverifiable':
36
+ return 40; // Low confidence - could not verify
37
+ case 'not_valid':
38
+ return 0; // Invalid email
39
+ case 'unknown':
40
+ default:
41
+ return 30;
42
+ }
43
+ }
44
+ /**
45
+ * Extract name and domain from candidate
46
+ */
47
+ function extractNameAndDomain(candidate) {
48
+ const firstName = candidate.firstName ||
49
+ candidate.first_name ||
50
+ candidate.first ||
51
+ candidate.name?.split(' ')?.[0] ||
52
+ '';
53
+ const lastName = candidate.lastName ||
54
+ candidate.last_name ||
55
+ candidate.last ||
56
+ candidate.name?.split(' ')?.slice(1).join(' ') ||
57
+ '';
58
+ const domain = candidate.domain ||
59
+ candidate.companyDomain ||
60
+ candidate.company_domain ||
61
+ '';
62
+ if (!firstName || !domain) {
63
+ return null;
64
+ }
65
+ return { firstName, lastName, domain };
66
+ }
67
+ /**
68
+ * Get OAuth access token from Snov.io
69
+ */
70
+ async function getAccessToken(userId, apiSecret, apiUrl, timeoutMs) {
71
+ // Return cached token if valid
72
+ if (cachedAccessToken && Date.now() < tokenExpiresAt) {
73
+ return cachedAccessToken;
74
+ }
75
+ const controller = new AbortController();
76
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
77
+ try {
78
+ const response = await fetch(`${apiUrl}/v1/oauth/access_token`, {
79
+ method: 'POST',
80
+ headers: {
81
+ 'Content-Type': 'application/json',
82
+ },
83
+ body: JSON.stringify({
84
+ grant_type: 'client_credentials',
85
+ client_id: userId,
86
+ client_secret: apiSecret,
87
+ }),
88
+ signal: controller.signal,
89
+ });
90
+ if (!response.ok) {
91
+ return null;
92
+ }
93
+ const data = await response.json();
94
+ // Cache token with 5 minute buffer
95
+ cachedAccessToken = data.access_token;
96
+ tokenExpiresAt = Date.now() + (data.expires_in - 300) * 1000;
97
+ return data.access_token;
98
+ }
99
+ catch {
100
+ return null;
101
+ }
102
+ finally {
103
+ clearTimeout(timeoutId);
104
+ }
105
+ }
106
+ /**
107
+ * Find emails using Snov.io's get-emails-from-name API
108
+ */
109
+ async function findEmailsByName(firstName, lastName, domain, accessToken, apiUrl, timeoutMs) {
110
+ const controller = new AbortController();
111
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
112
+ try {
113
+ const params = new URLSearchParams({
114
+ access_token: accessToken,
115
+ firstName,
116
+ lastName,
117
+ domain,
118
+ });
119
+ const response = await fetch(`${apiUrl}/v1/get-emails-from-names?${params}`, {
120
+ method: 'GET',
121
+ headers: {
122
+ 'Accept': 'application/json',
123
+ },
124
+ signal: controller.signal,
125
+ });
126
+ if (!response.ok) {
127
+ // Handle rate limiting
128
+ if (response.status === 429) {
129
+ throw new Error('Rate limited by Snov.io API');
130
+ }
131
+ return null;
132
+ }
133
+ const data = await response.json();
134
+ return data;
135
+ }
136
+ catch (error) {
137
+ if (error instanceof Error && error.name === 'AbortError') {
138
+ return null; // Timeout
139
+ }
140
+ throw error;
141
+ }
142
+ finally {
143
+ clearTimeout(timeoutId);
144
+ }
145
+ }
146
+ /**
147
+ * Create the Snov.io email finder provider
148
+ *
149
+ * This provider uses Snov.io's database to find verified emails
150
+ * when pattern guessing fails or for catch-all domains.
151
+ */
152
+ function createSnovioProvider(config) {
153
+ const { userId, apiSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
154
+ if (!userId || !apiSecret) {
155
+ // Return no-op provider if not configured
156
+ const noop = async () => null;
157
+ noop.__name = 'snovio';
158
+ return noop;
159
+ }
160
+ async function findEmails(candidate) {
161
+ const nameAndDomain = extractNameAndDomain(candidate);
162
+ if (!nameAndDomain) {
163
+ return null;
164
+ }
165
+ const { firstName, lastName, domain } = nameAndDomain;
166
+ // Get access token
167
+ const accessToken = await getAccessToken(userId, apiSecret, apiUrl, timeoutMs);
168
+ if (!accessToken) {
169
+ return null;
170
+ }
171
+ // Find emails
172
+ const result = await findEmailsByName(firstName, lastName, domain, accessToken, apiUrl, timeoutMs);
173
+ if (!result || !result.success || !result.data?.emails?.length) {
174
+ return null;
175
+ }
176
+ // Convert to provider multi-result format
177
+ const emails = result.data.emails
178
+ .filter((e) => e.email && e.emailStatus !== 'not_valid')
179
+ .map((e) => ({
180
+ email: e.email,
181
+ verified: e.emailStatus === 'valid',
182
+ confidence: statusToConfidence(e.emailStatus),
183
+ isCatchAll: e.emailStatus === 'catch_all',
184
+ metadata: {
185
+ snovioStatus: e.emailStatus,
186
+ firstName: e.firstName,
187
+ lastName: e.lastName,
188
+ position: e.position,
189
+ companyName: e.companyName,
190
+ sourcePage: e.sourcePage,
191
+ type: e.type,
192
+ },
193
+ }));
194
+ if (emails.length === 0) {
195
+ return null;
196
+ }
197
+ // Sort by confidence
198
+ emails.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
199
+ return { emails };
200
+ }
201
+ // Mark provider name for orchestrator
202
+ findEmails.__name = 'snovio';
203
+ return findEmails;
204
+ }
205
+ /**
206
+ * Standalone function to find emails via Snov.io
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * const result = await findEmailsWithSnovio(
211
+ * 'John',
212
+ * 'Doe',
213
+ * 'example.com',
214
+ * {
215
+ * userId: process.env.SNOVIO_USER_ID,
216
+ * apiSecret: process.env.SNOVIO_API_SECRET,
217
+ * }
218
+ * );
219
+ * ```
220
+ */
221
+ async function findEmailsWithSnovio(firstName, lastName, domain, config) {
222
+ const { userId, apiSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
223
+ const accessToken = await getAccessToken(userId, apiSecret, apiUrl, timeoutMs);
224
+ if (!accessToken) {
225
+ return null;
226
+ }
227
+ return findEmailsByName(firstName, lastName, domain, accessToken, apiUrl, timeoutMs);
228
+ }
229
+ /**
230
+ * Verify a single email via Snov.io
231
+ *
232
+ * @example
233
+ * ```typescript
234
+ * const result = await verifyEmailWithSnovio('test@example.com', {
235
+ * userId: process.env.SNOVIO_USER_ID,
236
+ * apiSecret: process.env.SNOVIO_API_SECRET,
237
+ * });
238
+ * ```
239
+ */
240
+ async function verifyEmailWithSnovio(email, config) {
241
+ const { userId, apiSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
242
+ const accessToken = await getAccessToken(userId, apiSecret, apiUrl, timeoutMs);
243
+ if (!accessToken) {
244
+ return null;
245
+ }
246
+ const controller = new AbortController();
247
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
248
+ try {
249
+ const params = new URLSearchParams({
250
+ access_token: accessToken,
251
+ email,
252
+ });
253
+ const response = await fetch(`${apiUrl}/v1/email-verifier?${params}`, {
254
+ method: 'GET',
255
+ headers: {
256
+ 'Accept': 'application/json',
257
+ },
258
+ signal: controller.signal,
259
+ });
260
+ if (!response.ok) {
261
+ return null;
262
+ }
263
+ const data = await response.json();
264
+ if (!data.success || !data.data?.emails?.length) {
265
+ return null;
266
+ }
267
+ const result = data.data.emails[0];
268
+ return {
269
+ email: result.email,
270
+ status: result.result,
271
+ };
272
+ }
273
+ catch {
274
+ return null;
275
+ }
276
+ finally {
277
+ clearTimeout(timeoutId);
278
+ }
279
+ }
280
+ /**
281
+ * Clear the cached access token (useful for testing)
282
+ */
283
+ function clearSnovioTokenCache() {
284
+ cachedAccessToken = null;
285
+ tokenExpiresAt = 0;
286
+ }
@@ -166,6 +166,109 @@ export interface LddConfig {
166
166
  export interface DropcontactConfig {
167
167
  apiKey: string;
168
168
  }
169
+ /**
170
+ * Bouncer.io provider configuration
171
+ *
172
+ * Bouncer provides SMTP email verification with 99%+ accuracy.
173
+ * Best for verifying pattern-guessed emails on non-catch-all domains.
174
+ *
175
+ * @see https://docs.usebouncer.com
176
+ */
177
+ export interface BouncerConfig {
178
+ /** Bouncer API key */
179
+ apiKey: string;
180
+ /** API URL override (default: https://api.usebouncer.com/v1.1) */
181
+ apiUrl?: string;
182
+ /** Timeout in ms (default: 30000) */
183
+ timeoutMs?: number;
184
+ }
185
+ /**
186
+ * Bouncer verification result status
187
+ */
188
+ export type BouncerStatus = 'deliverable' | 'undeliverable' | 'risky' | 'unknown';
189
+ /**
190
+ * Bouncer verification result reason
191
+ */
192
+ export type BouncerReason = 'accepted_email' | 'rejected_email' | 'invalid_domain' | 'invalid_email' | 'unavailable_smtp' | 'dns_error' | 'low_deliverability' | 'low_quality' | 'catch_all' | 'full_mailbox' | 'role_account' | 'disposable' | 'timeout' | 'unknown';
193
+ /**
194
+ * Bouncer API response for single email verification
195
+ */
196
+ export interface BouncerVerifyResponse {
197
+ status: BouncerStatus;
198
+ reason: BouncerReason;
199
+ email: string;
200
+ domain: string;
201
+ account: string;
202
+ /** Whether this is a free email provider (Gmail, Yahoo, etc.) */
203
+ free: boolean;
204
+ /** Whether this is a disposable email */
205
+ disposable: boolean;
206
+ /** Whether this is a role-based email */
207
+ role: boolean;
208
+ /** Whether domain is catch-all (accepts all emails) */
209
+ acceptAll: boolean;
210
+ /** Did you mean suggestion for typos */
211
+ didYouMean?: string;
212
+ /** DNS information */
213
+ dns?: {
214
+ type: string;
215
+ record: string;
216
+ };
217
+ /** Toxicity score (0-5, higher = more risky) */
218
+ toxicity?: number;
219
+ }
220
+ /**
221
+ * Snov.io provider configuration
222
+ *
223
+ * Snov.io provides email finding with 98% delivery rate and built-in verification.
224
+ * Best for finding emails when pattern guessing fails or on catch-all domains.
225
+ *
226
+ * @see https://snov.io/api
227
+ */
228
+ export interface SnovioConfig {
229
+ /** Snov.io API user ID */
230
+ userId: string;
231
+ /** Snov.io API secret */
232
+ apiSecret: string;
233
+ /** API URL override (default: https://api.snov.io) */
234
+ apiUrl?: string;
235
+ /** Timeout in ms (default: 30000) */
236
+ timeoutMs?: number;
237
+ }
238
+ /**
239
+ * Snov.io email verification status
240
+ */
241
+ export type SnovioVerificationStatus = 'valid' | 'not_valid' | 'catch_all' | 'unverifiable' | 'unknown';
242
+ /**
243
+ * Snov.io email result
244
+ */
245
+ export interface SnovioEmailResult {
246
+ email: string;
247
+ emailStatus: SnovioVerificationStatus;
248
+ firstName?: string;
249
+ lastName?: string;
250
+ position?: string;
251
+ sourcePage?: string;
252
+ companyName?: string;
253
+ type?: 'prospect' | 'personal';
254
+ status?: string;
255
+ }
256
+ /**
257
+ * Snov.io get-emails-from-name API response
258
+ */
259
+ export interface SnovioGetEmailsResponse {
260
+ success: boolean;
261
+ data: {
262
+ firstName: string;
263
+ lastName: string;
264
+ emails: SnovioEmailResult[];
265
+ };
266
+ params?: {
267
+ firstName: string;
268
+ lastName: string;
269
+ domain: string;
270
+ };
271
+ }
169
272
  /**
170
273
  * Email construction provider configuration (no API key needed)
171
274
  */
@@ -185,6 +288,10 @@ export interface ProvidersConfig {
185
288
  hunter?: HunterConfig;
186
289
  apollo?: ApolloConfig;
187
290
  dropcontact?: DropcontactConfig;
291
+ /** Bouncer.io for SMTP email verification (99%+ accuracy) */
292
+ bouncer?: BouncerConfig;
293
+ /** Snov.io for email finding (98% delivery rate) */
294
+ snovio?: SnovioConfig;
188
295
  }
189
296
  /**
190
297
  * Options for enrichment operations
@@ -263,13 +370,30 @@ export interface EnrichmentClient {
263
370
  /**
264
371
  * Available provider names
265
372
  */
266
- export type ProviderName = "construct" | "ldd" | "smartprospect" | "hunter" | "apollo" | "dropcontact";
373
+ export type ProviderName = "construct" | "ldd" | "smartprospect" | "hunter" | "apollo" | "dropcontact" | "bouncer" | "snovio";
267
374
  /**
268
375
  * Default provider order
376
+ *
377
+ * Strategy:
378
+ * 1. construct - FREE pattern guessing with MX check
379
+ * 2. bouncer - SMTP verification of construct results ($0.006/email)
380
+ * 3. ldd - FREE LinkedIn data dump lookup
381
+ * 4. smartprospect - Paid SmartLead lookup ($0.01/email)
382
+ * 5. snovio - Email finder for catch-all domains ($0.02/email)
383
+ * 6. hunter - Hunter.io API ($0.005/email)
384
+ * 7. apollo - FREE Apollo.io lookup
385
+ * 8. dropcontact - Dropcontact API ($0.01/email)
269
386
  */
270
387
  export declare const DEFAULT_PROVIDER_ORDER: ProviderName[];
271
388
  /**
272
389
  * Provider costs in USD per lookup
390
+ *
391
+ * Costs based on 2025 pricing:
392
+ * - Bouncer: $0.006/email at scale (best accuracy 99%+)
393
+ * - Snov.io: $0.02/email (email finding + verification)
394
+ * - Hunter: $0.005/email
395
+ * - SmartProspect: $0.01/email
396
+ * - Dropcontact: $0.01/email
273
397
  */
274
398
  export declare const PROVIDER_COSTS: Record<ProviderName, number>;
275
399
  /**
@@ -9,17 +9,36 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.SMARTPROSPECT_SUB_INDUSTRIES = exports.PROVIDER_COSTS = exports.DEFAULT_PROVIDER_ORDER = void 0;
10
10
  /**
11
11
  * Default provider order
12
+ *
13
+ * Strategy:
14
+ * 1. construct - FREE pattern guessing with MX check
15
+ * 2. bouncer - SMTP verification of construct results ($0.006/email)
16
+ * 3. ldd - FREE LinkedIn data dump lookup
17
+ * 4. smartprospect - Paid SmartLead lookup ($0.01/email)
18
+ * 5. snovio - Email finder for catch-all domains ($0.02/email)
19
+ * 6. hunter - Hunter.io API ($0.005/email)
20
+ * 7. apollo - FREE Apollo.io lookup
21
+ * 8. dropcontact - Dropcontact API ($0.01/email)
12
22
  */
13
23
  exports.DEFAULT_PROVIDER_ORDER = [
14
24
  "construct",
25
+ "bouncer",
15
26
  "ldd",
16
27
  "smartprospect",
28
+ "snovio",
17
29
  "hunter",
18
30
  "apollo",
19
31
  "dropcontact",
20
32
  ];
21
33
  /**
22
34
  * Provider costs in USD per lookup
35
+ *
36
+ * Costs based on 2025 pricing:
37
+ * - Bouncer: $0.006/email at scale (best accuracy 99%+)
38
+ * - Snov.io: $0.02/email (email finding + verification)
39
+ * - Hunter: $0.005/email
40
+ * - SmartProspect: $0.01/email
41
+ * - Dropcontact: $0.01/email
23
42
  */
24
43
  exports.PROVIDER_COSTS = {
25
44
  construct: 0,
@@ -28,6 +47,8 @@ exports.PROVIDER_COSTS = {
28
47
  hunter: 0.005,
29
48
  apollo: 0,
30
49
  dropcontact: 0.01,
50
+ bouncer: 0.006,
51
+ snovio: 0.02,
31
52
  };
32
53
  /**
33
54
  * SmartProspect Sub-Industry values (exact API values - partial list)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linkedin-secret-sauce",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "Private LinkedIn Sales Navigator client with automatic cookie management",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,26 +10,6 @@
10
10
  "publishConfig": {
11
11
  "registry": "https://registry.npmjs.org/"
12
12
  },
13
- "scripts": {
14
- "dev:playground": "pnpm -C apps/playground dev",
15
- "search": "node scripts/rg-fast.mjs",
16
- "rg:fast": "node scripts/rg-fast.mjs",
17
- "build": "tsc -p tsconfig.json",
18
- "typecheck": "tsc --noEmit",
19
- "typecheck:playground": "pnpm -C apps/playground typecheck",
20
- "typecheck:all": "pnpm typecheck && pnpm typecheck:playground",
21
- "lint": "eslint \"src/**/*.ts\" --max-warnings=0",
22
- "lint:playground": "eslint \"apps/playground/src/**/*.{ts,tsx}\" \"apps/playground/server/**/*.ts\" --max-warnings=0",
23
- "lint:all": "eslint \"src/**/*.ts\" \"apps/playground/src/**/*.{ts,tsx}\" \"apps/playground/server/**/*.ts\" --max-warnings=0",
24
- "lint:fix": "eslint \"src/**/*.ts\" \"apps/playground/src/**/*.{ts,tsx}\" \"apps/playground/server/**/*.ts\" --fix",
25
- "check": "pnpm typecheck:all && pnpm lint:all",
26
- "dev": "tsc -w -p tsconfig.json",
27
- "test": "vitest run",
28
- "prepublishOnly": "npm run build",
29
- "release:patch": "npm version patch && git push --follow-tags",
30
- "release:minor": "npm version minor && git push --follow-tags",
31
- "release:major": "npm version major && git push --follow-tags"
32
- },
33
13
  "keywords": [
34
14
  "linkedin",
35
15
  "sales-navigator",
@@ -59,5 +39,24 @@
59
39
  "husky": "^9.0.11",
60
40
  "typescript": "^5.9.3",
61
41
  "vitest": "^1.6.0"
42
+ },
43
+ "scripts": {
44
+ "dev:playground": "pnpm -C apps/playground dev",
45
+ "search": "node scripts/rg-fast.mjs",
46
+ "rg:fast": "node scripts/rg-fast.mjs",
47
+ "build": "tsc -p tsconfig.json",
48
+ "typecheck": "tsc --noEmit",
49
+ "typecheck:playground": "pnpm -C apps/playground typecheck",
50
+ "typecheck:all": "pnpm typecheck && pnpm typecheck:playground",
51
+ "lint": "eslint \"src/**/*.ts\" --max-warnings=0",
52
+ "lint:playground": "eslint \"apps/playground/src/**/*.{ts,tsx}\" \"apps/playground/server/**/*.ts\" --max-warnings=0",
53
+ "lint:all": "eslint \"src/**/*.ts\" \"apps/playground/src/**/*.{ts,tsx}\" \"apps/playground/server/**/*.ts\" --max-warnings=0",
54
+ "lint:fix": "eslint \"src/**/*.ts\" \"apps/playground/src/**/*.{ts,tsx}\" \"apps/playground/server/**/*.ts\" --fix",
55
+ "check": "pnpm typecheck:all && pnpm lint:all",
56
+ "dev": "tsc -w -p tsconfig.json",
57
+ "test": "vitest run",
58
+ "release:patch": "npm version patch && git push --follow-tags",
59
+ "release:minor": "npm version minor && git push --follow-tags",
60
+ "release:major": "npm version major && git push --follow-tags"
62
61
  }
63
- }
62
+ }