@diffbot/typescript 0.1.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/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/index.cjs +691 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +163 -0
- package/dist/index.d.ts +163 -0
- package/dist/index.js +642 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
var VERSION = "0.1.0";
|
|
3
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
4
|
+
var EXTRACT_BASE = "https://api.diffbot.com/v3";
|
|
5
|
+
var CRAWL_BASE = "https://api.diffbot.com/v3/crawl";
|
|
6
|
+
var DIFFBOT_LLM_BASE = "https://llm.diffbot.com/rag/v1/chat/completions";
|
|
7
|
+
var WEB_SEARCH_BASE = "https://llm.diffbot.com/api/v1/web_search";
|
|
8
|
+
var NLP_BASE = "https://nl.diffbot.com/v1/";
|
|
9
|
+
var KG_DQL_ENDPOINT = "https://kg.diffbot.com/kg/v3/dql";
|
|
10
|
+
var KG_ONTOLOGY_ENDPOINT = "https://kg.diffbot.com/kg/ontology";
|
|
11
|
+
var TOKEN_ENV_VAR = "DIFFBOT_API_TOKEN";
|
|
12
|
+
|
|
13
|
+
// src/errors.ts
|
|
14
|
+
var DiffbotError = class extends Error {
|
|
15
|
+
name = "DiffbotError";
|
|
16
|
+
};
|
|
17
|
+
var ValidationError = class extends DiffbotError {
|
|
18
|
+
name = "ValidationError";
|
|
19
|
+
};
|
|
20
|
+
var APIError = class extends DiffbotError {
|
|
21
|
+
statusCode;
|
|
22
|
+
body;
|
|
23
|
+
apiMessage;
|
|
24
|
+
requestId;
|
|
25
|
+
constructor(statusCode, body) {
|
|
26
|
+
let apiMessage;
|
|
27
|
+
let requestId;
|
|
28
|
+
try {
|
|
29
|
+
const data = JSON.parse(body);
|
|
30
|
+
apiMessage = data.message;
|
|
31
|
+
requestId = data.requestId;
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
const display = apiMessage ?? (body.length > 200 ? `${body.slice(0, 200)}...` : body);
|
|
35
|
+
super(`Diffbot API error ${statusCode}: ${display}`);
|
|
36
|
+
this.name = "APIError";
|
|
37
|
+
this.statusCode = statusCode;
|
|
38
|
+
this.body = body;
|
|
39
|
+
this.apiMessage = apiMessage;
|
|
40
|
+
this.requestId = requestId;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var AuthError = class extends APIError {
|
|
44
|
+
name = "AuthError";
|
|
45
|
+
};
|
|
46
|
+
var RateLimitError = class extends APIError {
|
|
47
|
+
retryAfter;
|
|
48
|
+
name = "RateLimitError";
|
|
49
|
+
constructor(statusCode, body, retryAfter) {
|
|
50
|
+
super(statusCode, body);
|
|
51
|
+
this.retryAfter = retryAfter ?? void 0;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var ExtractionError = class extends DiffbotError {
|
|
55
|
+
errorCode;
|
|
56
|
+
error;
|
|
57
|
+
name = "ExtractionError";
|
|
58
|
+
constructor(errorCode, error) {
|
|
59
|
+
super(`Diffbot extraction error ${errorCode}: ${error}`);
|
|
60
|
+
this.errorCode = errorCode;
|
|
61
|
+
this.error = error;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/http.ts
|
|
66
|
+
var HttpClient = class {
|
|
67
|
+
constructor(fetchFn, timeoutMs, userAgent) {
|
|
68
|
+
this.fetchFn = fetchFn;
|
|
69
|
+
this.timeoutMs = timeoutMs;
|
|
70
|
+
this.userAgent = userAgent;
|
|
71
|
+
}
|
|
72
|
+
fetchFn;
|
|
73
|
+
timeoutMs;
|
|
74
|
+
userAgent;
|
|
75
|
+
closed = false;
|
|
76
|
+
async get(url, options = {}) {
|
|
77
|
+
return this.request(url, { ...options, method: "GET" });
|
|
78
|
+
}
|
|
79
|
+
async post(url, options = {}) {
|
|
80
|
+
return this.request(url, { ...options, method: "POST" });
|
|
81
|
+
}
|
|
82
|
+
async request(url, options = {}) {
|
|
83
|
+
if (this.closed) {
|
|
84
|
+
throw new Error("HTTP client is closed");
|
|
85
|
+
}
|
|
86
|
+
const parsed = new URL(url);
|
|
87
|
+
if (options.params) {
|
|
88
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
89
|
+
if (value !== void 0) {
|
|
90
|
+
parsed.searchParams.set(key, String(value));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const headers = new Headers(options.headers);
|
|
95
|
+
headers.set("User-Agent", this.userAgent);
|
|
96
|
+
if (options.json !== void 0) {
|
|
97
|
+
headers.set("Content-Type", "application/json");
|
|
98
|
+
}
|
|
99
|
+
const init = {
|
|
100
|
+
method: options.method ?? "GET",
|
|
101
|
+
headers,
|
|
102
|
+
redirect: options.redirect,
|
|
103
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
104
|
+
};
|
|
105
|
+
if (options.json !== void 0) {
|
|
106
|
+
init.body = JSON.stringify(options.json);
|
|
107
|
+
}
|
|
108
|
+
return this.fetchFn(parsed.toString(), init);
|
|
109
|
+
}
|
|
110
|
+
async close() {
|
|
111
|
+
this.closed = true;
|
|
112
|
+
}
|
|
113
|
+
get isClosed() {
|
|
114
|
+
return this.closed;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
function raiseForStatus(response, bodyText) {
|
|
118
|
+
if (response.ok) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const status = response.status;
|
|
122
|
+
const body = bodyText ?? "";
|
|
123
|
+
if (status === 401 || status === 403) {
|
|
124
|
+
throw new AuthError(status, body);
|
|
125
|
+
}
|
|
126
|
+
if (status === 429) {
|
|
127
|
+
throw new RateLimitError(status, body, response.headers.get("retry-after"));
|
|
128
|
+
}
|
|
129
|
+
throw new APIError(status, body);
|
|
130
|
+
}
|
|
131
|
+
async function readBodyText(response) {
|
|
132
|
+
return response.text();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/client.ts
|
|
136
|
+
var DiffbotClient = class {
|
|
137
|
+
token;
|
|
138
|
+
analyzeUrl;
|
|
139
|
+
llmUrl;
|
|
140
|
+
crawlerUrl;
|
|
141
|
+
webSearchUrl;
|
|
142
|
+
nlpUrl;
|
|
143
|
+
http;
|
|
144
|
+
constructor(options) {
|
|
145
|
+
if (!options.token) {
|
|
146
|
+
throw new ValidationError("token is required");
|
|
147
|
+
}
|
|
148
|
+
this.token = options.token;
|
|
149
|
+
this.analyzeUrl = options.analyzeUrl ?? EXTRACT_BASE;
|
|
150
|
+
this.llmUrl = options.llmUrl ?? DIFFBOT_LLM_BASE;
|
|
151
|
+
this.crawlerUrl = options.crawlerUrl ?? CRAWL_BASE;
|
|
152
|
+
this.webSearchUrl = options.webSearchUrl ?? WEB_SEARCH_BASE;
|
|
153
|
+
this.nlpUrl = options.nlpUrl ?? NLP_BASE;
|
|
154
|
+
this.http = new HttpClient(
|
|
155
|
+
options.fetch ?? fetch,
|
|
156
|
+
options.timeout ?? DEFAULT_TIMEOUT,
|
|
157
|
+
`diffbot-typescript/${VERSION}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
async close() {
|
|
161
|
+
await this.http.close();
|
|
162
|
+
}
|
|
163
|
+
async [Symbol.asyncDispose]() {
|
|
164
|
+
await this.close();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// src/auth.ts
|
|
169
|
+
import { existsSync, readFileSync } from "fs";
|
|
170
|
+
import { homedir } from "os";
|
|
171
|
+
import { join } from "path";
|
|
172
|
+
var authConfig = {
|
|
173
|
+
credentialsPath: join(homedir(), ".diffbot", "credentials")
|
|
174
|
+
};
|
|
175
|
+
function readCredentialsFile(path = authConfig.credentialsPath) {
|
|
176
|
+
if (!existsSync(path)) {
|
|
177
|
+
return "";
|
|
178
|
+
}
|
|
179
|
+
const text = readFileSync(path, "utf8");
|
|
180
|
+
for (const line of text.split("\n")) {
|
|
181
|
+
const trimmed = line.trim();
|
|
182
|
+
if (trimmed.startsWith(`${TOKEN_ENV_VAR}=`)) {
|
|
183
|
+
return trimmed.slice(TOKEN_ENV_VAR.length + 1).trim();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return "";
|
|
187
|
+
}
|
|
188
|
+
function resolveToken(token) {
|
|
189
|
+
if (token?.trim()) {
|
|
190
|
+
return token.trim();
|
|
191
|
+
}
|
|
192
|
+
const envToken = process.env[TOKEN_ENV_VAR]?.trim();
|
|
193
|
+
if (envToken) {
|
|
194
|
+
return envToken;
|
|
195
|
+
}
|
|
196
|
+
return readCredentialsFile();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/extract.ts
|
|
200
|
+
function buildParams(client, url, fmt) {
|
|
201
|
+
const params = {
|
|
202
|
+
token: client.token,
|
|
203
|
+
url,
|
|
204
|
+
timeout: 3e4
|
|
205
|
+
};
|
|
206
|
+
if (fmt === "markdown") {
|
|
207
|
+
params.mode = "llm";
|
|
208
|
+
}
|
|
209
|
+
return params;
|
|
210
|
+
}
|
|
211
|
+
function parseResponse(data) {
|
|
212
|
+
if ("errorCode" in data) {
|
|
213
|
+
throw new ExtractionError(
|
|
214
|
+
Number(data.errorCode),
|
|
215
|
+
String(data.error ?? "")
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return data;
|
|
219
|
+
}
|
|
220
|
+
function normalizeUrl(url) {
|
|
221
|
+
return url.startsWith("http") ? url : `https://${url}`;
|
|
222
|
+
}
|
|
223
|
+
async function extract(client, url, api = "analyze", fmt = "markdown") {
|
|
224
|
+
const normalized = normalizeUrl(url);
|
|
225
|
+
const response = await client.http.get(`${client.analyzeUrl}/${api}`, {
|
|
226
|
+
params: buildParams(client, normalized, fmt)
|
|
227
|
+
});
|
|
228
|
+
const body = await readBodyText(response);
|
|
229
|
+
raiseForStatus(response, body);
|
|
230
|
+
return parseResponse(JSON.parse(body));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/ask.ts
|
|
234
|
+
function parseChunk(line) {
|
|
235
|
+
try {
|
|
236
|
+
const chunk = JSON.parse(line.replace(/^data: /, ""));
|
|
237
|
+
const content = chunk.choices?.[0]?.delta?.content;
|
|
238
|
+
return content ?? void 0;
|
|
239
|
+
} catch {
|
|
240
|
+
return void 0;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function* linesFromText(text) {
|
|
244
|
+
for (const line of text.split("\n")) {
|
|
245
|
+
if (line) yield line;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async function* linesFromStream(body) {
|
|
249
|
+
const reader = body.getReader();
|
|
250
|
+
const decoder = new TextDecoder();
|
|
251
|
+
let buffer = "";
|
|
252
|
+
while (true) {
|
|
253
|
+
const { done, value } = await reader.read();
|
|
254
|
+
if (done) break;
|
|
255
|
+
buffer += decoder.decode(value, { stream: true });
|
|
256
|
+
const parts = buffer.split("\n");
|
|
257
|
+
buffer = parts.pop() ?? "";
|
|
258
|
+
for (const line of parts) {
|
|
259
|
+
if (line) yield line;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (buffer) yield buffer;
|
|
263
|
+
}
|
|
264
|
+
async function* ask(client, messages) {
|
|
265
|
+
const headers = { Authorization: `Bearer ${client.token}` };
|
|
266
|
+
const payload = { model: "diffbot-small-xl", messages, stream: true };
|
|
267
|
+
const response = await client.http.post(client.llmUrl, { headers, json: payload });
|
|
268
|
+
if (!response.ok) {
|
|
269
|
+
const body = await readBodyText(response);
|
|
270
|
+
raiseForStatus(response, body);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (response.body) {
|
|
274
|
+
for await (const line of linesFromStream(response.body)) {
|
|
275
|
+
const content = parseChunk(line);
|
|
276
|
+
if (content) yield content;
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const text = await response.text();
|
|
281
|
+
for (const line of linesFromText(text)) {
|
|
282
|
+
const content = parseChunk(line);
|
|
283
|
+
if (content) yield content;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/crawl.ts
|
|
288
|
+
var CrawlEventType = /* @__PURE__ */ ((CrawlEventType2) => {
|
|
289
|
+
CrawlEventType2["JobCreated"] = "job_created";
|
|
290
|
+
CrawlEventType2["UrlProcessed"] = "url_processed";
|
|
291
|
+
return CrawlEventType2;
|
|
292
|
+
})(CrawlEventType || {});
|
|
293
|
+
function buildCrawlParams(client, jobName, site, options) {
|
|
294
|
+
const params = {
|
|
295
|
+
token: client.token,
|
|
296
|
+
name: jobName,
|
|
297
|
+
seeds: site,
|
|
298
|
+
maxToCrawl: options.maxToCrawl,
|
|
299
|
+
maxToProcess: options.maxToProcess,
|
|
300
|
+
maxHops: options.hops,
|
|
301
|
+
restrictDomain: options.restrictDomain ? 1 : 0
|
|
302
|
+
};
|
|
303
|
+
if (options.apiUrl) params.apiUrl = options.apiUrl;
|
|
304
|
+
if (options.crawlDelay > 0) params.crawlDelay = options.crawlDelay;
|
|
305
|
+
if (options.urlCrawlPattern) params.urlCrawlPattern = options.urlCrawlPattern;
|
|
306
|
+
if (options.urlProcessPattern) params.urlProcessPattern = options.urlProcessPattern;
|
|
307
|
+
if (options.obeyRobots) params.obeyRobots = 1;
|
|
308
|
+
if (options.useProxies) params.useProxies = 1;
|
|
309
|
+
if (options.customHeaders) params.customHeaders = options.customHeaders;
|
|
310
|
+
return params;
|
|
311
|
+
}
|
|
312
|
+
function parseUrlCsv(text, seenUrls) {
|
|
313
|
+
const events = [];
|
|
314
|
+
const lines = text.trim().split("\n");
|
|
315
|
+
if (lines.length < 2) return events;
|
|
316
|
+
const headers = lines[0].split(",").map((h) => h.trim());
|
|
317
|
+
const urlIdx = headers.indexOf("Url");
|
|
318
|
+
const timeIdx = headers.indexOf("Crawled Time");
|
|
319
|
+
const statusIdx = headers.indexOf("Crawl Status");
|
|
320
|
+
for (const line of lines.slice(1)) {
|
|
321
|
+
const cols = line.split(",");
|
|
322
|
+
const url = cols[urlIdx]?.replace(/^"|"$/g, "").trim() ?? "";
|
|
323
|
+
if (url && !seenUrls.has(url)) {
|
|
324
|
+
seenUrls.add(url);
|
|
325
|
+
events.push({
|
|
326
|
+
eventType: "url_processed" /* UrlProcessed */,
|
|
327
|
+
timestamp: cols[timeIdx]?.trim() ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
328
|
+
details: {
|
|
329
|
+
url,
|
|
330
|
+
status: cols[statusIdx]?.trim() ?? "unknown"
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return events;
|
|
336
|
+
}
|
|
337
|
+
function sleep(ms) {
|
|
338
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
339
|
+
}
|
|
340
|
+
async function* crawl(client, site, options = {}) {
|
|
341
|
+
let normalizedSite = site.startsWith("http") ? site : `https://${site}`;
|
|
342
|
+
const jobName = options.jobName ?? `crawl-${Math.floor(Date.now() / 1e3)}`;
|
|
343
|
+
const params = buildCrawlParams(client, jobName, normalizedSite, {
|
|
344
|
+
hops: options.hops ?? 2,
|
|
345
|
+
maxToCrawl: options.maxToCrawl ?? 100,
|
|
346
|
+
maxToProcess: options.maxToProcess ?? 100,
|
|
347
|
+
restrictDomain: options.restrictDomain ?? true,
|
|
348
|
+
apiUrl: options.apiUrl ?? "",
|
|
349
|
+
crawlDelay: options.crawlDelay ?? -1,
|
|
350
|
+
urlCrawlPattern: options.urlCrawlPattern,
|
|
351
|
+
urlProcessPattern: options.urlProcessPattern,
|
|
352
|
+
obeyRobots: options.obeyRobots ?? false,
|
|
353
|
+
useProxies: options.useProxies ?? false,
|
|
354
|
+
customHeaders: options.customHeaders
|
|
355
|
+
});
|
|
356
|
+
const response = await client.http.get(client.crawlerUrl, { params });
|
|
357
|
+
const body = await readBodyText(response);
|
|
358
|
+
raiseForStatus(response, body);
|
|
359
|
+
yield {
|
|
360
|
+
eventType: "job_created" /* JobCreated */,
|
|
361
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
362
|
+
details: { job_name: jobName }
|
|
363
|
+
};
|
|
364
|
+
if (!options.watch) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
368
|
+
const pollInterval = options.pollInterval ?? 2e3;
|
|
369
|
+
while (true) {
|
|
370
|
+
const statusResponse = await client.http.get(client.crawlerUrl, {
|
|
371
|
+
params: { token: client.token, name: jobName }
|
|
372
|
+
});
|
|
373
|
+
const statusBody = await readBodyText(statusResponse);
|
|
374
|
+
raiseForStatus(statusResponse, statusBody);
|
|
375
|
+
const jobs = JSON.parse(statusBody).jobs;
|
|
376
|
+
if (!jobs?.length) break;
|
|
377
|
+
const jobStatus = jobs[0].jobStatus ?? {};
|
|
378
|
+
const statusCode = Number(jobStatus.status ?? 0);
|
|
379
|
+
const urlsResponse = await client.http.get(`${client.crawlerUrl}/data`, {
|
|
380
|
+
params: { token: client.token, name: jobName, type: "urls" },
|
|
381
|
+
redirect: "follow"
|
|
382
|
+
});
|
|
383
|
+
const urlsBody = await readBodyText(urlsResponse);
|
|
384
|
+
raiseForStatus(urlsResponse, urlsBody);
|
|
385
|
+
if (urlsBody) {
|
|
386
|
+
for (const event of parseUrlCsv(urlsBody, seenUrls)) {
|
|
387
|
+
yield event;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (statusCode !== 0 && statusCode !== 7) {
|
|
391
|
+
const message = String(jobStatus.message ?? "");
|
|
392
|
+
if (/fail|error/i.test(message)) {
|
|
393
|
+
throw new APIError(500, `Crawler job failed: ${message}`);
|
|
394
|
+
}
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
await sleep(pollInterval);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
async function crawlListJobs(client) {
|
|
401
|
+
const response = await client.http.get(client.crawlerUrl, {
|
|
402
|
+
params: { token: client.token }
|
|
403
|
+
});
|
|
404
|
+
const body = await readBodyText(response);
|
|
405
|
+
raiseForStatus(response, body);
|
|
406
|
+
return JSON.parse(body).jobs ?? [];
|
|
407
|
+
}
|
|
408
|
+
async function crawlGetJob(client, jobName) {
|
|
409
|
+
const response = await client.http.get(client.crawlerUrl, {
|
|
410
|
+
params: { token: client.token, name: jobName }
|
|
411
|
+
});
|
|
412
|
+
const body = await readBodyText(response);
|
|
413
|
+
raiseForStatus(response, body);
|
|
414
|
+
const jobs = JSON.parse(body).jobs;
|
|
415
|
+
return jobs?.[0] ?? {};
|
|
416
|
+
}
|
|
417
|
+
async function crawlDeleteJob(client, jobName) {
|
|
418
|
+
const response = await client.http.get(client.crawlerUrl, {
|
|
419
|
+
params: { token: client.token, name: jobName, delete: 1 }
|
|
420
|
+
});
|
|
421
|
+
const body = await readBodyText(response);
|
|
422
|
+
raiseForStatus(response, body);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// src/kg.ts
|
|
426
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
427
|
+
import { dirname } from "path";
|
|
428
|
+
|
|
429
|
+
// src/ontology.ts
|
|
430
|
+
import { readFile } from "fs/promises";
|
|
431
|
+
var Ontology = class _Ontology {
|
|
432
|
+
data;
|
|
433
|
+
constructor(data) {
|
|
434
|
+
this.data = data;
|
|
435
|
+
}
|
|
436
|
+
static fromJson(raw) {
|
|
437
|
+
const text = typeof raw === "string" ? raw : new TextDecoder().decode(raw);
|
|
438
|
+
return new _Ontology(JSON.parse(text));
|
|
439
|
+
}
|
|
440
|
+
static async fromPath(path) {
|
|
441
|
+
const text = await readFile(path, "utf8");
|
|
442
|
+
return _Ontology.fromJson(text);
|
|
443
|
+
}
|
|
444
|
+
types() {
|
|
445
|
+
const types = this.data.types;
|
|
446
|
+
return Object.keys(types ?? {}).sort();
|
|
447
|
+
}
|
|
448
|
+
composites() {
|
|
449
|
+
const composites = this.data.composites;
|
|
450
|
+
return Object.keys(composites ?? {}).sort();
|
|
451
|
+
}
|
|
452
|
+
enums() {
|
|
453
|
+
const enums = this.data.enums;
|
|
454
|
+
return Object.keys(enums ?? {}).sort();
|
|
455
|
+
}
|
|
456
|
+
taxonomies() {
|
|
457
|
+
const taxonomies = this.data.taxonomies;
|
|
458
|
+
return Object.keys(taxonomies ?? {}).sort();
|
|
459
|
+
}
|
|
460
|
+
static fieldsOf(container, typeName) {
|
|
461
|
+
const entry = container[typeName];
|
|
462
|
+
if (!entry) throw new Error(`Unknown name: ${typeName}`);
|
|
463
|
+
return entry.fields ?? {};
|
|
464
|
+
}
|
|
465
|
+
fieldsFor(typeName) {
|
|
466
|
+
const types = this.data.types ?? {};
|
|
467
|
+
const composites = this.data.composites ?? {};
|
|
468
|
+
if (typeName in types) return _Ontology.fieldsOf(types, typeName);
|
|
469
|
+
if (typeName in composites) return _Ontology.fieldsOf(composites, typeName);
|
|
470
|
+
throw new Error(`${typeName} is not a known entity type or composite`);
|
|
471
|
+
}
|
|
472
|
+
static filterFields(fields, search, includeDeprecated = false) {
|
|
473
|
+
const pattern = search ? new RegExp(search, "i") : null;
|
|
474
|
+
const out = [];
|
|
475
|
+
for (const [name, meta] of Object.entries(fields)) {
|
|
476
|
+
if (!includeDeprecated && meta.isDeprecated) continue;
|
|
477
|
+
if (pattern && !pattern.test(name)) continue;
|
|
478
|
+
out.push([name, meta]);
|
|
479
|
+
}
|
|
480
|
+
return out;
|
|
481
|
+
}
|
|
482
|
+
taxonomyValues(name, search) {
|
|
483
|
+
const taxonomies = this.data.taxonomies ?? {};
|
|
484
|
+
const tax = taxonomies[name];
|
|
485
|
+
if (!tax) throw new Error(`Unknown taxonomy: ${name}`);
|
|
486
|
+
const pattern = search ? new RegExp(search, "i") : null;
|
|
487
|
+
const out = [];
|
|
488
|
+
const walk = (node) => {
|
|
489
|
+
const n = node.name;
|
|
490
|
+
if (typeof n === "string" && (!pattern || pattern.test(n))) {
|
|
491
|
+
out.push(n);
|
|
492
|
+
}
|
|
493
|
+
for (const child of node.children ?? []) {
|
|
494
|
+
walk(child);
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
for (const cat of tax.categories ?? []) {
|
|
498
|
+
walk(cat);
|
|
499
|
+
}
|
|
500
|
+
return out;
|
|
501
|
+
}
|
|
502
|
+
enumValues(name) {
|
|
503
|
+
const enums = this.data.enums ?? {};
|
|
504
|
+
const enumEntry = enums[name];
|
|
505
|
+
if (!enumEntry) throw new Error(`Unknown enum: ${name}`);
|
|
506
|
+
return [...enumEntry.values ?? []];
|
|
507
|
+
}
|
|
508
|
+
findNamed(search) {
|
|
509
|
+
const pattern = new RegExp(search, "i");
|
|
510
|
+
const found = /* @__PURE__ */ new Set();
|
|
511
|
+
const walk = (node) => {
|
|
512
|
+
if (Array.isArray(node)) {
|
|
513
|
+
for (const v of node) walk(v);
|
|
514
|
+
} else if (node && typeof node === "object") {
|
|
515
|
+
const obj = node;
|
|
516
|
+
const n = obj.name;
|
|
517
|
+
if (typeof n === "string" && pattern.test(n)) found.add(n);
|
|
518
|
+
for (const v of Object.values(obj)) walk(v);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
walk(this.data);
|
|
522
|
+
return [...found].sort();
|
|
523
|
+
}
|
|
524
|
+
static formatField(name, meta) {
|
|
525
|
+
let typeName = String(meta.type ?? "?");
|
|
526
|
+
if (typeName === "LinkedEntity") {
|
|
527
|
+
const le = meta.leType;
|
|
528
|
+
if (le?.length) typeName = `LinkedEntity (${le[0]})`;
|
|
529
|
+
}
|
|
530
|
+
const flags = [];
|
|
531
|
+
if (meta.isList) flags.push("isList");
|
|
532
|
+
if (meta.isComposite) flags.push("isComposite");
|
|
533
|
+
if (meta.isEnum) flags.push("isEnum");
|
|
534
|
+
if (meta.isDeprecated) flags.push("DEPRECATED");
|
|
535
|
+
const suffix = flags.map((f) => ` [${f}]`).join("");
|
|
536
|
+
return `${name}: [${typeName}]${suffix}`;
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
// src/kg.ts
|
|
541
|
+
function buildDqlParams(client, query, options) {
|
|
542
|
+
const params = {
|
|
543
|
+
token: client.token,
|
|
544
|
+
query,
|
|
545
|
+
size: options.size ?? 10
|
|
546
|
+
};
|
|
547
|
+
if (options.from) params.from = options.from;
|
|
548
|
+
if (options.format && options.format !== "json") params.format = options.format;
|
|
549
|
+
if (options.filter !== void 0) params.filter = options.filter;
|
|
550
|
+
if (options.exportspec !== void 0) params.exportspec = options.exportspec;
|
|
551
|
+
if (options.extra) Object.assign(params, options.extra);
|
|
552
|
+
return params;
|
|
553
|
+
}
|
|
554
|
+
async function dql(client, query, options = {}) {
|
|
555
|
+
const params = buildDqlParams(client, query, options);
|
|
556
|
+
const response = await client.http.get(KG_DQL_ENDPOINT, { params });
|
|
557
|
+
const body = await readBodyText(response);
|
|
558
|
+
raiseForStatus(response, body);
|
|
559
|
+
if (options.raw) {
|
|
560
|
+
return new TextEncoder().encode(body);
|
|
561
|
+
}
|
|
562
|
+
return JSON.parse(body);
|
|
563
|
+
}
|
|
564
|
+
async function dqlParallel(client, queries, workers = 8) {
|
|
565
|
+
if (!queries.length) return [];
|
|
566
|
+
const limit = Math.min(workers, queries.length);
|
|
567
|
+
const results = new Array(queries.length);
|
|
568
|
+
let index = 0;
|
|
569
|
+
async function worker() {
|
|
570
|
+
while (true) {
|
|
571
|
+
const current = index++;
|
|
572
|
+
if (current >= queries.length) return;
|
|
573
|
+
const { query, ...opts } = queries[current];
|
|
574
|
+
results[current] = await dql(client, query, opts);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
await Promise.all(Array.from({ length: limit }, () => worker()));
|
|
578
|
+
return results;
|
|
579
|
+
}
|
|
580
|
+
async function dqlRefreshOntology(client, dest) {
|
|
581
|
+
const response = await client.http.get(KG_ONTOLOGY_ENDPOINT);
|
|
582
|
+
const body = await readBodyText(response);
|
|
583
|
+
raiseForStatus(response, body);
|
|
584
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
585
|
+
await writeFile(dest, body);
|
|
586
|
+
}
|
|
587
|
+
async function dqlFetchOntology(client) {
|
|
588
|
+
const response = await client.http.get(KG_ONTOLOGY_ENDPOINT);
|
|
589
|
+
const body = await readBodyText(response);
|
|
590
|
+
raiseForStatus(response, body);
|
|
591
|
+
return Ontology.fromJson(body);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// src/nlp.ts
|
|
595
|
+
var NLP_FIELDS = "entities,sentiment";
|
|
596
|
+
async function entities(client, text, lang = "auto") {
|
|
597
|
+
const params = { token: client.token, fields: NLP_FIELDS };
|
|
598
|
+
const payload = [{ lang, format: "plain text", content: text }];
|
|
599
|
+
const response = await client.http.post(client.nlpUrl, { params, json: payload });
|
|
600
|
+
const body = await readBodyText(response);
|
|
601
|
+
raiseForStatus(response, body);
|
|
602
|
+
const data = JSON.parse(body);
|
|
603
|
+
return Array.isArray(data) ? data[0] ?? {} : data;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// src/web-search.ts
|
|
607
|
+
async function webSearch(client, text, options = {}) {
|
|
608
|
+
const headers = { Authorization: `Bearer ${client.token}` };
|
|
609
|
+
const params = { text };
|
|
610
|
+
if (options.numResults !== void 0) params.size = options.numResults;
|
|
611
|
+
if (options.maxTokens !== void 0) params.maxTokens = options.maxTokens;
|
|
612
|
+
const response = await client.http.get(client.webSearchUrl, { headers, params });
|
|
613
|
+
const body = await readBodyText(response);
|
|
614
|
+
raiseForStatus(response, body);
|
|
615
|
+
return JSON.parse(body);
|
|
616
|
+
}
|
|
617
|
+
export {
|
|
618
|
+
APIError,
|
|
619
|
+
AuthError,
|
|
620
|
+
CrawlEventType,
|
|
621
|
+
DiffbotClient,
|
|
622
|
+
DiffbotError,
|
|
623
|
+
ExtractionError,
|
|
624
|
+
Ontology,
|
|
625
|
+
RateLimitError,
|
|
626
|
+
VERSION,
|
|
627
|
+
ValidationError,
|
|
628
|
+
ask,
|
|
629
|
+
crawl,
|
|
630
|
+
crawlDeleteJob,
|
|
631
|
+
crawlGetJob,
|
|
632
|
+
crawlListJobs,
|
|
633
|
+
dql,
|
|
634
|
+
dqlFetchOntology,
|
|
635
|
+
dqlParallel,
|
|
636
|
+
dqlRefreshOntology,
|
|
637
|
+
entities,
|
|
638
|
+
extract,
|
|
639
|
+
resolveToken,
|
|
640
|
+
webSearch
|
|
641
|
+
};
|
|
642
|
+
//# sourceMappingURL=index.js.map
|