@opencraw/driver 0.0.0-stage → 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.
@@ -0,0 +1,334 @@
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.0.0";
15
+ var recipeObject = z.record(z.string(), z.unknown());
16
+ var recipeSource = {
17
+ paths: z.array(z.string()).min(1).optional(),
18
+ recipes: z.array(recipeObject).min(1).optional()
19
+ };
20
+ var initializeParamsSchema = z.strictObject({
21
+ clientName: z.string().min(1),
22
+ clientVersion: z.string().min(1),
23
+ protocolVersion: z.string().regex(/^\d+\.\d+\.\d+$/)
24
+ });
25
+ var initializeResultSchema = z.strictObject({
26
+ driverVersion: z.string(),
27
+ protocolVersion: z.string(),
28
+ methods: z.array(z.string())
29
+ });
30
+ var validateParamsSchema = z.strictObject({ ...recipeSource, only: z.array(z.string()).optional() });
31
+ var issueSchema = z.strictObject({ source: z.string().optional(), recipeId: z.string().optional(), path: z.string(), message: z.string() });
32
+ var validateResultSchema = z.strictObject({
33
+ ok: z.boolean(),
34
+ /** The input recipes that would run, when ok. */
35
+ inputs: z.array(z.string()),
36
+ /** Every hook the recipes call, which a `run` must serve. */
37
+ hooks: z.array(z.string()),
38
+ issues: z.array(issueSchema)
39
+ });
40
+ var runParamsSchema = z.strictObject({
41
+ ...recipeSource,
42
+ only: z.array(z.string()).optional(),
43
+ /** A file to write the records to, its extension naming the format; without it, each record is sent as a `record` notification. */
44
+ out: z.string().optional(),
45
+ append: z.boolean().optional(),
46
+ resume: z.boolean().optional(),
47
+ dryRun: z.boolean().optional(),
48
+ parallel: z.number().int().min(1).max(16).optional(),
49
+ browser: z.strictObject({ headless: z.boolean().optional(), executablePath: z.string().optional(), ignoreHTTPSErrors: z.boolean().optional() }).optional(),
50
+ /** The hooks the client answers itself: each becomes a `callout` request to the client when a recipe calls it. */
51
+ hooks: z.array(z.string()).optional()
52
+ });
53
+ var runResultSchema = z.strictObject({ runId: z.string() });
54
+ var cancelParamsSchema = z.strictObject({ runId: z.string() });
55
+ var cancelResultSchema = z.strictObject({ cancelled: z.boolean() });
56
+ var shutdownResultSchema = z.strictObject({});
57
+ var eventParamsSchema = z.strictObject({ runId: z.string(), event: z.record(z.string(), z.unknown()) });
58
+ var recordParamsSchema = z.strictObject({ runId: z.string(), recipeId: z.string(), key: z.string().nullable(), data: z.record(z.string(), z.unknown()) });
59
+ var finishedParamsSchema = z.strictObject({
60
+ runId: z.string(),
61
+ /** The crawl report (`CrawlReport`), absent when the run could not start. */
62
+ report: z.record(z.string(), z.unknown()).optional(),
63
+ /** Why the run could not start, or stopped as a whole. */
64
+ error: z.string().optional()
65
+ });
66
+ var calloutParamsSchema = z.strictObject({ runId: z.string(), request: calloutRequestSchema });
67
+
68
+ // src/protocol/driver-json-schema.mapper.ts
69
+ import { z as z2 } from "zod";
70
+
71
+ // src/session/run-session.use-case.ts
72
+ import { createCrawler, fileSink, RecipeSet } from "@opencraw/core";
73
+
74
+ // src/callout-bridge/client-hooks.client.ts
75
+ import { calloutHook, calloutResponseSchema as calloutResponseSchema2 } from "@opencraw/core";
76
+ function clientTransport(hookName, runId, ask) {
77
+ return {
78
+ label: `client hook ${hookName}`,
79
+ call: async (request) => calloutResponseSchema2.parse(await ask("callout", { runId, request }))
80
+ };
81
+ }
82
+ function clientHooks(names, runId, ask) {
83
+ return Object.fromEntries(names.map((name) => [name, calloutHook(name, clientTransport(name, runId, ask))]));
84
+ }
85
+
86
+ // src/session/recipe-loading.use-case.ts
87
+ import { loadForRun } from "@opencraw/cli";
88
+ import { hookUses, RecipeBindingError, RecipeValidationError } from "@opencraw/core";
89
+ function sourceOf(params) {
90
+ if (params.paths === void 0 === (params.recipes === void 0)) throw new Error('give exactly one of "paths" or "recipes"');
91
+ return params.paths ?? params.recipes ?? [];
92
+ }
93
+ async function loadRecipes(params) {
94
+ return await loadForRun(sourceOf(params), params.only ?? []);
95
+ }
96
+ function hooksOf(set) {
97
+ return [...new Set(set.inputs.flatMap((input) => hookUses(input).map((use) => use.name)))].sort((x, y) => x.localeCompare(y));
98
+ }
99
+ async function validateRecipes(params) {
100
+ try {
101
+ const set = await loadRecipes(params);
102
+ return { ok: true, inputs: set.inputs.map((input) => input.id), hooks: hooksOf(set), issues: [] };
103
+ } catch (error) {
104
+ if (error instanceof RecipeValidationError) return { ok: false, inputs: [], hooks: [], issues: error.issues.map((issue) => ({ source: error.source, path: issue.path, message: issue.message })) };
105
+ if (error instanceof RecipeBindingError) return { ok: false, inputs: [], hooks: [], issues: error.issues.map((issue) => ({ recipeId: issue.recipeId, path: issue.path, message: issue.message })) };
106
+ return { ok: false, inputs: [], hooks: [], issues: [{ path: "", message: error instanceof Error ? error.message : String(error) }] };
107
+ }
108
+ }
109
+
110
+ // src/session/streaming-sink.repository.ts
111
+ function streamingSink() {
112
+ let written = 0;
113
+ return {
114
+ open: async () => {
115
+ },
116
+ write: async () => {
117
+ written += 1;
118
+ },
119
+ close: async () => ({ written })
120
+ };
121
+ }
122
+
123
+ // src/session/run-session.use-case.ts
124
+ function runSessions(notify, ask) {
125
+ const running = /* @__PURE__ */ new Map();
126
+ let next = 1;
127
+ const crawl = async (runId, crawler, set) => {
128
+ try {
129
+ const report = await crawler.run(set);
130
+ notify("finished", { runId, report });
131
+ } catch (error) {
132
+ notify("finished", { runId, error: error instanceof Error ? error.message : String(error) });
133
+ } finally {
134
+ running.delete(runId);
135
+ await crawler.close();
136
+ }
137
+ };
138
+ return {
139
+ start: async (params) => {
140
+ const loaded = await loadRecipes(params);
141
+ const set = params.dryRun === true ? new RecipeSet(loaded.output, loaded.inputs.map((input) => ({ ...input, limits: { ...input.limits, maxRecords: 1 } }))) : loaded;
142
+ const runId = `run-${next++}`;
143
+ const crawler = createCrawler({
144
+ sink: params.out === void 0 ? streamingSink() : fileSink(params.out, { append: params.append, browser: { executablePath: params.browser?.executablePath } }),
145
+ hooks: clientHooks(params.hooks ?? [], runId, ask),
146
+ parallel: params.parallel,
147
+ resume: params.resume,
148
+ debug: params.dryRun,
149
+ browser: { headless: params.browser?.headless ?? true, executablePath: params.browser?.executablePath, ignoreHTTPSErrors: params.browser?.ignoreHTTPSErrors },
150
+ onEvent: (event) => {
151
+ notify("event", { runId, event });
152
+ if (event.type === "record:emit" && params.out === void 0) notify("record", { runId, recipeId: event.recipeId, key: event.key, data: event.data });
153
+ }
154
+ });
155
+ running.set(runId, crawler);
156
+ void crawl(runId, crawler, set);
157
+ return runId;
158
+ },
159
+ cancel: async (runId) => {
160
+ const crawler = running.get(runId);
161
+ if (crawler === void 0) return false;
162
+ running.delete(runId);
163
+ await crawler.close();
164
+ return true;
165
+ },
166
+ closeAll: async () => {
167
+ const crawlers = running.values().toArray();
168
+ running.clear();
169
+ await Promise.all(crawlers.map(async (crawler) => {
170
+ await crawler.close();
171
+ }));
172
+ }
173
+ };
174
+ }
175
+
176
+ // src/transport/line-channel.client.ts
177
+ import { createInterface } from "node:readline";
178
+
179
+ // src/transport/json-rpc.contract.ts
180
+ var RPC_ERRORS = {
181
+ parse: -32700,
182
+ invalidRequest: -32600,
183
+ methodNotFound: -32601,
184
+ invalidParams: -32602,
185
+ internal: -32603,
186
+ /** A failure the caller must act on: the message says what to do. */
187
+ server: -32e3
188
+ };
189
+ var RpcFailure = class extends Error {
190
+ code;
191
+ data;
192
+ constructor(code, message, data) {
193
+ super(message);
194
+ this.name = "RpcFailure";
195
+ this.code = code;
196
+ this.data = data;
197
+ }
198
+ };
199
+
200
+ // src/transport/line-channel.client.ts
201
+ var isRequestId = (id) => typeof id === "number" || typeof id === "string";
202
+ function openLineChannel(input, output, handle) {
203
+ const pending = /* @__PURE__ */ new Map();
204
+ const answering = /* @__PURE__ */ new Set();
205
+ const track = (answered) => {
206
+ answering.add(answered);
207
+ void (async () => {
208
+ try {
209
+ await answered;
210
+ } finally {
211
+ answering.delete(answered);
212
+ }
213
+ })();
214
+ };
215
+ let nextId = 1;
216
+ const write = (message) => {
217
+ output.write(`${JSON.stringify(message)}
218
+ `);
219
+ };
220
+ const fail = (id, error) => {
221
+ write({ jsonrpc: "2.0", id, error });
222
+ };
223
+ const answer = async (message) => {
224
+ const method = message.method;
225
+ try {
226
+ const result = await handle(method, message.params);
227
+ if (isRequestId(message.id)) write({ jsonrpc: "2.0", id: message.id, result: result ?? null });
228
+ } catch (error) {
229
+ if (!isRequestId(message.id)) return;
230
+ if (error instanceof RpcFailure) fail(message.id, { code: error.code, message: error.message, ...error.data !== void 0 && { data: error.data } });
231
+ else fail(message.id, { code: RPC_ERRORS.server, message: error instanceof Error ? error.message : String(error) });
232
+ }
233
+ };
234
+ const settle = (message) => {
235
+ const waiting = typeof message.id === "number" ? pending.get(message.id) : void 0;
236
+ if (waiting === void 0) return;
237
+ pending.delete(message.id);
238
+ if (message.error === void 0) waiting.resolve(message.result);
239
+ else waiting.reject(new RpcFailure(message.error.code, message.error.message, message.error.data));
240
+ };
241
+ const lines = createInterface({ input, crlfDelay: Infinity });
242
+ lines.on("line", (line) => {
243
+ if (line.trim() === "") return;
244
+ let message;
245
+ try {
246
+ message = JSON.parse(line);
247
+ } catch {
248
+ fail(null, { code: RPC_ERRORS.parse, message: "the line is not JSON" });
249
+ return;
250
+ }
251
+ if (message === null || typeof message !== "object" || message.jsonrpc !== "2.0") {
252
+ fail(isRequestId(message?.id) ? message.id : null, { code: RPC_ERRORS.invalidRequest, message: "not a JSON-RPC 2.0 message" });
253
+ return;
254
+ }
255
+ if (typeof message.method === "string") track(answer(message));
256
+ else settle(message);
257
+ });
258
+ const closed = new Promise((resolve) => {
259
+ lines.on("close", () => {
260
+ for (const waiting of pending.values()) waiting.reject(new Error("the client closed the channel"));
261
+ pending.clear();
262
+ resolve();
263
+ });
264
+ });
265
+ return {
266
+ request: async (method, params) => await new Promise((resolve, reject) => {
267
+ const id = nextId++;
268
+ pending.set(id, { resolve, reject });
269
+ write({ jsonrpc: "2.0", id, method, params });
270
+ }),
271
+ notify: (method, params) => {
272
+ write({ jsonrpc: "2.0", method, params });
273
+ },
274
+ closed,
275
+ drained: async () => {
276
+ while (answering.size > 0) await Promise.allSettled(answering);
277
+ }
278
+ };
279
+ }
280
+
281
+ // src/server/serve-driver.use-case.ts
282
+ var METHODS = ["initialize", "validate", "run", "cancel", "shutdown"];
283
+ var major = (version) => version.split(".", 1)[0];
284
+ function parsed(schema, params) {
285
+ const result = schema.safeParse(params ?? {});
286
+ if (!result.success) throw new RpcFailure(RPC_ERRORS.invalidParams, result.error.issues.map((issue) => `${issue.path.join(".") || "(params)"}: ${issue.message}`).join("; "), result.error.issues);
287
+ return result.data;
288
+ }
289
+ async function serveDriver(input, output, identity) {
290
+ let initialized = false;
291
+ const stopping = new AbortController();
292
+ const channel = openLineChannel(input, output, async (method, params) => {
293
+ if (method === "initialize") {
294
+ const hello = parsed(initializeParamsSchema, params);
295
+ 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`);
296
+ initialized = true;
297
+ const result = { driverVersion: identity.driverVersion, protocolVersion: PROTOCOL_VERSION, methods: METHODS };
298
+ return result;
299
+ }
300
+ if (!METHODS.includes(method)) throw new RpcFailure(RPC_ERRORS.methodNotFound, `no method "${method}": the driver has ${METHODS.join(", ")}`);
301
+ if (!initialized) throw new RpcFailure(RPC_ERRORS.server, "call initialize first");
302
+ if (method === "validate") return await validateRecipes(parsed(validateParamsSchema, params));
303
+ if (method === "run") return { runId: await sessions.start(parsed(runParamsSchema, params)) };
304
+ if (method === "cancel") return { cancelled: await sessions.cancel(parsed(cancelParamsSchema, params).runId) };
305
+ await sessions.closeAll();
306
+ setImmediate(() => {
307
+ stopping.abort();
308
+ });
309
+ return {};
310
+ });
311
+ const sessions = runSessions(channel.notify, channel.request);
312
+ await Promise.race([channel.closed, once(stopping.signal, "abort")]);
313
+ await channel.drained();
314
+ await sessions.closeAll();
315
+ }
316
+
317
+ // src/main.ts
318
+ console.log = console.error;
319
+ console.info = console.error;
320
+ function driverVersion() {
321
+ let dir = dirname(fileURLToPath(import.meta.url));
322
+ for (let i = 0; i < 6; i++) {
323
+ try {
324
+ const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
325
+ if (manifest.name === "@opencraw/driver" && manifest.version !== void 0) return manifest.version;
326
+ } catch {
327
+ }
328
+ dir = dirname(dir);
329
+ }
330
+ return "0.0.0";
331
+ }
332
+ await serveDriver(process.stdin, process.stdout, { driverVersion: driverVersion() });
333
+ process.exit(0);
334
+ //# sourceMappingURL=main.esm.js.map
@@ -0,0 +1,19 @@
1
+ import type { CalloutTransport, 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
+ export declare function clientTransport(hookName: string, runId: string, ask: AskClient): CalloutTransport;
10
+ /**
11
+ * The hooks a run's client serves, each answered by the client over the channel.
12
+ *
13
+ * @param names - The hooks the client said it serves.
14
+ * @param runId - The run they belong to, sent with every callout.
15
+ * @param ask - The channel's request.
16
+ * @returns A hook map for `createCrawler`.
17
+ */
18
+ export declare function clientHooks(names: readonly string[], runId: string, ask: AskClient): HookMap;
19
+ //# sourceMappingURL=client-hooks.client.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { 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,10 @@
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, } from './protocol/index.js';
4
+ export type { InitializeParams, InitializeResult, ValidateParams, ValidateResult, RunParams, RunResult, CancelParams, CancelResult, EventParams, RecordParams, FinishedParams, CalloutParams, } 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 } from './callout-bridge/index.js';
10
+ //# 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
@@ -0,0 +1,251 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * The driver protocol's version, separate from the package's: a client pins a major and the driver refuses
4
+ * another. Additions (methods, optional fields) raise the minor; anything a client could break on, the major.
5
+ */
6
+ export declare const PROTOCOL_VERSION = "1.0.0";
7
+ export declare const initializeParamsSchema: z.ZodObject<{
8
+ clientName: z.ZodString;
9
+ clientVersion: z.ZodString;
10
+ protocolVersion: z.ZodString;
11
+ }, z.core.$strict>;
12
+ export type InitializeParams = z.infer<typeof initializeParamsSchema>;
13
+ export declare const initializeResultSchema: z.ZodObject<{
14
+ driverVersion: z.ZodString;
15
+ protocolVersion: z.ZodString;
16
+ methods: z.ZodArray<z.ZodString>;
17
+ }, z.core.$strict>;
18
+ export type InitializeResult = z.infer<typeof initializeResultSchema>;
19
+ export declare const validateParamsSchema: z.ZodObject<{
20
+ paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
21
+ recipes: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
22
+ only: z.ZodOptional<z.ZodArray<z.ZodString>>;
23
+ }, z.core.$strict>;
24
+ export type ValidateParams = z.infer<typeof validateParamsSchema>;
25
+ export declare const validateResultSchema: z.ZodObject<{
26
+ ok: z.ZodBoolean;
27
+ inputs: z.ZodArray<z.ZodString>;
28
+ hooks: z.ZodArray<z.ZodString>;
29
+ issues: z.ZodArray<z.ZodObject<{
30
+ source: z.ZodOptional<z.ZodString>;
31
+ recipeId: z.ZodOptional<z.ZodString>;
32
+ path: z.ZodString;
33
+ message: z.ZodString;
34
+ }, z.core.$strict>>;
35
+ }, z.core.$strict>;
36
+ export type ValidateResult = z.infer<typeof validateResultSchema>;
37
+ export declare const runParamsSchema: z.ZodObject<{
38
+ paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
39
+ recipes: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
40
+ only: z.ZodOptional<z.ZodArray<z.ZodString>>;
41
+ out: z.ZodOptional<z.ZodString>;
42
+ append: z.ZodOptional<z.ZodBoolean>;
43
+ resume: z.ZodOptional<z.ZodBoolean>;
44
+ dryRun: z.ZodOptional<z.ZodBoolean>;
45
+ parallel: z.ZodOptional<z.ZodNumber>;
46
+ browser: z.ZodOptional<z.ZodObject<{
47
+ headless: z.ZodOptional<z.ZodBoolean>;
48
+ executablePath: z.ZodOptional<z.ZodString>;
49
+ ignoreHTTPSErrors: z.ZodOptional<z.ZodBoolean>;
50
+ }, z.core.$strict>>;
51
+ hooks: z.ZodOptional<z.ZodArray<z.ZodString>>;
52
+ }, z.core.$strict>;
53
+ export type RunParams = z.infer<typeof runParamsSchema>;
54
+ export declare const runResultSchema: z.ZodObject<{
55
+ runId: z.ZodString;
56
+ }, z.core.$strict>;
57
+ export type RunResult = z.infer<typeof runResultSchema>;
58
+ export declare const cancelParamsSchema: z.ZodObject<{
59
+ runId: z.ZodString;
60
+ }, z.core.$strict>;
61
+ export type CancelParams = z.infer<typeof cancelParamsSchema>;
62
+ export declare const cancelResultSchema: z.ZodObject<{
63
+ cancelled: z.ZodBoolean;
64
+ }, z.core.$strict>;
65
+ export type CancelResult = z.infer<typeof cancelResultSchema>;
66
+ export declare const shutdownResultSchema: z.ZodObject<{}, z.core.$strict>;
67
+ /** Every crawl event, as core reports it (`CrawlEvent`); its shape is core's, versioned with core. */
68
+ export declare const eventParamsSchema: z.ZodObject<{
69
+ runId: z.ZodString;
70
+ event: z.ZodRecord<z.ZodString, z.ZodUnknown>;
71
+ }, z.core.$strict>;
72
+ export type EventParams = z.infer<typeof eventParamsSchema>;
73
+ export declare const recordParamsSchema: z.ZodObject<{
74
+ runId: z.ZodString;
75
+ recipeId: z.ZodString;
76
+ key: z.ZodNullable<z.ZodString>;
77
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
78
+ }, z.core.$strict>;
79
+ export type RecordParams = z.infer<typeof recordParamsSchema>;
80
+ export declare const finishedParamsSchema: z.ZodObject<{
81
+ runId: z.ZodString;
82
+ report: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
83
+ error: z.ZodOptional<z.ZodString>;
84
+ }, z.core.$strict>;
85
+ export type FinishedParams = z.infer<typeof finishedParamsSchema>;
86
+ /** A hook the client serves: core's own callout request, answered with core's callout response. */
87
+ export declare const calloutParamsSchema: z.ZodObject<{
88
+ runId: z.ZodString;
89
+ request: z.ZodObject<{
90
+ kind: z.ZodEnum<{
91
+ access: "access";
92
+ captcha: "captcha";
93
+ hook: "hook";
94
+ }>;
95
+ name: z.ZodString;
96
+ input: z.ZodOptional<z.ZodUnknown>;
97
+ args: z.ZodRecord<z.ZodString, z.ZodUnknown>;
98
+ context: z.ZodObject<{
99
+ recipeId: z.ZodString;
100
+ scope: z.ZodRecord<z.ZodString, z.ZodUnknown>;
101
+ }, z.core.$strict>;
102
+ idempotencyKey: z.ZodString;
103
+ callback: z.ZodOptional<z.ZodObject<{
104
+ url: z.ZodString;
105
+ }, z.core.$strict>>;
106
+ }, z.core.$strict>;
107
+ }, z.core.$strict>;
108
+ export type CalloutParams = z.infer<typeof calloutParamsSchema>;
109
+ export declare const calloutResultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
110
+ status: z.ZodLiteral<"ok">;
111
+ output: z.ZodOptional<z.ZodUnknown>;
112
+ }, z.core.$strict>, z.ZodObject<{
113
+ status: z.ZodLiteral<"error">;
114
+ error: z.ZodString;
115
+ }, z.core.$strict>, z.ZodObject<{
116
+ status: z.ZodLiteral<"pending">;
117
+ retryAfterMs: z.ZodOptional<z.ZodNumber>;
118
+ }, z.core.$strict>], "status">;
119
+ /** Every method and notification, by name: what the generated schema and the clients' models are made from. */
120
+ export declare const MESSAGES: {
121
+ readonly initialize: {
122
+ readonly direction: 'client-to-driver';
123
+ readonly params: z.ZodObject<{
124
+ clientName: z.ZodString;
125
+ clientVersion: z.ZodString;
126
+ protocolVersion: z.ZodString;
127
+ }, z.core.$strict>;
128
+ readonly result: z.ZodObject<{
129
+ driverVersion: z.ZodString;
130
+ protocolVersion: z.ZodString;
131
+ methods: z.ZodArray<z.ZodString>;
132
+ }, z.core.$strict>;
133
+ };
134
+ readonly validate: {
135
+ readonly direction: 'client-to-driver';
136
+ readonly params: z.ZodObject<{
137
+ paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
138
+ recipes: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
139
+ only: z.ZodOptional<z.ZodArray<z.ZodString>>;
140
+ }, z.core.$strict>;
141
+ readonly result: z.ZodObject<{
142
+ ok: z.ZodBoolean;
143
+ inputs: z.ZodArray<z.ZodString>;
144
+ hooks: z.ZodArray<z.ZodString>;
145
+ issues: z.ZodArray<z.ZodObject<{
146
+ source: z.ZodOptional<z.ZodString>;
147
+ recipeId: z.ZodOptional<z.ZodString>;
148
+ path: z.ZodString;
149
+ message: z.ZodString;
150
+ }, z.core.$strict>>;
151
+ }, z.core.$strict>;
152
+ };
153
+ readonly run: {
154
+ readonly direction: 'client-to-driver';
155
+ readonly params: z.ZodObject<{
156
+ paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
157
+ recipes: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
158
+ only: z.ZodOptional<z.ZodArray<z.ZodString>>;
159
+ out: z.ZodOptional<z.ZodString>;
160
+ append: z.ZodOptional<z.ZodBoolean>;
161
+ resume: z.ZodOptional<z.ZodBoolean>;
162
+ dryRun: z.ZodOptional<z.ZodBoolean>;
163
+ parallel: z.ZodOptional<z.ZodNumber>;
164
+ browser: z.ZodOptional<z.ZodObject<{
165
+ headless: z.ZodOptional<z.ZodBoolean>;
166
+ executablePath: z.ZodOptional<z.ZodString>;
167
+ ignoreHTTPSErrors: z.ZodOptional<z.ZodBoolean>;
168
+ }, z.core.$strict>>;
169
+ hooks: z.ZodOptional<z.ZodArray<z.ZodString>>;
170
+ }, z.core.$strict>;
171
+ readonly result: z.ZodObject<{
172
+ runId: z.ZodString;
173
+ }, z.core.$strict>;
174
+ };
175
+ readonly cancel: {
176
+ readonly direction: 'client-to-driver';
177
+ readonly params: z.ZodObject<{
178
+ runId: z.ZodString;
179
+ }, z.core.$strict>;
180
+ readonly result: z.ZodObject<{
181
+ cancelled: z.ZodBoolean;
182
+ }, z.core.$strict>;
183
+ };
184
+ readonly shutdown: {
185
+ readonly direction: 'client-to-driver';
186
+ readonly params: undefined;
187
+ readonly result: z.ZodObject<{}, z.core.$strict>;
188
+ };
189
+ readonly callout: {
190
+ readonly direction: 'driver-to-client';
191
+ readonly params: z.ZodObject<{
192
+ runId: z.ZodString;
193
+ request: z.ZodObject<{
194
+ kind: z.ZodEnum<{
195
+ access: "access";
196
+ captcha: "captcha";
197
+ hook: "hook";
198
+ }>;
199
+ name: z.ZodString;
200
+ input: z.ZodOptional<z.ZodUnknown>;
201
+ args: z.ZodRecord<z.ZodString, z.ZodUnknown>;
202
+ context: z.ZodObject<{
203
+ recipeId: z.ZodString;
204
+ scope: z.ZodRecord<z.ZodString, z.ZodUnknown>;
205
+ }, z.core.$strict>;
206
+ idempotencyKey: z.ZodString;
207
+ callback: z.ZodOptional<z.ZodObject<{
208
+ url: z.ZodString;
209
+ }, z.core.$strict>>;
210
+ }, z.core.$strict>;
211
+ }, z.core.$strict>;
212
+ readonly result: z.ZodDiscriminatedUnion<[z.ZodObject<{
213
+ status: z.ZodLiteral<"ok">;
214
+ output: z.ZodOptional<z.ZodUnknown>;
215
+ }, z.core.$strict>, z.ZodObject<{
216
+ status: z.ZodLiteral<"error">;
217
+ error: z.ZodString;
218
+ }, z.core.$strict>, z.ZodObject<{
219
+ status: z.ZodLiteral<"pending">;
220
+ retryAfterMs: z.ZodOptional<z.ZodNumber>;
221
+ }, z.core.$strict>], "status">;
222
+ };
223
+ readonly event: {
224
+ readonly direction: 'driver-to-client';
225
+ readonly params: z.ZodObject<{
226
+ runId: z.ZodString;
227
+ event: z.ZodRecord<z.ZodString, z.ZodUnknown>;
228
+ }, z.core.$strict>;
229
+ readonly result: undefined;
230
+ };
231
+ readonly record: {
232
+ readonly direction: 'driver-to-client';
233
+ readonly params: z.ZodObject<{
234
+ runId: z.ZodString;
235
+ recipeId: z.ZodString;
236
+ key: z.ZodNullable<z.ZodString>;
237
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
238
+ }, z.core.$strict>;
239
+ readonly result: undefined;
240
+ };
241
+ readonly finished: {
242
+ readonly direction: 'driver-to-client';
243
+ readonly params: z.ZodObject<{
244
+ runId: z.ZodString;
245
+ report: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
246
+ error: z.ZodOptional<z.ZodString>;
247
+ }, z.core.$strict>;
248
+ readonly result: undefined;
249
+ };
250
+ };
251
+ //# sourceMappingURL=driver-message.contract.d.ts.map
@@ -0,0 +1,4 @@
1
+ export { PROTOCOL_VERSION, MESSAGES, initializeParamsSchema, initializeResultSchema, validateParamsSchema, validateResultSchema, runParamsSchema, runResultSchema, cancelParamsSchema, cancelResultSchema, shutdownResultSchema, eventParamsSchema, recordParamsSchema, finishedParamsSchema, calloutParamsSchema, calloutResultSchema, } from './driver-message.contract.js';
2
+ export type { InitializeParams, InitializeResult, ValidateParams, ValidateResult, RunParams, RunResult, CancelParams, CancelResult, EventParams, RecordParams, FinishedParams, CalloutParams, } from './driver-message.contract.js';
3
+ export { driverProtocolJsonSchema } from './driver-json-schema.mapper.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { serveDriver } from './serve-driver.use-case.js';
2
+ export type { DriverIdentity } from './serve-driver.use-case.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,17 @@
1
+ import type { Readable, Writable } from 'node:stream';
2
+ /** What the driver says about itself in `initialize`. */
3
+ export interface DriverIdentity {
4
+ driverVersion: string;
5
+ }
6
+ /**
7
+ * Serves the driver protocol on a stream pair until the client closes its end or asks for `shutdown`.
8
+ * Every method but `initialize` is refused until the client has initialized with a protocol of the same
9
+ * major. On close, every run still going is closed.
10
+ *
11
+ * @param input - Where the client's messages arrive (stdin).
12
+ * @param output - Where the driver's go (stdout). Nothing else may write to it.
13
+ * @param identity - The driver's version, for `initialize`.
14
+ * @returns Settles once the driver has stopped.
15
+ */
16
+ export declare function serveDriver(input: Readable, output: Writable, identity: DriverIdentity): Promise<void>;
17
+ //# sourceMappingURL=serve-driver.use-case.d.ts.map
@@ -0,0 +1,5 @@
1
+ export { runSessions } from './run-session.use-case.js';
2
+ export type { NotifyClient, RunSessions } from './run-session.use-case.js';
3
+ export { validateRecipes, hooksOf, loadRecipes, sourceOf } from './recipe-loading.use-case.js';
4
+ export { streamingSink } from './streaming-sink.repository.js';
5
+ //# sourceMappingURL=index.d.ts.map