@browserflow-ai/core 0.0.6
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/dist/config-schema.d.ts +354 -0
- package/dist/config-schema.d.ts.map +1 -0
- package/dist/config-schema.js +83 -0
- package/dist/config-schema.js.map +1 -0
- package/dist/config.d.ts +107 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -0
- package/dist/duration.d.ts +39 -0
- package/dist/duration.d.ts.map +1 -0
- package/dist/duration.js +111 -0
- package/dist/duration.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/locator-object.d.ts +556 -0
- package/dist/locator-object.d.ts.map +1 -0
- package/dist/locator-object.js +114 -0
- package/dist/locator-object.js.map +1 -0
- package/dist/lockfile.d.ts +1501 -0
- package/dist/lockfile.d.ts.map +1 -0
- package/dist/lockfile.js +86 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/run-store.d.ts +81 -0
- package/dist/run-store.d.ts.map +1 -0
- package/dist/run-store.js +181 -0
- package/dist/run-store.js.map +1 -0
- package/dist/spec-loader.d.ts +17 -0
- package/dist/spec-loader.d.ts.map +1 -0
- package/dist/spec-loader.js +37 -0
- package/dist/spec-loader.js.map +1 -0
- package/dist/spec-schema.d.ts +1411 -0
- package/dist/spec-schema.d.ts.map +1 -0
- package/dist/spec-schema.js +239 -0
- package/dist/spec-schema.js.map +1 -0
- package/package.json +45 -0
- package/schemas/browserflow.schema.json +220 -0
- package/schemas/lockfile.schema.json +568 -0
- package/schemas/spec-v2.schema.json +413 -0
- package/src/config-schema.test.ts +190 -0
- package/src/config-schema.ts +111 -0
- package/src/config.ts +112 -0
- package/src/duration.test.ts +175 -0
- package/src/duration.ts +128 -0
- package/src/index.ts +136 -0
- package/src/json-schemas.test.ts +374 -0
- package/src/locator-object.test.ts +323 -0
- package/src/locator-object.ts +250 -0
- package/src/lockfile.test.ts +345 -0
- package/src/lockfile.ts +209 -0
- package/src/run-store.test.ts +232 -0
- package/src/run-store.ts +240 -0
- package/src/spec-loader.test.ts +181 -0
- package/src/spec-loader.ts +47 -0
- package/src/spec-schema.test.ts +360 -0
- package/src/spec-schema.ts +347 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for BrowserFlow spec v2
|
|
3
|
+
*
|
|
4
|
+
* @see bf-dgs for implementation task
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { isValidDuration } from './duration.js';
|
|
9
|
+
|
|
10
|
+
// Duration validation
|
|
11
|
+
export const durationSchema = z.string().refine(isValidDuration, {
|
|
12
|
+
message: 'Must be a valid duration string like "3s", "2m", "500ms", or "1m30s"',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Target object - at least one locator strategy required
|
|
16
|
+
export const targetSchema: z.ZodType<Target> = z.lazy(() =>
|
|
17
|
+
z
|
|
18
|
+
.object({
|
|
19
|
+
query: z.string().optional(),
|
|
20
|
+
testid: z.string().optional(),
|
|
21
|
+
role: z.string().optional(),
|
|
22
|
+
name: z.string().optional(),
|
|
23
|
+
label: z.string().optional(),
|
|
24
|
+
placeholder: z.string().optional(),
|
|
25
|
+
text: z.string().optional(),
|
|
26
|
+
css: z.string().optional(),
|
|
27
|
+
within: targetSchema.optional(),
|
|
28
|
+
nth: z.number().int().optional(),
|
|
29
|
+
})
|
|
30
|
+
.refine(
|
|
31
|
+
(data) =>
|
|
32
|
+
data.query ||
|
|
33
|
+
data.testid ||
|
|
34
|
+
data.role ||
|
|
35
|
+
data.label ||
|
|
36
|
+
data.placeholder ||
|
|
37
|
+
data.text ||
|
|
38
|
+
data.css,
|
|
39
|
+
'Target must have at least one locator strategy (query, testid, role, label, placeholder, text, or css)'
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Action types from spec section 6.4
|
|
44
|
+
export const actionTypeSchema = z.enum([
|
|
45
|
+
'click',
|
|
46
|
+
'navigate',
|
|
47
|
+
'back',
|
|
48
|
+
'forward',
|
|
49
|
+
'refresh',
|
|
50
|
+
'reload',
|
|
51
|
+
'fill',
|
|
52
|
+
'type',
|
|
53
|
+
'select',
|
|
54
|
+
'check',
|
|
55
|
+
'press',
|
|
56
|
+
'upload',
|
|
57
|
+
'wait',
|
|
58
|
+
'expect',
|
|
59
|
+
'screenshot',
|
|
60
|
+
'scroll',
|
|
61
|
+
'scroll_into_view',
|
|
62
|
+
'verify_state',
|
|
63
|
+
'identify_element',
|
|
64
|
+
'ai_verify',
|
|
65
|
+
'custom',
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
// State values for expect action
|
|
69
|
+
export const stateSchema = z.enum([
|
|
70
|
+
'visible',
|
|
71
|
+
'hidden',
|
|
72
|
+
'enabled',
|
|
73
|
+
'disabled',
|
|
74
|
+
'checked',
|
|
75
|
+
'unchecked',
|
|
76
|
+
'focused',
|
|
77
|
+
'editable',
|
|
78
|
+
'attached',
|
|
79
|
+
'detached',
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
// Step schema - id is REQUIRED
|
|
83
|
+
export const stepSchema = z
|
|
84
|
+
.object({
|
|
85
|
+
id: z.string().min(1, 'Step id is required'),
|
|
86
|
+
action: actionTypeSchema,
|
|
87
|
+
name: z.string().optional(), // 1-4 word display name for UI
|
|
88
|
+
description: z.string().optional(),
|
|
89
|
+
why: z.string().optional(), // Rationale for this step
|
|
90
|
+
target: targetSchema.optional(),
|
|
91
|
+
// Navigate action
|
|
92
|
+
url: z.string().optional(),
|
|
93
|
+
// Fill/type action
|
|
94
|
+
value: z.string().optional(),
|
|
95
|
+
text: z.string().optional(),
|
|
96
|
+
// Select action
|
|
97
|
+
option: z.string().optional(),
|
|
98
|
+
// Check action
|
|
99
|
+
checked: z.boolean().optional(),
|
|
100
|
+
// Wait action
|
|
101
|
+
duration: durationSchema.optional(),
|
|
102
|
+
// Expect action
|
|
103
|
+
state: stateSchema.optional(),
|
|
104
|
+
// Timeout override
|
|
105
|
+
timeout: durationSchema.optional(),
|
|
106
|
+
// Keyboard
|
|
107
|
+
pressEnter: z.boolean().optional(),
|
|
108
|
+
// Verify state
|
|
109
|
+
checks: z
|
|
110
|
+
.array(
|
|
111
|
+
z.object({
|
|
112
|
+
element_visible: z.string().optional(),
|
|
113
|
+
element_not_visible: z.string().optional(),
|
|
114
|
+
text_contains: z.string().optional(),
|
|
115
|
+
text_not_contains: z.string().optional(),
|
|
116
|
+
url_contains: z.string().optional(),
|
|
117
|
+
element_count: z
|
|
118
|
+
.object({
|
|
119
|
+
selector: z.string(),
|
|
120
|
+
expected: z.number(),
|
|
121
|
+
})
|
|
122
|
+
.optional(),
|
|
123
|
+
attribute: z
|
|
124
|
+
.object({
|
|
125
|
+
selector: z.string(),
|
|
126
|
+
attribute: z.string(),
|
|
127
|
+
equals: z.string(),
|
|
128
|
+
})
|
|
129
|
+
.optional(),
|
|
130
|
+
})
|
|
131
|
+
)
|
|
132
|
+
.optional(),
|
|
133
|
+
// Screenshot options
|
|
134
|
+
highlight: z
|
|
135
|
+
.array(
|
|
136
|
+
z.object({
|
|
137
|
+
selector: z.string(),
|
|
138
|
+
label: z.string().optional(),
|
|
139
|
+
})
|
|
140
|
+
)
|
|
141
|
+
.optional(),
|
|
142
|
+
mask: z
|
|
143
|
+
.array(
|
|
144
|
+
z.object({
|
|
145
|
+
selector: z.string().optional(),
|
|
146
|
+
region: z
|
|
147
|
+
.object({
|
|
148
|
+
x: z.number(),
|
|
149
|
+
y: z.number(),
|
|
150
|
+
width: z.number(),
|
|
151
|
+
height: z.number(),
|
|
152
|
+
})
|
|
153
|
+
.optional(),
|
|
154
|
+
reason: z.string().optional(),
|
|
155
|
+
})
|
|
156
|
+
)
|
|
157
|
+
.optional(),
|
|
158
|
+
// AI verify
|
|
159
|
+
question: z.string().optional(),
|
|
160
|
+
expected: z.union([z.boolean(), z.string(), z.number()]).optional(),
|
|
161
|
+
// Custom action
|
|
162
|
+
save_as: z.string().optional(),
|
|
163
|
+
ref: z.string().optional(),
|
|
164
|
+
selector: z.string().optional(),
|
|
165
|
+
for: z.enum(['element', 'text', 'url', 'time']).optional(),
|
|
166
|
+
contains: z.string().optional(),
|
|
167
|
+
})
|
|
168
|
+
.strict();
|
|
169
|
+
|
|
170
|
+
// Preconditions schema
|
|
171
|
+
export const preconditionsSchema = z.object({
|
|
172
|
+
page: z
|
|
173
|
+
.object({
|
|
174
|
+
url: z.string().optional(),
|
|
175
|
+
})
|
|
176
|
+
.optional(),
|
|
177
|
+
auth: z
|
|
178
|
+
.object({
|
|
179
|
+
user: z.string().optional(),
|
|
180
|
+
state: z.string().optional(),
|
|
181
|
+
})
|
|
182
|
+
.optional(),
|
|
183
|
+
viewport: z
|
|
184
|
+
.object({
|
|
185
|
+
width: z.number(),
|
|
186
|
+
height: z.number(),
|
|
187
|
+
})
|
|
188
|
+
.optional(),
|
|
189
|
+
mocks: z
|
|
190
|
+
.array(
|
|
191
|
+
z.object({
|
|
192
|
+
url: z.string(),
|
|
193
|
+
response: z.unknown(),
|
|
194
|
+
})
|
|
195
|
+
)
|
|
196
|
+
.optional(),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Expected outcome schema
|
|
200
|
+
export const expectedOutcomeSchema = z.object({
|
|
201
|
+
description: z.string().optional(),
|
|
202
|
+
check: z.string().optional(),
|
|
203
|
+
expected: z.union([z.boolean(), z.number(), z.string()]).optional(),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Top-level spec schema
|
|
207
|
+
export const specSchema = z
|
|
208
|
+
.object({
|
|
209
|
+
version: z.literal(2),
|
|
210
|
+
name: z.string().regex(/^[a-z0-9-]+$/, 'Name must be kebab-case (lowercase letters, numbers, and hyphens only)'),
|
|
211
|
+
description: z.string().optional(),
|
|
212
|
+
steps: z.array(stepSchema).min(1, 'At least one step required'),
|
|
213
|
+
timeout: durationSchema.optional(),
|
|
214
|
+
priority: z.enum(['critical', 'high', 'normal', 'low']).optional(),
|
|
215
|
+
tags: z.array(z.string()).optional(),
|
|
216
|
+
preconditions: preconditionsSchema.optional(),
|
|
217
|
+
expected_outcomes: z.array(expectedOutcomeSchema).optional(),
|
|
218
|
+
})
|
|
219
|
+
.refine(
|
|
220
|
+
(data) => {
|
|
221
|
+
const ids = data.steps.map((s) => s.id);
|
|
222
|
+
return new Set(ids).size === ids.length;
|
|
223
|
+
},
|
|
224
|
+
{ message: 'Step IDs must be unique within spec' }
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// Type definitions
|
|
228
|
+
export type Target = {
|
|
229
|
+
query?: string;
|
|
230
|
+
testid?: string;
|
|
231
|
+
role?: string;
|
|
232
|
+
name?: string;
|
|
233
|
+
label?: string;
|
|
234
|
+
placeholder?: string;
|
|
235
|
+
text?: string;
|
|
236
|
+
css?: string;
|
|
237
|
+
within?: Target;
|
|
238
|
+
nth?: number;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export type ActionType = z.infer<typeof actionTypeSchema>;
|
|
242
|
+
export type State = z.infer<typeof stateSchema>;
|
|
243
|
+
export type SpecStep = z.infer<typeof stepSchema>;
|
|
244
|
+
export type Preconditions = z.infer<typeof preconditionsSchema>;
|
|
245
|
+
export type ExpectedOutcome = z.infer<typeof expectedOutcomeSchema>;
|
|
246
|
+
export type BrowserFlowSpec = z.infer<typeof specSchema>;
|
|
247
|
+
|
|
248
|
+
// Legacy exports for backwards compatibility
|
|
249
|
+
export const verifyCheckSchema = z.object({
|
|
250
|
+
element_visible: z.string().optional(),
|
|
251
|
+
element_not_visible: z.string().optional(),
|
|
252
|
+
text_contains: z.string().optional(),
|
|
253
|
+
text_not_contains: z.string().optional(),
|
|
254
|
+
url_contains: z.string().optional(),
|
|
255
|
+
element_count: z
|
|
256
|
+
.object({
|
|
257
|
+
selector: z.string(),
|
|
258
|
+
expected: z.number(),
|
|
259
|
+
})
|
|
260
|
+
.optional(),
|
|
261
|
+
attribute: z
|
|
262
|
+
.object({
|
|
263
|
+
selector: z.string(),
|
|
264
|
+
attribute: z.string(),
|
|
265
|
+
equals: z.string(),
|
|
266
|
+
})
|
|
267
|
+
.optional(),
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
export const highlightRegionSchema = z.object({
|
|
271
|
+
selector: z.string(),
|
|
272
|
+
label: z.string().optional(),
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
export const maskRegionSchema = z.object({
|
|
276
|
+
selector: z.string().optional(),
|
|
277
|
+
region: z
|
|
278
|
+
.object({
|
|
279
|
+
x: z.number(),
|
|
280
|
+
y: z.number(),
|
|
281
|
+
width: z.number(),
|
|
282
|
+
height: z.number(),
|
|
283
|
+
})
|
|
284
|
+
.optional(),
|
|
285
|
+
reason: z.string().optional(),
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Legacy type exports
|
|
289
|
+
export type VerifyCheck = z.infer<typeof verifyCheckSchema>;
|
|
290
|
+
export type HighlightRegion = z.infer<typeof highlightRegionSchema>;
|
|
291
|
+
export type MaskRegion = z.infer<typeof maskRegionSchema>;
|
|
292
|
+
|
|
293
|
+
// Alias for specStepSchema (legacy)
|
|
294
|
+
export const specStepSchema = stepSchema;
|
|
295
|
+
|
|
296
|
+
// Legacy SpecStep type (for backwards compatibility with generator)
|
|
297
|
+
export interface LegacySpecStep {
|
|
298
|
+
id?: string;
|
|
299
|
+
action: ActionType;
|
|
300
|
+
name?: string; // 1-4 word display name for UI
|
|
301
|
+
description?: string;
|
|
302
|
+
why?: string; // Rationale for this step
|
|
303
|
+
// Click/fill/type - query-based targeting
|
|
304
|
+
query?: string;
|
|
305
|
+
// Navigate action
|
|
306
|
+
to?: string;
|
|
307
|
+
// Fill action
|
|
308
|
+
value?: string;
|
|
309
|
+
// Wait action
|
|
310
|
+
for?: 'element' | 'text' | 'url' | 'time';
|
|
311
|
+
text?: string;
|
|
312
|
+
duration?: string | number;
|
|
313
|
+
contains?: string;
|
|
314
|
+
// Select action
|
|
315
|
+
option?: string;
|
|
316
|
+
// Check action
|
|
317
|
+
checked?: boolean;
|
|
318
|
+
// Screenshot action - uses top-level 'name' field
|
|
319
|
+
mask?: Array<{
|
|
320
|
+
selector?: string;
|
|
321
|
+
region?: { x: number; y: number; width: number; height: number };
|
|
322
|
+
reason?: string;
|
|
323
|
+
}>;
|
|
324
|
+
// Verify state action
|
|
325
|
+
checks?: Array<{
|
|
326
|
+
element_visible?: string;
|
|
327
|
+
element_not_visible?: string;
|
|
328
|
+
text_contains?: string;
|
|
329
|
+
text_not_contains?: string;
|
|
330
|
+
url_contains?: string;
|
|
331
|
+
element_count?: { selector: string; expected: number };
|
|
332
|
+
attribute?: { selector: string; attribute: string; equals: string };
|
|
333
|
+
}>;
|
|
334
|
+
// AI verify
|
|
335
|
+
question?: string;
|
|
336
|
+
expected?: boolean | string | number;
|
|
337
|
+
// Custom action
|
|
338
|
+
save_as?: string;
|
|
339
|
+
// Element reference
|
|
340
|
+
ref?: string;
|
|
341
|
+
// Legacy selector
|
|
342
|
+
selector?: string;
|
|
343
|
+
// Timeout override
|
|
344
|
+
timeout?: string;
|
|
345
|
+
// Keyboard
|
|
346
|
+
pressEnter?: boolean;
|
|
347
|
+
}
|