salesprompter-cli 0.1.0 → 0.1.1
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/package.json +6 -2
- package/.codex/environments/environment.toml +0 -19
- package/data/deel-icp.json +0 -17
- package/data/deel-leads.json +0 -77
- package/data/enriched.json +0 -106
- package/data/icp.json +0 -24
- package/data/leads.json +0 -62
- package/data/scored.json +0 -142
- package/dist-tests/src/cli.js +0 -114
- package/dist-tests/src/domain.js +0 -36
- package/dist-tests/src/engine.js +0 -147
- package/dist-tests/src/io.js +0 -17
- package/dist-tests/src/providers.js +0 -1
- package/dist-tests/src/sample-data.js +0 -34
- package/dist-tests/tests/cli.test.js +0 -149
- package/src/cli.ts +0 -136
- package/src/domain.ts +0 -50
- package/src/engine.ts +0 -170
- package/src/io.ts +0 -21
- package/src/providers.ts +0 -22
- package/src/sample-data.ts +0 -37
- package/tests/cli.test.ts +0 -184
- package/tsconfig.json +0 -16
- package/tsconfig.test.json +0 -8
package/src/engine.ts
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import type { EnrichmentProvider, LeadGenerationTarget, LeadProvider, ScoringProvider, SyncProvider } from "./providers.js";
|
|
3
|
-
import type { EnrichedLead, Icp, Lead, ScoredLead, SyncTarget } from "./domain.js";
|
|
4
|
-
import { SAMPLE_COMPANIES, SAMPLE_CONTACTS, SAMPLE_SIGNALS, SAMPLE_TECH } from "./sample-data.js";
|
|
5
|
-
|
|
6
|
-
function pickByIndex<T>(items: T[], index: number): T {
|
|
7
|
-
const item = items[index % items.length];
|
|
8
|
-
if (item === undefined) {
|
|
9
|
-
throw new Error("sample data invariant violated");
|
|
10
|
-
}
|
|
11
|
-
return item;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function normalizeCompanySize(employeeCount: number): string {
|
|
15
|
-
if (employeeCount < 50) {
|
|
16
|
-
return "1-49";
|
|
17
|
-
}
|
|
18
|
-
if (employeeCount < 200) {
|
|
19
|
-
return "50-199";
|
|
20
|
-
}
|
|
21
|
-
if (employeeCount < 500) {
|
|
22
|
-
return "200-499";
|
|
23
|
-
}
|
|
24
|
-
return "500+";
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function deriveCompanyNameFromDomain(domain: string): string {
|
|
28
|
-
const normalizedDomain = domain
|
|
29
|
-
.toLowerCase()
|
|
30
|
-
.replace(/^https?:\/\//, "")
|
|
31
|
-
.replace(/^www\./, "")
|
|
32
|
-
.split("/")[0];
|
|
33
|
-
const hostname = normalizedDomain.split(".")[0] ?? normalizedDomain;
|
|
34
|
-
|
|
35
|
-
return hostname
|
|
36
|
-
.split(/[-_]/)
|
|
37
|
-
.filter((part) => part.length > 0)
|
|
38
|
-
.map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`)
|
|
39
|
-
.join(" ");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function buildTargetCompany(target: LeadGenerationTarget, fallback: (typeof SAMPLE_COMPANIES)[number], icp: Icp) {
|
|
43
|
-
const domain = target.companyDomain?.trim().toLowerCase();
|
|
44
|
-
if (domain === undefined) {
|
|
45
|
-
return fallback;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
companyName: target.companyName?.trim() || deriveCompanyNameFromDomain(domain),
|
|
50
|
-
domain,
|
|
51
|
-
industry: icp.industries[0] ?? fallback.industry,
|
|
52
|
-
region: icp.regions[0] ?? fallback.region,
|
|
53
|
-
employeeCount: fallback.employeeCount
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export class HeuristicLeadProvider implements LeadProvider {
|
|
58
|
-
async generateLeads(icp: Icp, count: number, target: LeadGenerationTarget = {}): Promise<Lead[]> {
|
|
59
|
-
return Array.from({ length: count }, (_, index) => {
|
|
60
|
-
const fallbackCompany = pickByIndex(SAMPLE_COMPANIES, index);
|
|
61
|
-
const company = buildTargetCompany(target, fallbackCompany, icp);
|
|
62
|
-
const contact = pickByIndex(SAMPLE_CONTACTS, index + 1);
|
|
63
|
-
const signals = [pickByIndex(SAMPLE_SIGNALS, index), pickByIndex(SAMPLE_SIGNALS, index + 2)];
|
|
64
|
-
const industry = icp.industries[index % Math.max(icp.industries.length, 1)] ?? company.industry;
|
|
65
|
-
const region = icp.regions[index % Math.max(icp.regions.length, 1)] ?? company.region;
|
|
66
|
-
const title = icp.titles[index % Math.max(icp.titles.length, 1)] ?? contact.title;
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
companyName: company.companyName,
|
|
70
|
-
domain: company.domain,
|
|
71
|
-
industry,
|
|
72
|
-
region,
|
|
73
|
-
employeeCount: company.employeeCount,
|
|
74
|
-
contactName: contact.contactName,
|
|
75
|
-
title,
|
|
76
|
-
email: `${contact.contactName.toLowerCase().replaceAll(" ", ".")}@${company.domain}`,
|
|
77
|
-
source: target.companyDomain ? "heuristic-target-account" : "heuristic-seed",
|
|
78
|
-
signals
|
|
79
|
-
};
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export class HeuristicEnrichmentProvider implements EnrichmentProvider {
|
|
85
|
-
async enrichLeads(leads: Lead[]): Promise<EnrichedLead[]> {
|
|
86
|
-
return leads.map((lead, index) => ({
|
|
87
|
-
...lead,
|
|
88
|
-
techStack: [pickByIndex(SAMPLE_TECH, index), pickByIndex(SAMPLE_TECH, index + 3)],
|
|
89
|
-
crmFit: lead.employeeCount > 200 ? "high" : lead.employeeCount > 100 ? "medium" : "low",
|
|
90
|
-
outreachFit: lead.signals.some((signal) => signal.includes("outbound")) ? "high" : "medium",
|
|
91
|
-
buyingStage: lead.signals.some((signal) => signal.includes("funding")) ? "active-evaluation" : "solution-aware",
|
|
92
|
-
notes: [
|
|
93
|
-
`${lead.companyName} matches the ${lead.industry} segment.`,
|
|
94
|
-
`${lead.contactName} is likely close to revenue tooling decisions.`
|
|
95
|
-
]
|
|
96
|
-
}));
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export class HeuristicScoringProvider implements ScoringProvider {
|
|
101
|
-
async scoreLeads(icp: Icp, leads: EnrichedLead[]): Promise<ScoredLead[]> {
|
|
102
|
-
return leads.map((lead) => {
|
|
103
|
-
const rationale: string[] = [];
|
|
104
|
-
let score = 40;
|
|
105
|
-
|
|
106
|
-
if (icp.industries.includes(lead.industry)) {
|
|
107
|
-
score += 20;
|
|
108
|
-
rationale.push("Industry matches ICP.");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (icp.regions.includes(lead.region)) {
|
|
112
|
-
score += 10;
|
|
113
|
-
rationale.push("Region matches ICP.");
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const normalizedSize = normalizeCompanySize(lead.employeeCount);
|
|
117
|
-
if (icp.companySizes.includes(normalizedSize)) {
|
|
118
|
-
score += 10;
|
|
119
|
-
rationale.push("Company size matches ICP.");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (icp.titles.includes(lead.title)) {
|
|
123
|
-
score += 10;
|
|
124
|
-
rationale.push("Contact title matches ICP.");
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const requiredMatches = icp.requiredSignals.filter((signal) => lead.signals.includes(signal));
|
|
128
|
-
score += Math.min(requiredMatches.length * 5, 10);
|
|
129
|
-
if (requiredMatches.length > 0) {
|
|
130
|
-
rationale.push(`Matched ${requiredMatches.length} required buying signals.`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const excludedMatches = icp.excludedSignals.filter((signal) => lead.signals.includes(signal));
|
|
134
|
-
score -= excludedMatches.length * 15;
|
|
135
|
-
if (excludedMatches.length > 0) {
|
|
136
|
-
rationale.push(`Matched ${excludedMatches.length} excluded signals.`);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (lead.crmFit === "high") {
|
|
140
|
-
score += 5;
|
|
141
|
-
rationale.push("Strong CRM fit.");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (lead.outreachFit === "high") {
|
|
145
|
-
score += 5;
|
|
146
|
-
rationale.push("Strong outreach fit.");
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const clampedScore = z.number().int().min(0).max(100).parse(score);
|
|
150
|
-
const grade = clampedScore >= 85 ? "A" : clampedScore >= 70 ? "B" : clampedScore >= 55 ? "C" : "D";
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
...lead,
|
|
154
|
-
score: clampedScore,
|
|
155
|
-
grade,
|
|
156
|
-
rationale
|
|
157
|
-
};
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export class DryRunSyncProvider implements SyncProvider {
|
|
163
|
-
async sync(target: SyncTarget, leads: ScoredLead[]): Promise<{ target: SyncTarget; synced: number; dryRun: boolean }> {
|
|
164
|
-
return {
|
|
165
|
-
target,
|
|
166
|
-
synced: leads.length,
|
|
167
|
-
dryRun: true
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
}
|
package/src/io.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
|
|
5
|
-
export async function readJsonFile<T>(filePath: string, schema: z.ZodType<T>): Promise<T> {
|
|
6
|
-
const content = await readFile(filePath, "utf8");
|
|
7
|
-
const parsed = JSON.parse(content) as unknown;
|
|
8
|
-
return schema.parse(parsed);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function writeJsonFile(filePath: string, value: unknown): Promise<void> {
|
|
12
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
13
|
-
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function splitCsv(value: string): string[] {
|
|
17
|
-
return value
|
|
18
|
-
.split(",")
|
|
19
|
-
.map((entry) => entry.trim())
|
|
20
|
-
.filter((entry) => entry.length > 0);
|
|
21
|
-
}
|
package/src/providers.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { EnrichedLead, Icp, Lead, ScoredLead, SyncTarget } from "./domain.js";
|
|
2
|
-
|
|
3
|
-
export interface LeadGenerationTarget {
|
|
4
|
-
companyDomain?: string;
|
|
5
|
-
companyName?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface LeadProvider {
|
|
9
|
-
generateLeads(icp: Icp, count: number, target?: LeadGenerationTarget): Promise<Lead[]>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface EnrichmentProvider {
|
|
13
|
-
enrichLeads(leads: Lead[]): Promise<EnrichedLead[]>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ScoringProvider {
|
|
17
|
-
scoreLeads(icp: Icp, leads: EnrichedLead[]): Promise<ScoredLead[]>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface SyncProvider {
|
|
21
|
-
sync(target: SyncTarget, leads: ScoredLead[]): Promise<{ target: SyncTarget; synced: number; dryRun: boolean }>;
|
|
22
|
-
}
|
package/src/sample-data.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export const SAMPLE_COMPANIES = [
|
|
2
|
-
{ companyName: "Northstar Freight", domain: "northstarfreight.com", industry: "Logistics", region: "North America", employeeCount: 180 },
|
|
3
|
-
{ companyName: "Brightpath Health", domain: "brightpathhealth.io", industry: "Healthcare", region: "Europe", employeeCount: 320 },
|
|
4
|
-
{ companyName: "ForgeOps Cloud", domain: "forgeopscloud.dev", industry: "Software", region: "North America", employeeCount: 85 },
|
|
5
|
-
{ companyName: "Summit Retail Group", domain: "summitretailgroup.com", industry: "Retail", region: "Europe", employeeCount: 540 },
|
|
6
|
-
{ companyName: "Atlas Industrial", domain: "atlasindustrial.co", industry: "Manufacturing", region: "North America", employeeCount: 260 },
|
|
7
|
-
{ companyName: "Meridian Finance", domain: "meridianfinance.ai", industry: "Financial Services", region: "Europe", employeeCount: 140 }
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
export const SAMPLE_CONTACTS = [
|
|
11
|
-
{ contactName: "Avery Chen", title: "VP Sales" },
|
|
12
|
-
{ contactName: "Jordan Patel", title: "Head of Revenue Operations" },
|
|
13
|
-
{ contactName: "Taylor Morgan", title: "Director of Growth" },
|
|
14
|
-
{ contactName: "Morgan Diaz", title: "Chief Revenue Officer" },
|
|
15
|
-
{ contactName: "Cameron Lee", title: "Sales Operations Manager" },
|
|
16
|
-
{ contactName: "Riley Brooks", title: "Demand Generation Lead" }
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
export const SAMPLE_SIGNALS = [
|
|
20
|
-
"hiring sales reps",
|
|
21
|
-
"recent funding",
|
|
22
|
-
"expanding into new regions",
|
|
23
|
-
"using fragmented sales tooling",
|
|
24
|
-
"growing outbound team",
|
|
25
|
-
"launching new product line"
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
export const SAMPLE_TECH = [
|
|
29
|
-
"HubSpot",
|
|
30
|
-
"Salesforce",
|
|
31
|
-
"Apollo",
|
|
32
|
-
"Instantly",
|
|
33
|
-
"Outreach",
|
|
34
|
-
"Clay",
|
|
35
|
-
"Segment",
|
|
36
|
-
"PostHog"
|
|
37
|
-
];
|
package/tests/cli.test.ts
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
4
|
-
import os from "node:os";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { execFile } from "node:child_process";
|
|
7
|
-
import { promisify } from "node:util";
|
|
8
|
-
|
|
9
|
-
const execFileAsync = promisify(execFile);
|
|
10
|
-
const projectRoot = path.resolve(import.meta.dirname, "..", "..");
|
|
11
|
-
const cliPath = path.join(projectRoot, "dist", "cli.js");
|
|
12
|
-
|
|
13
|
-
async function runCli(args: string[]): Promise<unknown> {
|
|
14
|
-
const { stdout } = await execFileAsync("node", [cliPath, ...args], {
|
|
15
|
-
cwd: projectRoot
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return JSON.parse(stdout);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
test("CLI help renders the expected command surface", async () => {
|
|
22
|
-
const { stdout } = await execFileAsync("node", [cliPath, "--help"], {
|
|
23
|
-
cwd: projectRoot
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
assert.match(stdout, /icp:define/);
|
|
27
|
-
assert.match(stdout, /leads:generate/);
|
|
28
|
-
assert.match(stdout, /sync:outreach/);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("CLI workflow generates, enriches, scores, and syncs leads", async () => {
|
|
32
|
-
const tempDir = await mkdtemp(path.join(os.tmpdir(), "salesprompter-cli-"));
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
const icpPath = path.join(tempDir, "icp.json");
|
|
36
|
-
const leadsPath = path.join(tempDir, "leads.json");
|
|
37
|
-
const enrichedPath = path.join(tempDir, "enriched.json");
|
|
38
|
-
const scoredPath = path.join(tempDir, "scored.json");
|
|
39
|
-
|
|
40
|
-
const icpResult = await runCli([
|
|
41
|
-
"icp:define",
|
|
42
|
-
"--name",
|
|
43
|
-
"EU SaaS RevOps",
|
|
44
|
-
"--industries",
|
|
45
|
-
"Software,Financial Services",
|
|
46
|
-
"--company-sizes",
|
|
47
|
-
"50-199,200-499",
|
|
48
|
-
"--regions",
|
|
49
|
-
"Europe",
|
|
50
|
-
"--titles",
|
|
51
|
-
"Head of Revenue Operations,VP Sales",
|
|
52
|
-
"--required-signals",
|
|
53
|
-
"recent funding,growing outbound team",
|
|
54
|
-
"--out",
|
|
55
|
-
icpPath
|
|
56
|
-
]);
|
|
57
|
-
|
|
58
|
-
assert.equal((icpResult as { status: string }).status, "ok");
|
|
59
|
-
|
|
60
|
-
const generateResult = await runCli([
|
|
61
|
-
"leads:generate",
|
|
62
|
-
"--icp",
|
|
63
|
-
icpPath,
|
|
64
|
-
"--count",
|
|
65
|
-
"4",
|
|
66
|
-
"--out",
|
|
67
|
-
leadsPath
|
|
68
|
-
]);
|
|
69
|
-
|
|
70
|
-
assert.equal((generateResult as { generated: number }).generated, 4);
|
|
71
|
-
|
|
72
|
-
const enrichResult = await runCli([
|
|
73
|
-
"leads:enrich",
|
|
74
|
-
"--in",
|
|
75
|
-
leadsPath,
|
|
76
|
-
"--out",
|
|
77
|
-
enrichedPath
|
|
78
|
-
]);
|
|
79
|
-
|
|
80
|
-
assert.equal((enrichResult as { enriched: number }).enriched, 4);
|
|
81
|
-
|
|
82
|
-
const scoreResult = await runCli([
|
|
83
|
-
"leads:score",
|
|
84
|
-
"--icp",
|
|
85
|
-
icpPath,
|
|
86
|
-
"--in",
|
|
87
|
-
enrichedPath,
|
|
88
|
-
"--out",
|
|
89
|
-
scoredPath
|
|
90
|
-
]);
|
|
91
|
-
|
|
92
|
-
assert.equal((scoreResult as { scored: number }).scored, 4);
|
|
93
|
-
|
|
94
|
-
const crmSyncResult = await runCli([
|
|
95
|
-
"sync:crm",
|
|
96
|
-
"--target",
|
|
97
|
-
"hubspot",
|
|
98
|
-
"--in",
|
|
99
|
-
scoredPath
|
|
100
|
-
]);
|
|
101
|
-
|
|
102
|
-
assert.deepEqual(crmSyncResult, {
|
|
103
|
-
status: "ok",
|
|
104
|
-
target: "hubspot",
|
|
105
|
-
synced: 4,
|
|
106
|
-
dryRun: true
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const scoredLeads = JSON.parse(await readFile(scoredPath, "utf8")) as Array<{
|
|
110
|
-
score: number;
|
|
111
|
-
grade: string;
|
|
112
|
-
rationale: string[];
|
|
113
|
-
crmFit: string;
|
|
114
|
-
outreachFit: string;
|
|
115
|
-
}>;
|
|
116
|
-
|
|
117
|
-
assert.equal(scoredLeads.length, 4);
|
|
118
|
-
assert.ok(scoredLeads.every((lead) => lead.score >= 0 && lead.score <= 100));
|
|
119
|
-
assert.ok(scoredLeads.every((lead) => ["A", "B", "C", "D"].includes(lead.grade)));
|
|
120
|
-
assert.ok(scoredLeads.some((lead) => lead.rationale.length > 0));
|
|
121
|
-
assert.ok(scoredLeads.some((lead) => lead.crmFit === "high" || lead.outreachFit === "high"));
|
|
122
|
-
} finally {
|
|
123
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test("CLI can target a specific company domain like deel.com", async () => {
|
|
128
|
-
const tempDir = await mkdtemp(path.join(os.tmpdir(), "salesprompter-cli-target-"));
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
const icpPath = path.join(tempDir, "icp.json");
|
|
132
|
-
const leadsPath = path.join(tempDir, "deel-leads.json");
|
|
133
|
-
|
|
134
|
-
await runCli([
|
|
135
|
-
"icp:define",
|
|
136
|
-
"--name",
|
|
137
|
-
"Global HR Tech",
|
|
138
|
-
"--industries",
|
|
139
|
-
"Software",
|
|
140
|
-
"--regions",
|
|
141
|
-
"Global",
|
|
142
|
-
"--titles",
|
|
143
|
-
"VP Sales,Head of Revenue Operations",
|
|
144
|
-
"--out",
|
|
145
|
-
icpPath
|
|
146
|
-
]);
|
|
147
|
-
|
|
148
|
-
const generateResult = await runCli([
|
|
149
|
-
"leads:generate",
|
|
150
|
-
"--icp",
|
|
151
|
-
icpPath,
|
|
152
|
-
"--count",
|
|
153
|
-
"3",
|
|
154
|
-
"--company-domain",
|
|
155
|
-
"deel.com",
|
|
156
|
-
"--company-name",
|
|
157
|
-
"Deel",
|
|
158
|
-
"--out",
|
|
159
|
-
leadsPath
|
|
160
|
-
]);
|
|
161
|
-
|
|
162
|
-
assert.deepEqual(generateResult, {
|
|
163
|
-
status: "ok",
|
|
164
|
-
generated: 3,
|
|
165
|
-
out: leadsPath,
|
|
166
|
-
target: "deel.com"
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
const leads = JSON.parse(await readFile(leadsPath, "utf8")) as Array<{
|
|
170
|
-
companyName: string;
|
|
171
|
-
domain: string;
|
|
172
|
-
email: string;
|
|
173
|
-
source: string;
|
|
174
|
-
}>;
|
|
175
|
-
|
|
176
|
-
assert.equal(leads.length, 3);
|
|
177
|
-
assert.ok(leads.every((lead) => lead.companyName === "Deel"));
|
|
178
|
-
assert.ok(leads.every((lead) => lead.domain === "deel.com"));
|
|
179
|
-
assert.ok(leads.every((lead) => lead.email.endsWith("@deel.com")));
|
|
180
|
-
assert.ok(leads.every((lead) => lead.source === "heuristic-target-account"));
|
|
181
|
-
} finally {
|
|
182
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
183
|
-
}
|
|
184
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"lib": ["ES2022"],
|
|
7
|
-
"strict": true,
|
|
8
|
-
"rootDir": "src",
|
|
9
|
-
"outDir": "dist",
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"resolveJsonModule": true,
|
|
13
|
-
"forceConsistentCasingInFileNames": true
|
|
14
|
-
},
|
|
15
|
-
"include": ["src/**/*.ts"]
|
|
16
|
-
}
|