@opencraw/driver 0.0.0-stage → 0.2.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.
@@ -0,0 +1,459 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/main.ts
4
+ import { readFileSync } from "node:fs";
5
+ import { dirname, join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ // src/server/serve-driver.use-case.ts
9
+ import { once } from "node:events";
10
+
11
+ // src/protocol/driver-message.contract.ts
12
+ import { calloutRequestSchema, calloutResponseSchema } from "@opencraw/core";
13
+ import { z } from "zod";
14
+ var PROTOCOL_VERSION = "1.1.0";
15
+ var recipeObject = z.record(z.string(), z.unknown());
16
+ var coreObject = z.record(z.string(), z.unknown());
17
+ var accessFields = {
18
+ accessConfig: z.string().optional(),
19
+ access: z.string().optional(),
20
+ /** Access plugins the client serves: a profile `{ "kind": "plugin", "name": … }` naming one leases through a `callout` to the client. */
21
+ accessPlugins: z.array(z.string()).optional()
22
+ };
23
+ var recipeSource = {
24
+ paths: z.array(z.string()).min(1).optional(),
25
+ recipes: z.array(recipeObject).min(1).optional()
26
+ };
27
+ var initializeParamsSchema = z.strictObject({
28
+ clientName: z.string().min(1),
29
+ clientVersion: z.string().min(1),
30
+ protocolVersion: z.string().regex(/^\d+\.\d+\.\d+$/)
31
+ });
32
+ var initializeResultSchema = z.strictObject({
33
+ driverVersion: z.string(),
34
+ protocolVersion: z.string(),
35
+ methods: z.array(z.string())
36
+ });
37
+ var validateParamsSchema = z.strictObject({ ...recipeSource, only: z.array(z.string()).optional() });
38
+ var issueSchema = z.strictObject({ source: z.string().optional(), recipeId: z.string().optional(), path: z.string(), message: z.string() });
39
+ var validateResultSchema = z.strictObject({
40
+ ok: z.boolean(),
41
+ /** The input recipes that would run, when ok. */
42
+ inputs: z.array(z.string()),
43
+ /** Every hook the recipes call, which a `run` must serve. */
44
+ hooks: z.array(z.string()),
45
+ issues: z.array(issueSchema)
46
+ });
47
+ var runParamsSchema = z.strictObject({
48
+ ...recipeSource,
49
+ only: z.array(z.string()).optional(),
50
+ /** A file to write the records to, its extension naming the format; without it, each record is sent as a `record` notification. */
51
+ out: z.string().optional(),
52
+ append: z.boolean().optional(),
53
+ resume: z.boolean().optional(),
54
+ dryRun: z.boolean().optional(),
55
+ parallel: z.number().int().min(1).max(16).optional(),
56
+ browser: z.strictObject({ headless: z.boolean().optional(), executablePath: z.string().optional(), ignoreHTTPSErrors: z.boolean().optional() }).optional(),
57
+ /** The hooks the client answers itself: each becomes a `callout` request to the client when a recipe calls it. */
58
+ hooks: z.array(z.string()).optional(),
59
+ /** Captcha solvers the client serves, by name: a challenge goes to the client as a `captcha` callout. */
60
+ captchaSolvers: z.array(z.string()).optional(),
61
+ ...accessFields
62
+ });
63
+ var runResultSchema = z.strictObject({ runId: z.string() });
64
+ var cancelParamsSchema = z.strictObject({ runId: z.string() });
65
+ var cancelResultSchema = z.strictObject({ cancelled: z.boolean() });
66
+ var probeParamsSchema = z.strictObject({
67
+ url: z.string().min(1),
68
+ /** Render the page in Chromium first (for pages built by scripts). */
69
+ browser: z.boolean().optional(),
70
+ browserPath: z.string().optional(),
71
+ insecureTls: z.boolean().optional(),
72
+ userAgent: z.string().optional(),
73
+ ...accessFields
74
+ });
75
+ var diffParamsSchema = z.strictObject({
76
+ /** The earlier run's JSON Lines file. */
77
+ previous: z.string().min(1),
78
+ current: z.string().min(1),
79
+ /** The fields that identify a record; default: each line's `_key`. */
80
+ key: z.array(z.string()).optional(),
81
+ /** Fields left out of the comparison. */
82
+ ignore: z.array(z.string()).optional(),
83
+ /** Changes returned at most (default 200); the counts cover all of them. */
84
+ limit: z.number().int().min(0).optional()
85
+ });
86
+ var diffResultSchema = z.strictObject({
87
+ added: z.number(),
88
+ removed: z.number(),
89
+ changed: z.number(),
90
+ unchanged: z.number(),
91
+ /** Set when the current run lost most of the previous run's records. */
92
+ shrunk: z.strictObject({ previous: z.number(), current: z.number() }).optional(),
93
+ /** The report a person reads, one line per change. */
94
+ summary: z.array(z.string()),
95
+ /** The changes, up to `limit`: each `added`, `removed` or `changed` (with each field's before and after). */
96
+ changes: z.array(coreObject),
97
+ /** More changes than `limit` were found. */
98
+ truncated: z.boolean()
99
+ });
100
+ var exportParamsSchema = z.strictObject({
101
+ /** A run's JSON Lines file. */
102
+ records: z.string().min(1),
103
+ /** The output recipe that gives the columns. */
104
+ output: z.string().min(1),
105
+ /** The file to write; its extension names the format, as for `run`. */
106
+ out: z.string().min(1)
107
+ });
108
+ var exportResultSchema = z.strictObject({ written: z.number(), location: z.string() });
109
+ var shutdownResultSchema = z.strictObject({});
110
+ var eventParamsSchema = z.strictObject({ runId: z.string(), event: z.record(z.string(), z.unknown()) });
111
+ var recordParamsSchema = z.strictObject({ runId: z.string(), recipeId: z.string(), key: z.string().nullable(), data: z.record(z.string(), z.unknown()) });
112
+ var finishedParamsSchema = z.strictObject({
113
+ runId: z.string(),
114
+ /** The crawl report (`CrawlReport`), absent when the run could not start. */
115
+ report: z.record(z.string(), z.unknown()).optional(),
116
+ /** Why the run could not start, or stopped as a whole. */
117
+ error: z.string().optional()
118
+ });
119
+ var calloutParamsSchema = z.strictObject({ runId: z.string(), request: calloutRequestSchema });
120
+
121
+ // src/protocol/driver-json-schema.mapper.ts
122
+ import { z as z2 } from "zod";
123
+
124
+ // src/record-files/diff-record-files.use-case.ts
125
+ import { diffReport } from "@opencraw/cli";
126
+ import { diffRecords, readRecordsFile } from "@opencraw/core";
127
+ var CHANGE_LIMIT = 200;
128
+ async function diffRecordFiles(params) {
129
+ const [previous, current] = await Promise.all([readRecordsFile(params.previous), readRecordsFile(params.current)]);
130
+ const diff = diffRecords(previous, current, { key: params.key, ignore: params.ignore });
131
+ const limit = params.limit ?? CHANGE_LIMIT;
132
+ return {
133
+ added: diff.added,
134
+ removed: diff.removed,
135
+ changed: diff.changed,
136
+ unchanged: diff.unchanged,
137
+ ...diff.shrunk !== void 0 && { shrunk: diff.shrunk },
138
+ summary: diffReport(diff),
139
+ changes: diff.changes.slice(0, limit).map((change) => ({ ...change })),
140
+ truncated: diff.changes.length > limit
141
+ };
142
+ }
143
+
144
+ // src/record-files/export-record-files.use-case.ts
145
+ import { readFile } from "node:fs/promises";
146
+ import { fileSink, outputRecordOf, parseOutputRecipe, readRecordsFile as readRecordsFile2 } from "@opencraw/core";
147
+ async function exportRecordFiles(params) {
148
+ const output = parseOutputRecipe(JSON.parse(await readFile(params.output, "utf8")), params.output);
149
+ const stored = await readRecordsFile2(params.records);
150
+ const sink = fileSink(params.out, { browser: { executablePath: process.env.OPENCRAW_CHROMIUM } });
151
+ await sink.open(output);
152
+ for (const line of stored) await sink.write(outputRecordOf(line));
153
+ const summary = await sink.close();
154
+ return { written: summary.written, location: summary.location ?? params.out };
155
+ }
156
+
157
+ // src/session/run-session.use-case.ts
158
+ import { createCrawler, fileSink as fileSink2, RecipeSet } from "@opencraw/core";
159
+
160
+ // src/callout-bridge/client-hooks.client.ts
161
+ import { accessPluginVia, calloutHook, calloutResponseSchema as calloutResponseSchema2, captchaSolverVia } from "@opencraw/core";
162
+ function clientTransport(label, runId, ask) {
163
+ return {
164
+ label,
165
+ call: async (request) => calloutResponseSchema2.parse(await ask("callout", { runId, request }))
166
+ };
167
+ }
168
+ function clientHooks(names, runId, ask) {
169
+ return Object.fromEntries(names.map((name) => [name, calloutHook(name, clientTransport(`client hook ${name}`, runId, ask))]));
170
+ }
171
+ function clientCaptchaSolvers(names, runId, ask) {
172
+ return names.map((name) => captchaSolverVia(name, clientTransport(`client captcha solver ${name}`, runId, ask)));
173
+ }
174
+ function clientAccessPlugins(names, runId, ask) {
175
+ return names.map((name) => accessPluginVia(name, clientTransport(`client access plugin ${name}`, runId, ask)));
176
+ }
177
+
178
+ // src/site-access/resolve-site-access.use-case.ts
179
+ import { resolveAccess } from "@opencraw/cli";
180
+ async function resolveSiteAccess(params, runId, ask, env = process.env) {
181
+ const file = params.accessConfig ?? env.OPENCRAW_ACCESS;
182
+ if (file === void 0 && params.access !== void 0) throw new Error(`access profile "${params.access}" needs an access config: give "accessConfig", or set OPENCRAW_ACCESS where the driver runs`);
183
+ const access = await resolveAccess({ insecureTls: false, access: file, accessProfile: params.access });
184
+ return { access, accessPlugins: clientAccessPlugins(params.accessPlugins ?? [], runId, ask) };
185
+ }
186
+
187
+ // src/session/recipe-loading.use-case.ts
188
+ import { loadForRun } from "@opencraw/cli";
189
+ import { hookUses, RecipeBindingError, RecipeValidationError } from "@opencraw/core";
190
+ function sourceOf(params) {
191
+ if (params.paths === void 0 === (params.recipes === void 0)) throw new Error('give exactly one of "paths" or "recipes"');
192
+ return params.paths ?? params.recipes ?? [];
193
+ }
194
+ async function loadRecipes(params) {
195
+ return await loadForRun(sourceOf(params), params.only ?? []);
196
+ }
197
+ function hooksOf(set) {
198
+ return [...new Set(set.inputs.flatMap((input) => hookUses(input).map((use) => use.name)))].sort((x, y) => x.localeCompare(y));
199
+ }
200
+ async function validateRecipes(params) {
201
+ try {
202
+ const set = await loadRecipes(params);
203
+ return { ok: true, inputs: set.inputs.map((input) => input.id), hooks: hooksOf(set), issues: [] };
204
+ } catch (error) {
205
+ if (error instanceof RecipeValidationError) return { ok: false, inputs: [], hooks: [], issues: error.issues.map((issue) => ({ source: error.source, path: issue.path, message: issue.message })) };
206
+ if (error instanceof RecipeBindingError) return { ok: false, inputs: [], hooks: [], issues: error.issues.map((issue) => ({ recipeId: issue.recipeId, path: issue.path, message: issue.message })) };
207
+ return { ok: false, inputs: [], hooks: [], issues: [{ path: "", message: error instanceof Error ? error.message : String(error) }] };
208
+ }
209
+ }
210
+
211
+ // src/session/streaming-sink.repository.ts
212
+ function streamingSink() {
213
+ let written = 0;
214
+ return {
215
+ open: async () => {
216
+ },
217
+ write: async () => {
218
+ written += 1;
219
+ },
220
+ close: async () => ({ written })
221
+ };
222
+ }
223
+
224
+ // src/session/run-session.use-case.ts
225
+ function runSessions(notify, ask) {
226
+ const running = /* @__PURE__ */ new Map();
227
+ let next = 1;
228
+ const crawl = async (runId, crawler, set) => {
229
+ try {
230
+ const report = await crawler.run(set);
231
+ notify("finished", { runId, report });
232
+ } catch (error) {
233
+ notify("finished", { runId, error: error instanceof Error ? error.message : String(error) });
234
+ } finally {
235
+ running.delete(runId);
236
+ await crawler.close();
237
+ }
238
+ };
239
+ return {
240
+ start: async (params) => {
241
+ const loaded = await loadRecipes(params);
242
+ const set = params.dryRun === true ? new RecipeSet(loaded.output, loaded.inputs.map((input) => ({ ...input, limits: { ...input.limits, maxRecords: 1 } }))) : loaded;
243
+ const runId = `run-${next++}`;
244
+ const { access, accessPlugins } = await resolveSiteAccess(params, runId, ask);
245
+ const crawler = createCrawler({
246
+ sink: params.out === void 0 ? streamingSink() : fileSink2(params.out, { append: params.append, browser: { executablePath: params.browser?.executablePath } }),
247
+ hooks: clientHooks(params.hooks ?? [], runId, ask),
248
+ captchaSolvers: clientCaptchaSolvers(params.captchaSolvers ?? [], runId, ask),
249
+ access,
250
+ accessPlugins,
251
+ profilesDir: process.env.OPENCRAW_PROFILES,
252
+ parallel: params.parallel,
253
+ resume: params.resume,
254
+ debug: params.dryRun,
255
+ browser: { headless: params.browser?.headless ?? true, executablePath: params.browser?.executablePath, ignoreHTTPSErrors: params.browser?.ignoreHTTPSErrors },
256
+ onEvent: (event) => {
257
+ notify("event", { runId, event });
258
+ if (event.type === "record:emit" && params.out === void 0) notify("record", { runId, recipeId: event.recipeId, key: event.key, data: event.data });
259
+ }
260
+ });
261
+ running.set(runId, crawler);
262
+ void crawl(runId, crawler, set);
263
+ return runId;
264
+ },
265
+ cancel: async (runId) => {
266
+ const crawler = running.get(runId);
267
+ if (crawler === void 0) return false;
268
+ running.delete(runId);
269
+ await crawler.close();
270
+ return true;
271
+ },
272
+ closeAll: async () => {
273
+ const crawlers = running.values().toArray();
274
+ running.clear();
275
+ await Promise.all(crawlers.map(async (crawler) => {
276
+ await crawler.close();
277
+ }));
278
+ }
279
+ };
280
+ }
281
+
282
+ // src/site-probe/probe-site.use-case.ts
283
+ import { probeUrl } from "@opencraw/cli";
284
+ async function probeSite(params, probeId, ask) {
285
+ const { access, accessPlugins } = await resolveSiteAccess(params, probeId, ask);
286
+ const result = await probeUrl(params.url, {
287
+ browser: params.browser ?? false,
288
+ browserPath: params.browserPath ?? process.env.OPENCRAW_CHROMIUM,
289
+ insecureTls: params.insecureTls ?? false,
290
+ userAgent: params.userAgent,
291
+ accessConfig: access,
292
+ accessPlugins
293
+ });
294
+ return { ...result };
295
+ }
296
+
297
+ // src/transport/line-channel.client.ts
298
+ import { createInterface } from "node:readline";
299
+
300
+ // src/transport/json-rpc.contract.ts
301
+ var RPC_ERRORS = {
302
+ parse: -32700,
303
+ invalidRequest: -32600,
304
+ methodNotFound: -32601,
305
+ invalidParams: -32602,
306
+ internal: -32603,
307
+ /** A failure the caller must act on: the message says what to do. */
308
+ server: -32e3
309
+ };
310
+ var RpcFailure = class extends Error {
311
+ code;
312
+ data;
313
+ constructor(code, message, data) {
314
+ super(message);
315
+ this.name = "RpcFailure";
316
+ this.code = code;
317
+ this.data = data;
318
+ }
319
+ };
320
+
321
+ // src/transport/line-channel.client.ts
322
+ var isRequestId = (id) => typeof id === "number" || typeof id === "string";
323
+ function openLineChannel(input, output, handle) {
324
+ const pending = /* @__PURE__ */ new Map();
325
+ const answering = /* @__PURE__ */ new Set();
326
+ const track = (answered) => {
327
+ answering.add(answered);
328
+ void (async () => {
329
+ try {
330
+ await answered;
331
+ } finally {
332
+ answering.delete(answered);
333
+ }
334
+ })();
335
+ };
336
+ let nextId = 1;
337
+ const write = (message) => {
338
+ output.write(`${JSON.stringify(message)}
339
+ `);
340
+ };
341
+ const fail = (id, error) => {
342
+ write({ jsonrpc: "2.0", id, error });
343
+ };
344
+ const answer = async (message) => {
345
+ const method = message.method;
346
+ try {
347
+ const result = await handle(method, message.params);
348
+ if (isRequestId(message.id)) write({ jsonrpc: "2.0", id: message.id, result: result ?? null });
349
+ } catch (error) {
350
+ if (!isRequestId(message.id)) return;
351
+ if (error instanceof RpcFailure) fail(message.id, { code: error.code, message: error.message, ...error.data !== void 0 && { data: error.data } });
352
+ else fail(message.id, { code: RPC_ERRORS.server, message: error instanceof Error ? error.message : String(error) });
353
+ }
354
+ };
355
+ const settle = (message) => {
356
+ const waiting = typeof message.id === "number" ? pending.get(message.id) : void 0;
357
+ if (waiting === void 0) return;
358
+ pending.delete(message.id);
359
+ if (message.error === void 0) waiting.resolve(message.result);
360
+ else waiting.reject(new RpcFailure(message.error.code, message.error.message, message.error.data));
361
+ };
362
+ const lines = createInterface({ input, crlfDelay: Infinity });
363
+ lines.on("line", (line) => {
364
+ if (line.trim() === "") return;
365
+ let message;
366
+ try {
367
+ message = JSON.parse(line);
368
+ } catch {
369
+ fail(null, { code: RPC_ERRORS.parse, message: "the line is not JSON" });
370
+ return;
371
+ }
372
+ if (message === null || typeof message !== "object" || message.jsonrpc !== "2.0") {
373
+ fail(isRequestId(message?.id) ? message.id : null, { code: RPC_ERRORS.invalidRequest, message: "not a JSON-RPC 2.0 message" });
374
+ return;
375
+ }
376
+ if (typeof message.method === "string") track(answer(message));
377
+ else settle(message);
378
+ });
379
+ const closed = new Promise((resolve) => {
380
+ lines.on("close", () => {
381
+ for (const waiting of pending.values()) waiting.reject(new Error("the client closed the channel"));
382
+ pending.clear();
383
+ resolve();
384
+ });
385
+ });
386
+ return {
387
+ request: async (method, params) => await new Promise((resolve, reject) => {
388
+ const id = nextId++;
389
+ pending.set(id, { resolve, reject });
390
+ write({ jsonrpc: "2.0", id, method, params });
391
+ }),
392
+ notify: (method, params) => {
393
+ write({ jsonrpc: "2.0", method, params });
394
+ },
395
+ closed,
396
+ drained: async () => {
397
+ while (answering.size > 0) await Promise.allSettled(answering);
398
+ }
399
+ };
400
+ }
401
+
402
+ // src/server/serve-driver.use-case.ts
403
+ var METHODS = ["initialize", "validate", "run", "cancel", "probe", "diff", "export", "shutdown"];
404
+ var major = (version) => version.split(".", 1)[0];
405
+ function parsed(schema, params) {
406
+ const result = schema.safeParse(params ?? {});
407
+ if (!result.success) throw new RpcFailure(RPC_ERRORS.invalidParams, result.error.issues.map((issue) => `${issue.path.join(".") || "(params)"}: ${issue.message}`).join("; "), result.error.issues);
408
+ return result.data;
409
+ }
410
+ async function serveDriver(input, output, identity) {
411
+ let initialized = false;
412
+ const stopping = new AbortController();
413
+ let probes = 0;
414
+ const channel = openLineChannel(input, output, async (method, params) => {
415
+ if (method === "initialize") {
416
+ const hello = parsed(initializeParamsSchema, params);
417
+ if (major(hello.protocolVersion) !== major(PROTOCOL_VERSION)) throw new RpcFailure(RPC_ERRORS.server, `this driver speaks protocol ${PROTOCOL_VERSION}; ${hello.clientName} ${hello.clientVersion} asked for ${hello.protocolVersion}: use a client for protocol ${major(PROTOCOL_VERSION)}.x`);
418
+ initialized = true;
419
+ const result = { driverVersion: identity.driverVersion, protocolVersion: PROTOCOL_VERSION, methods: METHODS };
420
+ return result;
421
+ }
422
+ if (!METHODS.includes(method)) throw new RpcFailure(RPC_ERRORS.methodNotFound, `no method "${method}": the driver has ${METHODS.join(", ")}`);
423
+ if (!initialized) throw new RpcFailure(RPC_ERRORS.server, "call initialize first");
424
+ if (method === "validate") return await validateRecipes(parsed(validateParamsSchema, params));
425
+ if (method === "run") return { runId: await sessions.start(parsed(runParamsSchema, params)) };
426
+ if (method === "cancel") return { cancelled: await sessions.cancel(parsed(cancelParamsSchema, params).runId) };
427
+ if (method === "probe") return await probeSite(parsed(probeParamsSchema, params), `probe-${++probes}`, channel.request);
428
+ if (method === "diff") return await diffRecordFiles(parsed(diffParamsSchema, params));
429
+ if (method === "export") return await exportRecordFiles(parsed(exportParamsSchema, params));
430
+ await sessions.closeAll();
431
+ setImmediate(() => {
432
+ stopping.abort();
433
+ });
434
+ return {};
435
+ });
436
+ const sessions = runSessions(channel.notify, channel.request);
437
+ await Promise.race([channel.closed, once(stopping.signal, "abort")]);
438
+ await channel.drained();
439
+ await sessions.closeAll();
440
+ }
441
+
442
+ // src/main.ts
443
+ console.log = console.error;
444
+ console.info = console.error;
445
+ function driverVersion() {
446
+ let dir = dirname(fileURLToPath(import.meta.url));
447
+ for (let i = 0; i < 6; i++) {
448
+ try {
449
+ const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
450
+ if (manifest.name === "@opencraw/driver" && manifest.version !== void 0) return manifest.version;
451
+ } catch {
452
+ }
453
+ dir = dirname(dir);
454
+ }
455
+ return "0.0.0";
456
+ }
457
+ await serveDriver(process.stdin, process.stdout, { driverVersion: driverVersion() });
458
+ process.exit(0);
459
+ //# sourceMappingURL=main.esm.js.map
@@ -0,0 +1,45 @@
1
+ import type { AccessPlugin, CalloutTransport, CaptchaSolver, HookMap } from '@opencraw/core';
2
+ /** Sends one request to the client and waits for its answer: the channel's `request`. */
3
+ export type AskClient = (method: string, params: unknown) => Promise<unknown>;
4
+ /**
5
+ * A transport whose handler is the client: each call is a `callout` request carrying core's own
6
+ * `CalloutRequest`, and the client answers with a `CalloutResponse`, so `pending` and idempotency keys
7
+ * work exactly as they do for a command or an HTTP hook.
8
+ *
9
+ * @param label - What the logs call it (`client hook tidy`).
10
+ * @param runId - The run it belongs to, sent with every callout.
11
+ * @param ask - The channel's request.
12
+ * @returns The transport.
13
+ */
14
+ export declare function clientTransport(label: string, runId: string, ask: AskClient): CalloutTransport;
15
+ /**
16
+ * The hooks a run's client serves, each answered by the client over the channel.
17
+ *
18
+ * @param names - The hooks the client said it serves.
19
+ * @param runId - The run they belong to, sent with every callout.
20
+ * @param ask - The channel's request.
21
+ * @returns A hook map for `createCrawler`.
22
+ */
23
+ export declare function clientHooks(names: readonly string[], runId: string, ask: AskClient): HookMap;
24
+ /**
25
+ * The captcha solvers a run's client serves: a challenge goes to the client as a `captcha` callout (its
26
+ * input is core's `CaptchaCalloutInput`), and the answer's `token` or `text` is put on the page.
27
+ *
28
+ * @param names - The solvers the client said it serves.
29
+ * @param runId - The run they belong to.
30
+ * @param ask - The channel's request.
31
+ * @returns Solvers for `createCrawler`.
32
+ */
33
+ export declare function clientCaptchaSolvers(names: readonly string[], runId: string, ask: AskClient): CaptchaSolver[];
34
+ /**
35
+ * The access plugins a client serves: a profile `{ "kind": "plugin", "name": … }` leases through an
36
+ * `access` callout to the client (`phase: "lease"`), and releases through another (`phase: "release"`)
37
+ * when the client's lease asked for it.
38
+ *
39
+ * @param names - The plugins the client said it serves.
40
+ * @param runId - The run, or the probe, they belong to.
41
+ * @param ask - The channel's request.
42
+ * @returns Plugins for `createCrawler` or a probe.
43
+ */
44
+ export declare function clientAccessPlugins(names: readonly string[], runId: string, ask: AskClient): AccessPlugin[];
45
+ //# sourceMappingURL=client-hooks.client.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { clientAccessPlugins, clientCaptchaSolvers, clientHooks, clientTransport } from './client-hooks.client.js';
2
+ export type { AskClient } from './client-hooks.client.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,12 @@
1
+ export { serveDriver } from './server/index.js';
2
+ export type { DriverIdentity } from './server/index.js';
3
+ export { PROTOCOL_VERSION, MESSAGES, driverProtocolJsonSchema, initializeParamsSchema, initializeResultSchema, validateParamsSchema, validateResultSchema, runParamsSchema, runResultSchema, cancelParamsSchema, cancelResultSchema, eventParamsSchema, recordParamsSchema, finishedParamsSchema, calloutParamsSchema, calloutResultSchema, probeParamsSchema, probeResultSchema, diffParamsSchema, diffResultSchema, exportParamsSchema, exportResultSchema, } from './protocol/index.js';
4
+ export type { InitializeParams, InitializeResult, ValidateParams, ValidateResult, RunParams, RunResult, CancelParams, CancelResult, EventParams, RecordParams, FinishedParams, CalloutParams, ProbeParams, DiffParams, DiffResult, ExportParams, ExportResult, } from './protocol/index.js';
5
+ export { openLineChannel, RPC_ERRORS, RpcFailure } from './transport/index.js';
6
+ export type { LineChannel, RequestHandler, RpcError, RpcMessage } from './transport/index.js';
7
+ export { runSessions, validateRecipes, streamingSink } from './session/index.js';
8
+ export type { RunSessions, NotifyClient } from './session/index.js';
9
+ export { clientHooks, clientCaptchaSolvers, clientAccessPlugins } from './callout-bridge/index.js';
10
+ export { probeSite } from './site-probe/index.js';
11
+ export { diffRecordFiles, exportRecordFiles } from './record-files/index.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The driver protocol as one JSON Schema document (`schemas/driver-protocol.schema.json`): every method's
3
+ * params and result, every notification's params, by name under `$defs`. A client in another language
4
+ * generates its models from it instead of typing them by hand.
5
+ *
6
+ * @returns A draft 2020-12 document.
7
+ */
8
+ export declare function driverProtocolJsonSchema(): Record<string, unknown>;
9
+ //# sourceMappingURL=driver-json-schema.mapper.d.ts.map