@automate.ax/integration-contracts 0.152.4 → 0.153.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,466 @@
1
+ import type { Encodable } from "@automate.ax/codec"
2
+ import * as z from "zod"
3
+
4
+ const RESOURCE_TYPE_SCHEMA = z.enum([
5
+ "document",
6
+ "stylesheet",
7
+ "image",
8
+ "media",
9
+ "font",
10
+ "script",
11
+ "texttrack",
12
+ "xhr",
13
+ "fetch",
14
+ "prefetch",
15
+ "eventsource",
16
+ "websocket",
17
+ "manifest",
18
+ "signedexchange",
19
+ "ping",
20
+ "cspviolationreport",
21
+ "preflight",
22
+ "other",
23
+ ])
24
+ const SCRIPT_TAG_SCHEMA = z.object({
25
+ content: z.string().optional(),
26
+ id: z.string().optional(),
27
+ type: z.string().optional(),
28
+ url: z.url().optional(),
29
+ })
30
+ const STYLE_TAG_SCHEMA = z.object({
31
+ content: z.string().optional(),
32
+ url: z.url().optional(),
33
+ })
34
+ const COOKIE_SCHEMA = z.object({
35
+ domain: z.string().optional(),
36
+ expires: z.number().optional(),
37
+ httpOnly: z.boolean().optional(),
38
+ name: z.string().min(1),
39
+ partitionKey: z.string().optional(),
40
+ path: z.string().optional(),
41
+ priority: z.enum(["Low", "Medium", "High"]).optional(),
42
+ sameParty: z.boolean().optional(),
43
+ sameSite: z.enum(["Strict", "Lax", "None"]).optional(),
44
+ secure: z.boolean().optional(),
45
+ sourcePort: z.number().int().min(0).max(65_535).optional(),
46
+ sourceScheme: z.enum(["Unset", "NonSecure", "Secure"]).optional(),
47
+ url: z.string().optional(),
48
+ value: z.string(),
49
+ })
50
+ const WAIT_UNTIL_SCHEMA = z.enum([
51
+ "load",
52
+ "domcontentloaded",
53
+ "networkidle0",
54
+ "networkidle2",
55
+ ])
56
+ const GOTO_OPTIONS_SCHEMA = z.object({
57
+ referer: z.string().optional(),
58
+ referrerPolicy: z.string().optional(),
59
+ timeout: z.number().min(0).max(60_000).optional(),
60
+ waitUntil: z
61
+ .union([WAIT_UNTIL_SCHEMA, WAIT_UNTIL_SCHEMA.array().min(1)])
62
+ .optional(),
63
+ })
64
+ const VIEWPORT_SCHEMA = z.object({
65
+ deviceScaleFactor: z.number().positive().optional(),
66
+ hasTouch: z.boolean().optional(),
67
+ height: z.number().positive(),
68
+ isLandscape: z.boolean().optional(),
69
+ isMobile: z.boolean().optional(),
70
+ width: z.number().positive(),
71
+ })
72
+ const WAIT_FOR_SELECTOR_SCHEMA = z
73
+ .object({
74
+ hidden: z.literal(true).optional(),
75
+ selector: z.string().min(1),
76
+ timeout: z.number().min(0).max(120_000).optional(),
77
+ visible: z.literal(true).optional(),
78
+ })
79
+ .refine(({ hidden, visible }) => !(hidden && visible), {
80
+ message: "Choose hidden or visible, not both.",
81
+ })
82
+ const SCREENSHOT_OPTIONS_SCHEMA = z
83
+ .object({
84
+ captureBeyondViewport: z.boolean().optional(),
85
+ clip: z
86
+ .object({
87
+ height: z.number().positive(),
88
+ scale: z.number().positive().optional(),
89
+ width: z.number().positive(),
90
+ x: z.number(),
91
+ y: z.number(),
92
+ })
93
+ .optional(),
94
+ fromSurface: z.boolean().optional(),
95
+ fullPage: z.boolean().optional(),
96
+ omitBackground: z.boolean().optional(),
97
+ optimizeForSpeed: z.boolean().optional(),
98
+ quality: z.number().min(0).max(100).optional(),
99
+ type: z.enum(["png", "jpeg", "webp"]).optional(),
100
+ })
101
+ .refine(({ quality, type }) => quality === undefined || type !== "png", {
102
+ message: "Screenshot quality requires jpeg or webp output.",
103
+ path: ["quality"],
104
+ })
105
+ const PDF_DIMENSION_SCHEMA = z.union([z.number().positive(), z.string().min(1)])
106
+ const PDF_OPTIONS_SCHEMA = z.object({
107
+ displayHeaderFooter: z.boolean().optional(),
108
+ footerTemplate: z.string().optional(),
109
+ format: z
110
+ .enum([
111
+ "letter",
112
+ "legal",
113
+ "tabloid",
114
+ "ledger",
115
+ "a0",
116
+ "a1",
117
+ "a2",
118
+ "a3",
119
+ "a4",
120
+ "a5",
121
+ "a6",
122
+ ])
123
+ .optional(),
124
+ headerTemplate: z.string().optional(),
125
+ height: PDF_DIMENSION_SCHEMA.optional(),
126
+ landscape: z.boolean().optional(),
127
+ margin: z
128
+ .object({
129
+ bottom: PDF_DIMENSION_SCHEMA.optional(),
130
+ left: PDF_DIMENSION_SCHEMA.optional(),
131
+ right: PDF_DIMENSION_SCHEMA.optional(),
132
+ top: PDF_DIMENSION_SCHEMA.optional(),
133
+ })
134
+ .optional(),
135
+ omitBackground: z.boolean().optional(),
136
+ outline: z.boolean().optional(),
137
+ pageRanges: z.string().optional(),
138
+ preferCSSPageSize: z.boolean().optional(),
139
+ printBackground: z.boolean().optional(),
140
+ scale: z.number().min(0.1).max(2).optional(),
141
+ tagged: z.boolean().optional(),
142
+ timeout: z.number().min(0).optional(),
143
+ width: PDF_DIMENSION_SCHEMA.optional(),
144
+ })
145
+ const JSON_EXTRACTION_OPTIONS = {
146
+ customAi: z
147
+ .object({
148
+ authorization: z.string().min(1).optional(),
149
+ model: z.string().min(1),
150
+ })
151
+ .array()
152
+ .min(1)
153
+ .optional(),
154
+ prompt: z.string().min(1).optional(),
155
+ responseFormat: z
156
+ .object({
157
+ jsonSchema: z.record(z.string(), z.json()).nullable().optional(),
158
+ type: z.string().min(1),
159
+ })
160
+ .optional(),
161
+ }
162
+ const COMMON_RENDERING_FIELDS = {
163
+ accountId: z.string().trim().min(1),
164
+ actionTimeout: z.number().min(0).max(120_000).optional(),
165
+ addScriptTag: SCRIPT_TAG_SCHEMA.array().optional(),
166
+ addStyleTag: STYLE_TAG_SCHEMA.array().optional(),
167
+ allowRequestPattern: z.string().min(1).array().min(1).optional(),
168
+ allowResourceTypes: RESOURCE_TYPE_SCHEMA.array().min(1).optional(),
169
+ authenticate: z
170
+ .object({ password: z.string().min(1), username: z.string().min(1) })
171
+ .optional(),
172
+ bestAttempt: z.boolean().optional(),
173
+ cacheTtl: z.number().int().min(0).max(86_400).optional(),
174
+ cookies: COOKIE_SCHEMA.array().optional(),
175
+ emulateMediaType: z.string().optional(),
176
+ gotoOptions: GOTO_OPTIONS_SCHEMA.optional(),
177
+ rejectRequestPattern: z.string().min(1).array().min(1).optional(),
178
+ rejectResourceTypes: RESOURCE_TYPE_SCHEMA.array().min(1).optional(),
179
+ setExtraHttpHeaders: z.record(z.string(), z.string()).optional(),
180
+ setJavaScriptEnabled: z.boolean().optional(),
181
+ viewport: VIEWPORT_SCHEMA.optional(),
182
+ waitForSelector: WAIT_FOR_SELECTOR_SCHEMA.optional(),
183
+ waitForTimeout: z.number().min(0).max(120_000).optional(),
184
+ }
185
+ const SOURCE_FIELDS = {
186
+ html: z.string().min(1).optional(),
187
+ url: z.url().optional(),
188
+ }
189
+ const QUICK_ACTION_RENDERING_FIELDS = {
190
+ ...COMMON_RENDERING_FIELDS,
191
+ userAgent: z.string().optional(),
192
+ }
193
+
194
+ /** Recursive accessibility node returned by Cloudflare Browser Run. */
195
+ export interface CloudflareAccessibilityNode extends Record<string, Encodable> {
196
+ autocomplete?: string
197
+ checked?: boolean | "mixed"
198
+ children?: CloudflareAccessibilityNode[]
199
+ description?: string
200
+ disabled?: boolean
201
+ expanded?: boolean
202
+ focused?: boolean
203
+ haspopup?: string
204
+ invalid?: string
205
+ keyshortcuts?: string
206
+ level?: number
207
+ modal?: boolean
208
+ multiline?: boolean
209
+ multiselectable?: boolean
210
+ name?: string
211
+ orientation?: string
212
+ pressed?: boolean | "mixed"
213
+ readonly?: boolean
214
+ required?: boolean
215
+ role: string
216
+ roledescription?: string
217
+ selected?: boolean
218
+ value?: number | string
219
+ valuemax?: number
220
+ valuemin?: number
221
+ valuetext?: string
222
+ }
223
+
224
+ export const CLOUDFLARE_ACCESSIBILITY_NODE_SCHEMA: z.ZodType<CloudflareAccessibilityNode> =
225
+ z.lazy(() =>
226
+ z.object({
227
+ autocomplete: z.string().optional(),
228
+ checked: z.union([z.boolean(), z.literal("mixed")]).optional(),
229
+ children: CLOUDFLARE_ACCESSIBILITY_NODE_SCHEMA.array().optional(),
230
+ description: z.string().optional(),
231
+ disabled: z.boolean().optional(),
232
+ expanded: z.boolean().optional(),
233
+ focused: z.boolean().optional(),
234
+ haspopup: z.string().optional(),
235
+ invalid: z.string().optional(),
236
+ keyshortcuts: z.string().optional(),
237
+ level: z.number().optional(),
238
+ modal: z.boolean().optional(),
239
+ multiline: z.boolean().optional(),
240
+ multiselectable: z.boolean().optional(),
241
+ name: z.string().optional(),
242
+ orientation: z.string().optional(),
243
+ pressed: z.union([z.boolean(), z.literal("mixed")]).optional(),
244
+ readonly: z.boolean().optional(),
245
+ required: z.boolean().optional(),
246
+ role: z.string(),
247
+ roledescription: z.string().optional(),
248
+ selected: z.boolean().optional(),
249
+ value: z.union([z.number(), z.string()]).optional(),
250
+ valuemax: z.number().optional(),
251
+ valuemin: z.number().optional(),
252
+ valuetext: z.string().optional(),
253
+ }),
254
+ )
255
+
256
+ export const CLOUDFLARE_BROWSER_CONTENT_INPUT_SCHEMA = browserRenderingInput({})
257
+ export const CLOUDFLARE_BROWSER_MARKDOWN_INPUT_SCHEMA = browserRenderingInput(
258
+ {},
259
+ )
260
+ export const CLOUDFLARE_BROWSER_ACCESSIBILITY_INPUT_SCHEMA =
261
+ browserRenderingInput({
262
+ interestingOnly: z.boolean().optional(),
263
+ root: z.string().optional(),
264
+ })
265
+ export const CLOUDFLARE_BROWSER_LINKS_INPUT_SCHEMA = browserRenderingInput({
266
+ excludeExternalLinks: z.boolean().optional(),
267
+ visibleLinksOnly: z.boolean().optional(),
268
+ })
269
+ export const CLOUDFLARE_BROWSER_SCRAPE_INPUT_SCHEMA = browserRenderingInput({
270
+ elements: z
271
+ .object({ selector: z.string().min(1) })
272
+ .array()
273
+ .min(1),
274
+ })
275
+ export const CLOUDFLARE_BROWSER_SCREENSHOT_INPUT_SCHEMA = browserRenderingInput(
276
+ {
277
+ screenshotOptions: SCREENSHOT_OPTIONS_SCHEMA.optional(),
278
+ scrollPage: z.boolean().optional(),
279
+ selector: z.string().min(1).optional(),
280
+ },
281
+ )
282
+ export const CLOUDFLARE_BROWSER_PDF_INPUT_SCHEMA = browserRenderingInput({
283
+ pdfOptions: PDF_OPTIONS_SCHEMA.optional(),
284
+ })
285
+ export const CLOUDFLARE_BROWSER_SNAPSHOT_INPUT_SCHEMA = browserRenderingInput({
286
+ formats: z
287
+ .enum(["content", "screenshot", "markdown", "accessibilityTree"])
288
+ .array()
289
+ .min(2)
290
+ .refine((formats) => new Set(formats).size === formats.length, {
291
+ message: "Choose each snapshot format once.",
292
+ })
293
+ .optional(),
294
+ screenshotOptions: SCREENSHOT_OPTIONS_SCHEMA.optional(),
295
+ })
296
+ export const CLOUDFLARE_BROWSER_JSON_INPUT_SCHEMA = browserRenderingInput(
297
+ JSON_EXTRACTION_OPTIONS,
298
+ ).superRefine(({ prompt, responseFormat }, context) => {
299
+ if (!prompt && !responseFormat)
300
+ context.addIssue({
301
+ code: "custom",
302
+ message: "Provide prompt or responseFormat.",
303
+ })
304
+ })
305
+
306
+ export const CLOUDFLARE_BROWSER_SCRAPE_RESULT_SCHEMA = z.object({
307
+ results: z.object({
308
+ attributes: z.object({ name: z.string(), value: z.string() }).array(),
309
+ height: z.number(),
310
+ html: z.string(),
311
+ left: z.number(),
312
+ text: z.string(),
313
+ top: z.number(),
314
+ width: z.number(),
315
+ }),
316
+ selector: z.string(),
317
+ })
318
+ export const CLOUDFLARE_BROWSER_SNAPSHOT_RESULT_SCHEMA = z.object({
319
+ accessibilityTree: CLOUDFLARE_ACCESSIBILITY_NODE_SCHEMA.optional(),
320
+ content: z.string().optional(),
321
+ markdown: z.string().optional(),
322
+ screenshot: z.base64().optional(),
323
+ })
324
+
325
+ const CRAWL_STATUS_SCHEMA = z.enum([
326
+ "queued",
327
+ "errored",
328
+ "completed",
329
+ "disallowed",
330
+ "skipped",
331
+ "cancelled",
332
+ ])
333
+ const CRAWL_OPTIONS_SCHEMA = z.object({
334
+ excludePatterns: z.string().min(1).array().optional(),
335
+ includeExternalLinks: z.boolean().optional(),
336
+ includePatterns: z.string().min(1).array().optional(),
337
+ includeSubdomains: z.boolean().optional(),
338
+ })
339
+ const CRAWL_FIELDS = {
340
+ contentUse: z.enum(["reference", "full"]).optional(),
341
+ crawlPurposes: z
342
+ .enum(["search", "ai-input", "ai-train"])
343
+ .array()
344
+ .min(1)
345
+ .max(3)
346
+ .refine((purposes) => new Set(purposes).size === purposes.length, {
347
+ message: "Choose each crawl purpose once.",
348
+ })
349
+ .optional(),
350
+ depth: z.number().int().min(1).max(100_000).optional(),
351
+ formats: z
352
+ .enum(["html", "markdown", "json"])
353
+ .array()
354
+ .min(1)
355
+ .refine((formats) => new Set(formats).size === formats.length, {
356
+ message: "Choose each crawl format once.",
357
+ })
358
+ .optional(),
359
+ jsonOptions: z.object(JSON_EXTRACTION_OPTIONS).optional(),
360
+ limit: z.number().int().min(1).max(100_000).optional(),
361
+ maxAge: z.number().int().min(0).max(604_800).optional(),
362
+ modifiedSince: z.number().int().nonnegative().optional(),
363
+ options: CRAWL_OPTIONS_SCHEMA.optional(),
364
+ source: z.enum(["sitemaps", "links", "all"]).optional(),
365
+ url: z.url(),
366
+ }
367
+ export const CLOUDFLARE_BROWSER_CREATE_CRAWL_INPUT_SCHEMA = z
368
+ .object({
369
+ ...COMMON_RENDERING_FIELDS,
370
+ ...CRAWL_FIELDS,
371
+ render: z.boolean().prefault(true),
372
+ })
373
+ .superRefine((input, context) => {
374
+ if (
375
+ !input.render &&
376
+ (input.actionTimeout !== undefined ||
377
+ input.addScriptTag !== undefined ||
378
+ input.addStyleTag !== undefined ||
379
+ input.allowRequestPattern !== undefined ||
380
+ input.allowResourceTypes !== undefined ||
381
+ input.authenticate !== undefined ||
382
+ input.bestAttempt !== undefined ||
383
+ input.cookies !== undefined ||
384
+ input.emulateMediaType !== undefined ||
385
+ input.gotoOptions !== undefined ||
386
+ input.rejectRequestPattern !== undefined ||
387
+ input.rejectResourceTypes !== undefined ||
388
+ input.setExtraHttpHeaders !== undefined ||
389
+ input.setJavaScriptEnabled !== undefined ||
390
+ input.viewport !== undefined ||
391
+ input.waitForSelector !== undefined ||
392
+ input.waitForTimeout !== undefined)
393
+ )
394
+ context.addIssue({
395
+ code: "custom",
396
+ message: "Rendered-page options require render: true.",
397
+ path: ["render"],
398
+ })
399
+ })
400
+ export const CLOUDFLARE_BROWSER_GET_CRAWL_INPUT_SCHEMA = z.object({
401
+ accountId: z.string().trim().min(1),
402
+ cacheTtl: z.number().int().min(0).max(86_400).optional(),
403
+ cursor: z.number().int().nonnegative().optional(),
404
+ jobId: z.string().trim().min(1),
405
+ limit: z.number().int().positive().optional(),
406
+ status: CRAWL_STATUS_SCHEMA.optional(),
407
+ })
408
+ export const CLOUDFLARE_BROWSER_CANCEL_CRAWL_INPUT_SCHEMA = z.object({
409
+ accountId: z.string().trim().min(1),
410
+ jobId: z.string().trim().min(1),
411
+ })
412
+ export const CLOUDFLARE_BROWSER_CRAWL_RESULT_SCHEMA = z.object({
413
+ browserSecondsUsed: z.number().nonnegative(),
414
+ cursor: z.string().optional(),
415
+ finished: z.number().int().nonnegative(),
416
+ id: z.string().min(1),
417
+ records: z
418
+ .object({
419
+ html: z.string().optional(),
420
+ json: z.record(z.string(), z.json()).optional(),
421
+ markdown: z.string().optional(),
422
+ metadata: z
423
+ .object({
424
+ status: z.number().int(),
425
+ title: z.string().optional(),
426
+ url: z.string(),
427
+ })
428
+ .optional(),
429
+ status: CRAWL_STATUS_SCHEMA,
430
+ url: z.string(),
431
+ })
432
+ .array(),
433
+ skipped: z.number().int().nonnegative(),
434
+ status: z.string(),
435
+ total: z.number().int().nonnegative(),
436
+ })
437
+ export const CLOUDFLARE_BROWSER_CANCEL_CRAWL_RESULT_SCHEMA = z.object({
438
+ jobId: z.string().min(1),
439
+ message: z.string(),
440
+ })
441
+
442
+ /**
443
+ * Builds a Browser Run input schema that requires exactly one page source.
444
+ *
445
+ * @param fields - Endpoint-specific input fields.
446
+ */
447
+ function browserRenderingInput<TFields extends z.ZodRawShape>(fields: TFields) {
448
+ return z
449
+ .object({ ...QUICK_ACTION_RENDERING_FIELDS, ...SOURCE_FIELDS, ...fields })
450
+ .refine(hasExactlyOnePageSource, {
451
+ message: "Provide exactly one of url or html.",
452
+ })
453
+ }
454
+
455
+ /**
456
+ * Returns whether an input provides exactly one Browser Run page source.
457
+ *
458
+ * @param input - Candidate page source.
459
+ * @param input.html - Inline HTML source.
460
+ * @param input.url - URL source.
461
+ */
462
+ function hasExactlyOnePageSource(input: { html?: unknown; url?: unknown }) {
463
+ return (
464
+ Number(input.html !== undefined) + Number(input.url !== undefined) === 1
465
+ )
466
+ }
@@ -1,3 +1,4 @@
1
1
  export * from "./api"
2
+ export * from "./browser-rendering"
2
3
  export * from "./events"
3
4
  export * from "./schemas"