open-local-audit 0.67.0 → 0.69.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.
- package/CHANGELOG.md +17 -0
- package/README.md +15 -1
- package/dist/cli.js +86 -5
- package/dist/cli.js.map +1 -1
- package/dist/discovery-cache.d.ts +24 -0
- package/dist/discovery-cache.js +312 -0
- package/dist/discovery-cache.js.map +1 -0
- package/dist/discovery-location.d.ts +13 -0
- package/dist/discovery-location.js +99 -0
- package/dist/discovery-location.js.map +1 -0
- package/dist/discovery-runner.d.ts +30 -0
- package/dist/discovery-runner.js +82 -7
- package/dist/discovery-runner.js.map +1 -1
- package/dist/discovery.d.ts +15 -3
- package/dist/discovery.js +56 -12
- package/dist/discovery.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/overture.d.ts +17 -0
- package/dist/overture.js +340 -0
- package/dist/overture.js.map +1 -0
- package/dist/schema.d.ts +28 -4
- package/dist/schema.js +9 -1
- package/dist/schema.js.map +1 -1
- package/dist/start.d.ts +11 -0
- package/dist/start.js +234 -0
- package/dist/start.js.map +1 -0
- package/dist/website-enrichment.d.ts +21 -0
- package/dist/website-enrichment.js +400 -0
- package/dist/website-enrichment.js.map +1 -0
- package/dist/workflow-config.d.ts +48 -3
- package/dist/workflow-config.js +17 -1
- package/dist/workflow-config.js.map +1 -1
- package/dist/workflow-plan.d.ts +1 -1
- package/dist/workflow-plan.js +4 -1
- package/dist/workflow-plan.js.map +1 -1
- package/dist/workflow-preflight.d.ts +1 -1
- package/dist/workflow-preflight.js +2 -2
- package/dist/workflow-preflight.js.map +1 -1
- package/dist/workflow.js +2 -1
- package/dist/workflow.js.map +1 -1
- package/docs/architecture/workflow-command.md +1 -1
- package/docs/guides/free-discovery.md +123 -0
- package/package.json +10 -4
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { lstat, mkdir, open } from "node:fs/promises";
|
|
4
|
+
import { isIP } from "node:net";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { isAbsolute, join, parse, relative, resolve, sep } from "node:path";
|
|
7
|
+
const cacheSchemaVersion = 1;
|
|
8
|
+
const cacheFreshnessMs = 7 * 24 * 60 * 60 * 1000;
|
|
9
|
+
const maximumCacheBytes = 2 * 1024 * 1024;
|
|
10
|
+
const minimumConfidence = 0.5;
|
|
11
|
+
const socialProfileHosts = ["facebook.com", "instagram.com", "linkedin.com", "x.com", "twitter.com", "tiktok.com", "youtube.com", "wa.me", "whatsapp.com"];
|
|
12
|
+
const shortLinkHosts = ["fbf.bz", "fb.me", "bit.ly", "tinyurl.com", "t.co", "goo.gl", "maps.app.goo.gl", "linktr.ee"];
|
|
13
|
+
export function defaultDiscoveryCacheDirectory() {
|
|
14
|
+
if (process.platform === "win32") {
|
|
15
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
16
|
+
return join(localAppData && isAbsolute(localAppData) ? localAppData : join(homedir(), "AppData", "Local"), "open-local-audit", "cache", "discovery");
|
|
17
|
+
}
|
|
18
|
+
if (process.platform === "darwin") {
|
|
19
|
+
return join(homedir(), "Library", "Caches", "open-local-audit", "discovery");
|
|
20
|
+
}
|
|
21
|
+
const xdgCacheHome = process.env.XDG_CACHE_HOME;
|
|
22
|
+
return join(xdgCacheHome && isAbsolute(xdgCacheHome) ? xdgCacheHome : join(homedir(), ".cache"), "open-local-audit", "discovery");
|
|
23
|
+
}
|
|
24
|
+
export function discoveryCacheKey(input) {
|
|
25
|
+
return createHash("sha256")
|
|
26
|
+
.update(JSON.stringify({ schemaVersion: cacheSchemaVersion, category: input.category, bbox: input.bbox, limit: input.limit, release: input.release }))
|
|
27
|
+
.digest("hex");
|
|
28
|
+
}
|
|
29
|
+
function cacheFilePath(input) {
|
|
30
|
+
return join(input.cacheDir, `${discoveryCacheKey(input)}.json`);
|
|
31
|
+
}
|
|
32
|
+
function cacheError(error) {
|
|
33
|
+
return error instanceof Error && error.message ? error.message : "Cache storage was unavailable";
|
|
34
|
+
}
|
|
35
|
+
function isPublicHttpUrl(value) {
|
|
36
|
+
try {
|
|
37
|
+
const url = new URL(value);
|
|
38
|
+
const host = url.hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
39
|
+
if ((url.protocol !== "http:" && url.protocol !== "https:") || url.username || url.password || host === "localhost" || host.endsWith(".localhost")) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (isIP(host) === 4) {
|
|
43
|
+
const octets = host.split(".").map(Number);
|
|
44
|
+
return !(octets[0] === 0 || octets[0] === 10 || octets[0] === 127 || (octets[0] === 169 && octets[1] === 254) || (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) || (octets[0] === 192 && octets[1] === 168));
|
|
45
|
+
}
|
|
46
|
+
if (isIP(host) === 6) {
|
|
47
|
+
const mappedIpv4 = host.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i)?.[1];
|
|
48
|
+
return !(host === "::1" || host.startsWith("fc") || host.startsWith("fd") || host.startsWith("fe80:") || (mappedIpv4 !== undefined && !isPublicHttpUrl(`http://${mappedIpv4}`)));
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function hasHost(value, hosts) {
|
|
57
|
+
try {
|
|
58
|
+
const host = new URL(value).hostname.toLowerCase();
|
|
59
|
+
return hosts.some((entry) => host === entry || host.endsWith(`.${entry}`));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function isSafeWebsite(value) {
|
|
66
|
+
return typeof value === "string" && isPublicHttpUrl(value) && !hasHost(value, socialProfileHosts) && !hasHost(value, shortLinkHosts);
|
|
67
|
+
}
|
|
68
|
+
function isPastIsoDate(value, now) {
|
|
69
|
+
const time = typeof value === "string" ? Date.parse(value) : Number.NaN;
|
|
70
|
+
return Number.isFinite(time) && time <= now;
|
|
71
|
+
}
|
|
72
|
+
function isStringList(value, predicate) {
|
|
73
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string" && predicate(entry));
|
|
74
|
+
}
|
|
75
|
+
function isOptionalString(value) {
|
|
76
|
+
return value === undefined || typeof value === "string";
|
|
77
|
+
}
|
|
78
|
+
function isFiniteCoordinate(value, minimum, maximum) {
|
|
79
|
+
return value === undefined || (typeof value === "number" && Number.isFinite(value) && value >= minimum && value <= maximum);
|
|
80
|
+
}
|
|
81
|
+
function isStringRecord(value, allowedKeys) {
|
|
82
|
+
return typeof value === "object"
|
|
83
|
+
&& value !== null
|
|
84
|
+
&& !Array.isArray(value)
|
|
85
|
+
&& Object.keys(value).every((key) => allowedKeys.includes(key) && typeof value[key] === "string");
|
|
86
|
+
}
|
|
87
|
+
function isSafeCandidate(value, request, now, allowProfile = false) {
|
|
88
|
+
if (typeof value !== "object" || value === null)
|
|
89
|
+
return false;
|
|
90
|
+
const candidate = value;
|
|
91
|
+
if (candidate.source !== "overture" || typeof candidate.sourceId !== "string" || !candidate.sourceId.trim() || candidate.segment !== request.category)
|
|
92
|
+
return false;
|
|
93
|
+
if (candidate.label !== undefined && typeof candidate.label !== "string")
|
|
94
|
+
return false;
|
|
95
|
+
if (candidate.websiteUri !== undefined && !isSafeWebsite(candidate.websiteUri))
|
|
96
|
+
return false;
|
|
97
|
+
if (!Object.keys(candidate).every((key) => ["source", "sourceId", "label", "segment", "websiteUri", "sourceMetadata", ...(allowProfile ? ["profile"] : [])].includes(key)))
|
|
98
|
+
return false;
|
|
99
|
+
if (candidate.profile !== undefined && !["generic", "dental", "beauty", "restaurant", "contractor", "lawyer", "clinic", "gym", "hotel", "auto-service"].includes(candidate.profile))
|
|
100
|
+
return false;
|
|
101
|
+
if (typeof candidate.sourceMetadata !== "object" || candidate.sourceMetadata === null || Array.isArray(candidate.sourceMetadata))
|
|
102
|
+
return false;
|
|
103
|
+
const metadata = candidate.sourceMetadata;
|
|
104
|
+
if (metadata.datasetRelease !== request.release || !isPastIsoDate(metadata.retrievedAt, now))
|
|
105
|
+
return false;
|
|
106
|
+
if (!Object.keys(metadata).every((key) => ["address", "country", "locality", "region", "latitude", "longitude", "phones", "emails", "socials", "confidence", "operatingStatus", "datasetRelease", "sourceUrl", "sourceUrls", "sourceProvenance", "retrievedAt"].includes(key)))
|
|
107
|
+
return false;
|
|
108
|
+
if (![metadata.address, metadata.country, metadata.locality, metadata.region, metadata.operatingStatus].every(isOptionalString))
|
|
109
|
+
return false;
|
|
110
|
+
if (!isFiniteCoordinate(metadata.latitude, -90, 90) || !isFiniteCoordinate(metadata.longitude, -180, 180))
|
|
111
|
+
return false;
|
|
112
|
+
if (!isStringList(metadata.phones, () => true) || !isStringList(metadata.emails, () => true) || !isStringList(metadata.socials, isPublicHttpUrl))
|
|
113
|
+
return false;
|
|
114
|
+
if (metadata.confidence !== undefined && (typeof metadata.confidence !== "number" || !Number.isFinite(metadata.confidence) || metadata.confidence < minimumConfidence || metadata.confidence > 1))
|
|
115
|
+
return false;
|
|
116
|
+
if (typeof metadata.operatingStatus === "string" && metadata.operatingStatus.toLowerCase() === "closed_permanently")
|
|
117
|
+
return false;
|
|
118
|
+
if (typeof metadata.sourceUrl !== "string" || !isPublicHttpUrl(metadata.sourceUrl))
|
|
119
|
+
return false;
|
|
120
|
+
if (!isStringList(metadata.sourceUrls, isPublicHttpUrl))
|
|
121
|
+
return false;
|
|
122
|
+
if (!Array.isArray(metadata.sourceProvenance) || !metadata.sourceProvenance.every((entry) => isStringRecord(entry, ["provider", "dataset", "license", "recordId", "resource", "version", "updateTime"])))
|
|
123
|
+
return false;
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
function isSafeRecord(value, request, now) {
|
|
127
|
+
if (typeof value !== "object" || value === null)
|
|
128
|
+
return false;
|
|
129
|
+
const record = value;
|
|
130
|
+
if (record.schemaVersion !== cacheSchemaVersion || record.source !== "overture" || record.category !== request.category || record.limit !== request.limit || record.release !== request.release)
|
|
131
|
+
return false;
|
|
132
|
+
if (!Array.isArray(record.bbox) || record.bbox.length !== 4 || record.bbox.some((entry, index) => entry !== request.bbox[index]))
|
|
133
|
+
return false;
|
|
134
|
+
if (!isPastIsoDate(record.fetchedAt, now) || !isPastIsoDate(record.cachedAt, now) || Date.parse(record.cachedAt) < Date.parse(record.fetchedAt) || now - Date.parse(record.cachedAt) > cacheFreshnessMs)
|
|
135
|
+
return false;
|
|
136
|
+
if (!Array.isArray(record.candidates) || record.candidates.length > request.limit || !record.candidates.every((candidate) => isSafeCandidate(candidate, request, now)))
|
|
137
|
+
return false;
|
|
138
|
+
return new Set(record.candidates.map((candidate) => candidate.sourceId)).size === record.candidates.length;
|
|
139
|
+
}
|
|
140
|
+
async function ensureSafeDirectory(cacheDir) {
|
|
141
|
+
if (!isAbsolute(cacheDir))
|
|
142
|
+
throw new Error("Discovery cache directory must be absolute");
|
|
143
|
+
const normalized = resolve(cacheDir);
|
|
144
|
+
const parsed = parse(normalized);
|
|
145
|
+
const parts = relative(parsed.root, normalized).split(sep).filter(Boolean);
|
|
146
|
+
let current = parsed.root;
|
|
147
|
+
for (const part of parts) {
|
|
148
|
+
current = join(current, part);
|
|
149
|
+
try {
|
|
150
|
+
const entry = await lstat(current);
|
|
151
|
+
if (!entry.isDirectory() || entry.isSymbolicLink())
|
|
152
|
+
throw new Error("Discovery cache path is not a safe directory");
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
if (error.code !== "ENOENT")
|
|
156
|
+
throw error;
|
|
157
|
+
await mkdir(current);
|
|
158
|
+
const created = await lstat(current);
|
|
159
|
+
if (!created.isDirectory() || created.isSymbolicLink())
|
|
160
|
+
throw new Error("Discovery cache path is not a safe directory");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async function safeCacheFile(path) {
|
|
165
|
+
try {
|
|
166
|
+
const entry = await lstat(path);
|
|
167
|
+
if (!entry.isFile() || entry.isSymbolicLink() || entry.nlink !== 1)
|
|
168
|
+
return { state: "unsafe" };
|
|
169
|
+
return { state: "file", stat: entry };
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
if (error.code === "ENOENT")
|
|
173
|
+
return { state: "missing" };
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function noFollow(flags) {
|
|
178
|
+
return process.platform === "win32" ? flags : flags | constants.O_NOFOLLOW;
|
|
179
|
+
}
|
|
180
|
+
function matchesCacheFile(expected, actual) {
|
|
181
|
+
return actual.isFile() && actual.nlink === 1 && actual.dev === expected.dev && actual.ino === expected.ino;
|
|
182
|
+
}
|
|
183
|
+
async function openExistingCacheFile(path, expected, flags) {
|
|
184
|
+
const handle = await open(path, noFollow(flags));
|
|
185
|
+
try {
|
|
186
|
+
const actual = await handle.stat();
|
|
187
|
+
if (!matchesCacheFile(expected, actual))
|
|
188
|
+
throw new Error("Discovery cache entry changed while opening");
|
|
189
|
+
return handle;
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
await handle.close();
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async function openNewCacheFile(path) {
|
|
197
|
+
const handle = await open(path, noFollow(constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL));
|
|
198
|
+
try {
|
|
199
|
+
const actual = await handle.stat();
|
|
200
|
+
if (!actual.isFile() || actual.nlink !== 1)
|
|
201
|
+
throw new Error("Discovery cache entry was unsafe");
|
|
202
|
+
return handle;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
await handle.close();
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
export async function readDiscoveryCache(request) {
|
|
210
|
+
try {
|
|
211
|
+
await ensureSafeDirectory(request.cacheDir);
|
|
212
|
+
const path = cacheFilePath(request);
|
|
213
|
+
const file = await safeCacheFile(path);
|
|
214
|
+
if (file.state === "missing")
|
|
215
|
+
return { info: { status: "miss", release: request.release } };
|
|
216
|
+
if (file.state !== "file" || file.stat.size > maximumCacheBytes)
|
|
217
|
+
return { info: { status: "unavailable", release: request.release, message: "Discovery cache entry was unsafe or oversized" } };
|
|
218
|
+
const handle = await openExistingCacheFile(path, file.stat, constants.O_RDONLY);
|
|
219
|
+
let content;
|
|
220
|
+
try {
|
|
221
|
+
await ensureSafeDirectory(request.cacheDir);
|
|
222
|
+
if (!matchesCacheFile(file.stat, await handle.stat()))
|
|
223
|
+
throw new Error("Discovery cache entry changed while reading");
|
|
224
|
+
const buffer = Buffer.alloc(maximumCacheBytes + 1);
|
|
225
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
226
|
+
if (bytesRead > maximumCacheBytes) {
|
|
227
|
+
return { info: { status: "unavailable", release: request.release, message: "Discovery cache record exceeded its size limit" } };
|
|
228
|
+
}
|
|
229
|
+
content = buffer.subarray(0, bytesRead).toString("utf8");
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
await handle.close();
|
|
233
|
+
}
|
|
234
|
+
if (Buffer.byteLength(content, "utf8") > maximumCacheBytes) {
|
|
235
|
+
return { info: { status: "unavailable", release: request.release, message: "Discovery cache record exceeded its size limit" } };
|
|
236
|
+
}
|
|
237
|
+
const record = JSON.parse(content);
|
|
238
|
+
const now = Date.now();
|
|
239
|
+
if (!isSafeRecord(record, request, now)) {
|
|
240
|
+
return { info: { status: "unavailable", release: request.release, message: "Discovery cache record was invalid or expired" } };
|
|
241
|
+
}
|
|
242
|
+
return { candidates: record.candidates, info: { status: "hit", release: request.release, fetchedAt: record.fetchedAt, cachedAt: record.cachedAt } };
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
return { info: { status: "unavailable", release: request.release, message: cacheError(error) } };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
export async function writeDiscoveryCache(request, candidates, fetchedAt, status) {
|
|
249
|
+
try {
|
|
250
|
+
const now = Date.now();
|
|
251
|
+
if (!isPastIsoDate(fetchedAt, now) || !candidates.every((candidate) => isSafeCandidate(candidate, request, now, true))) {
|
|
252
|
+
return { status: "unavailable", release: request.release, message: "Discovery results did not satisfy the cache record contract" };
|
|
253
|
+
}
|
|
254
|
+
await ensureSafeDirectory(request.cacheDir);
|
|
255
|
+
const path = cacheFilePath(request);
|
|
256
|
+
const existing = await safeCacheFile(path);
|
|
257
|
+
if (existing.state === "unsafe")
|
|
258
|
+
return { status: "unavailable", release: request.release, message: "Discovery cache entry was unsafe" };
|
|
259
|
+
const cachedAt = new Date().toISOString();
|
|
260
|
+
const sourceCandidates = candidates.map(({ profile: _profile, ...candidate }) => candidate);
|
|
261
|
+
const record = {
|
|
262
|
+
schemaVersion: cacheSchemaVersion,
|
|
263
|
+
source: "overture",
|
|
264
|
+
category: request.category,
|
|
265
|
+
bbox: request.bbox,
|
|
266
|
+
limit: request.limit,
|
|
267
|
+
release: request.release,
|
|
268
|
+
fetchedAt,
|
|
269
|
+
cachedAt,
|
|
270
|
+
candidates: sourceCandidates
|
|
271
|
+
};
|
|
272
|
+
const content = JSON.stringify(record);
|
|
273
|
+
if (Buffer.byteLength(content, "utf8") > maximumCacheBytes)
|
|
274
|
+
return { status: "unavailable", release: request.release, message: "Discovery cache record exceeded its size limit" };
|
|
275
|
+
let handle;
|
|
276
|
+
let expected;
|
|
277
|
+
if (existing.state === "file") {
|
|
278
|
+
expected = existing.stat;
|
|
279
|
+
handle = await openExistingCacheFile(path, existing.stat, constants.O_WRONLY);
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
try {
|
|
283
|
+
handle = await openNewCacheFile(path);
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
if (error.code !== "EEXIST")
|
|
287
|
+
throw error;
|
|
288
|
+
const createdByPeer = await safeCacheFile(path);
|
|
289
|
+
if (createdByPeer.state !== "file")
|
|
290
|
+
throw new Error("Discovery cache entry was unsafe");
|
|
291
|
+
expected = createdByPeer.stat;
|
|
292
|
+
handle = await openExistingCacheFile(path, createdByPeer.stat, constants.O_WRONLY);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
await ensureSafeDirectory(request.cacheDir);
|
|
297
|
+
const current = await handle.stat();
|
|
298
|
+
if (expected ? !matchesCacheFile(expected, current) : !current.isFile() || current.nlink !== 1)
|
|
299
|
+
throw new Error("Discovery cache entry changed while writing");
|
|
300
|
+
await handle.truncate(0);
|
|
301
|
+
await handle.writeFile(content, "utf8");
|
|
302
|
+
}
|
|
303
|
+
finally {
|
|
304
|
+
await handle.close();
|
|
305
|
+
}
|
|
306
|
+
return { status, release: request.release, fetchedAt, cachedAt };
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
return { status: "unavailable", release: request.release, message: cacheError(error) };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=discovery-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery-cache.js","sourceRoot":"","sources":["../src/discovery-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BoundingBox } from "./overture.js";
|
|
2
|
+
export declare const maximumDiscoveryBboxSpan = 5;
|
|
3
|
+
export interface DiscoveryCity {
|
|
4
|
+
name: string;
|
|
5
|
+
country: string;
|
|
6
|
+
region: string;
|
|
7
|
+
latitude: number;
|
|
8
|
+
longitude: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function findDiscoveryCity(data: string, name: string, country: string): DiscoveryCity;
|
|
11
|
+
export declare function parseDiscoveryBbox(value: string): BoundingBox;
|
|
12
|
+
export declare function cityBoundingBox(city: DiscoveryCity, radiusKm?: number): BoundingBox;
|
|
13
|
+
export declare function resolveDiscoveryCity(name: string, country: string, cacheDir: string): Promise<DiscoveryCity>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { strFromU8, unzipSync } from "fflate";
|
|
4
|
+
export const maximumDiscoveryBboxSpan = 5;
|
|
5
|
+
function normalizedName(value) {
|
|
6
|
+
return value.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase().replace(/ı/g, "i").trim();
|
|
7
|
+
}
|
|
8
|
+
export function findDiscoveryCity(data, name, country) {
|
|
9
|
+
if (!name.trim() || !/^[A-Z]{2}$/i.test(country)) {
|
|
10
|
+
throw new Error("City and two-letter country code are required, for example --city Istanbul --country TR");
|
|
11
|
+
}
|
|
12
|
+
const query = normalizedName(name);
|
|
13
|
+
const matches = [];
|
|
14
|
+
for (const line of data.split("\n")) {
|
|
15
|
+
const cells = line.split("\t");
|
|
16
|
+
if (cells[8] !== country.toUpperCase())
|
|
17
|
+
continue;
|
|
18
|
+
if (![cells[1], cells[2], ...(cells[3] ?? "").split(",")].some((value) => normalizedName(value ?? "") === query))
|
|
19
|
+
continue;
|
|
20
|
+
const latitude = Number(cells[4]);
|
|
21
|
+
const longitude = Number(cells[5]);
|
|
22
|
+
if (!Number.isFinite(latitude) || !Number.isFinite(longitude))
|
|
23
|
+
continue;
|
|
24
|
+
matches.push({ name: cells[1], country: cells[8], region: cells[10], latitude, longitude });
|
|
25
|
+
}
|
|
26
|
+
if (!matches.length)
|
|
27
|
+
throw new Error(`City not found in GeoNames cities15000: ${name}, ${country}. Use --bbox for smaller places.`);
|
|
28
|
+
if (matches.length > 1)
|
|
29
|
+
throw new Error(`City is ambiguous: ${name}, ${country}. Use --bbox to select the intended area.`);
|
|
30
|
+
return matches[0];
|
|
31
|
+
}
|
|
32
|
+
export function parseDiscoveryBbox(value) {
|
|
33
|
+
const parts = value.split(",");
|
|
34
|
+
const values = parts.map(Number);
|
|
35
|
+
if (parts.length !== 4 || parts.some((part) => !part.trim()) || values.some((entry) => !Number.isFinite(entry))) {
|
|
36
|
+
throw new Error("--bbox must be west,south,east,north in decimal degrees");
|
|
37
|
+
}
|
|
38
|
+
const [west, south, east, north] = values;
|
|
39
|
+
if (west < -180 || east > 180 || south < -90 || north > 90 || west >= east || south >= north) {
|
|
40
|
+
throw new Error("Invalid --bbox: use increasing longitude/latitude bounds within world coordinates");
|
|
41
|
+
}
|
|
42
|
+
if (east - west > maximumDiscoveryBboxSpan || north - south > maximumDiscoveryBboxSpan) {
|
|
43
|
+
throw new Error(`Search bounds must span at most ${maximumDiscoveryBboxSpan} degrees on each axis`);
|
|
44
|
+
}
|
|
45
|
+
return [west, south, east, north];
|
|
46
|
+
}
|
|
47
|
+
export function cityBoundingBox(city, radiusKm = 10) {
|
|
48
|
+
if (!Number.isFinite(radiusKm) || radiusKm <= 0 || radiusKm > 50)
|
|
49
|
+
throw new Error("--radius-km must be greater than 0 and at most 50");
|
|
50
|
+
const dy = radiusKm / 111.32;
|
|
51
|
+
const dx = dy / Math.cos(city.latitude * Math.PI / 180);
|
|
52
|
+
return parseDiscoveryBbox([Math.max(-180, city.longitude - dx), Math.max(-90, city.latitude - dy), Math.min(180, city.longitude + dx), Math.min(90, city.latitude + dy)].join(","));
|
|
53
|
+
}
|
|
54
|
+
export async function resolveDiscoveryCity(name, country, cacheDir) {
|
|
55
|
+
const path = join(cacheDir, "geonames-cities15000.txt");
|
|
56
|
+
let data;
|
|
57
|
+
try {
|
|
58
|
+
const info = await stat(path);
|
|
59
|
+
if (Date.now() - info.mtimeMs < 30 * 24 * 60 * 60 * 1000 && info.size < 40 * 1024 * 1024)
|
|
60
|
+
data = await readFile(path, "utf8");
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT"))
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
if (data === undefined) {
|
|
67
|
+
const response = await fetch("https://download.geonames.org/export/dump/cities15000.zip", { signal: AbortSignal.timeout(60000) });
|
|
68
|
+
if (!response.ok || !response.body)
|
|
69
|
+
throw new Error(`GeoNames city download failed: HTTP ${response.status}`);
|
|
70
|
+
const reader = response.body.getReader();
|
|
71
|
+
const chunks = [];
|
|
72
|
+
let bytes = 0;
|
|
73
|
+
try {
|
|
74
|
+
for (;;) {
|
|
75
|
+
const { value, done } = await reader.read();
|
|
76
|
+
if (done)
|
|
77
|
+
break;
|
|
78
|
+
bytes += value.length;
|
|
79
|
+
if (bytes > 20 * 1024 * 1024)
|
|
80
|
+
throw new Error("GeoNames download exceeds 20 MB limit");
|
|
81
|
+
chunks.push(value);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
await reader.cancel();
|
|
86
|
+
}
|
|
87
|
+
const files = unzipSync(Buffer.concat(chunks), { filter: (file) => file.name === "cities15000.txt" && file.originalSize < 40 * 1024 * 1024 });
|
|
88
|
+
if (!files["cities15000.txt"])
|
|
89
|
+
throw new Error("GeoNames archive has no bounded cities15000.txt dataset");
|
|
90
|
+
data = strFromU8(files["cities15000.txt"]);
|
|
91
|
+
// Validate before retaining downloaded content for subsequent runs.
|
|
92
|
+
const city = findDiscoveryCity(data, name, country);
|
|
93
|
+
await mkdir(cacheDir, { recursive: true });
|
|
94
|
+
await writeFile(path, data, "utf8");
|
|
95
|
+
return city;
|
|
96
|
+
}
|
|
97
|
+
return findDiscoveryCity(data, name, country);
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=discovery-location.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery-location.js","sourceRoot":"","sources":["../src/discovery-location.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type DiscoveryProviderName, type DiscoverySummary, type ProspectCsvExportPreset, type ProspectExportRow } from "./discovery.js";
|
|
2
2
|
import type { AuditProfile, ReportBrandConfig } from "./types.js";
|
|
3
|
+
import type { DiscoveryCacheInfo } from "./discovery-cache.js";
|
|
3
4
|
export interface DiscoveryRunOptions {
|
|
4
5
|
provider: DiscoveryProviderName;
|
|
5
6
|
query?: string;
|
|
@@ -18,11 +19,40 @@ export interface DiscoveryRunOptions {
|
|
|
18
19
|
minOpportunityScore?: number;
|
|
19
20
|
concurrency: number;
|
|
20
21
|
apiKey?: string;
|
|
22
|
+
city?: string;
|
|
23
|
+
country?: string;
|
|
24
|
+
bbox?: string;
|
|
25
|
+
radiusKm?: number;
|
|
26
|
+
release?: string;
|
|
27
|
+
cacheDir?: string;
|
|
28
|
+
refreshCache?: boolean;
|
|
21
29
|
managedOutputRoot?: string;
|
|
22
30
|
brand?: ReportBrandConfig;
|
|
23
31
|
}
|
|
24
32
|
export interface DiscoveryRunResult {
|
|
25
33
|
rows: ProspectExportRow[];
|
|
26
34
|
summary: DiscoverySummary;
|
|
35
|
+
metrics?: {
|
|
36
|
+
discoveryMs: number;
|
|
37
|
+
auditMs: number;
|
|
38
|
+
totalMs: number;
|
|
39
|
+
cache?: DiscoveryCacheInfo;
|
|
40
|
+
sourceCoverage?: {
|
|
41
|
+
denominator: number;
|
|
42
|
+
website: number;
|
|
43
|
+
phone: number;
|
|
44
|
+
email: number;
|
|
45
|
+
address: number;
|
|
46
|
+
};
|
|
47
|
+
websites: Array<{
|
|
48
|
+
url: string;
|
|
49
|
+
status: string;
|
|
50
|
+
durationMs: number;
|
|
51
|
+
pagesFetched: number;
|
|
52
|
+
sourceUrls: string[];
|
|
53
|
+
error?: string;
|
|
54
|
+
warnings?: string[];
|
|
55
|
+
}>;
|
|
56
|
+
};
|
|
27
57
|
}
|
|
28
58
|
export declare function runDiscovery(options: DiscoveryRunOptions): Promise<DiscoveryRunResult>;
|