@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.
Files changed (57) hide show
  1. package/dist/config-schema.d.ts +354 -0
  2. package/dist/config-schema.d.ts.map +1 -0
  3. package/dist/config-schema.js +83 -0
  4. package/dist/config-schema.js.map +1 -0
  5. package/dist/config.d.ts +107 -0
  6. package/dist/config.d.ts.map +1 -0
  7. package/dist/config.js +5 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/duration.d.ts +39 -0
  10. package/dist/duration.d.ts.map +1 -0
  11. package/dist/duration.js +111 -0
  12. package/dist/duration.js.map +1 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +20 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/locator-object.d.ts +556 -0
  18. package/dist/locator-object.d.ts.map +1 -0
  19. package/dist/locator-object.js +114 -0
  20. package/dist/locator-object.js.map +1 -0
  21. package/dist/lockfile.d.ts +1501 -0
  22. package/dist/lockfile.d.ts.map +1 -0
  23. package/dist/lockfile.js +86 -0
  24. package/dist/lockfile.js.map +1 -0
  25. package/dist/run-store.d.ts +81 -0
  26. package/dist/run-store.d.ts.map +1 -0
  27. package/dist/run-store.js +181 -0
  28. package/dist/run-store.js.map +1 -0
  29. package/dist/spec-loader.d.ts +17 -0
  30. package/dist/spec-loader.d.ts.map +1 -0
  31. package/dist/spec-loader.js +37 -0
  32. package/dist/spec-loader.js.map +1 -0
  33. package/dist/spec-schema.d.ts +1411 -0
  34. package/dist/spec-schema.d.ts.map +1 -0
  35. package/dist/spec-schema.js +239 -0
  36. package/dist/spec-schema.js.map +1 -0
  37. package/package.json +45 -0
  38. package/schemas/browserflow.schema.json +220 -0
  39. package/schemas/lockfile.schema.json +568 -0
  40. package/schemas/spec-v2.schema.json +413 -0
  41. package/src/config-schema.test.ts +190 -0
  42. package/src/config-schema.ts +111 -0
  43. package/src/config.ts +112 -0
  44. package/src/duration.test.ts +175 -0
  45. package/src/duration.ts +128 -0
  46. package/src/index.ts +136 -0
  47. package/src/json-schemas.test.ts +374 -0
  48. package/src/locator-object.test.ts +323 -0
  49. package/src/locator-object.ts +250 -0
  50. package/src/lockfile.test.ts +345 -0
  51. package/src/lockfile.ts +209 -0
  52. package/src/run-store.test.ts +232 -0
  53. package/src/run-store.ts +240 -0
  54. package/src/spec-loader.test.ts +181 -0
  55. package/src/spec-loader.ts +47 -0
  56. package/src/spec-schema.test.ts +360 -0
  57. 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
+ }