@kubuild/schema 0.1.0 → 0.3.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.
package/dist/index.js CHANGED
@@ -1,56 +1,369 @@
1
1
  // src/document.ts
2
+ import { z as z3 } from "zod";
3
+
4
+ // src/actions.ts
2
5
  import { z } from "zod";
6
+ var ActionTriggerTypeSchema = z.enum([
7
+ "click",
8
+ "submit",
9
+ "change",
10
+ "blur",
11
+ "focus",
12
+ "load"
13
+ ]);
14
+ var ActionTriggerSchema = ActionTriggerTypeSchema;
15
+ var ActionStepTypeSchema = z.enum([
16
+ "api_request",
17
+ "navigate",
18
+ "set_state",
19
+ "reset_form",
20
+ "show_toast",
21
+ "open_modal",
22
+ "close_modal",
23
+ "copy_clipboard",
24
+ "custom_event"
25
+ ]);
26
+ var ConditionOperatorSchema = z.enum([
27
+ "equals",
28
+ "not_equals",
29
+ "contains",
30
+ "not_contains",
31
+ "is_truthy",
32
+ "is_falsy",
33
+ "gt",
34
+ "gte",
35
+ "lt",
36
+ "lte",
37
+ "regex"
38
+ ]);
39
+ var ActionStepConditionSchema = z.object({
40
+ field: z.string().min(1, "Condition field cannot be empty"),
41
+ operator: ConditionOperatorSchema,
42
+ value: z.unknown().optional()
43
+ });
44
+ var DANGEROUS_URL_PATTERN = /(javascript:|vbscript:|data:text\/html)/i;
45
+ var DANGEROUS_SCRIPT_PATTERN = /<\s*script/i;
46
+ function isSafeActionUrl(url) {
47
+ if (!url || typeof url !== "string") return false;
48
+ const trimmed = url.trim();
49
+ if (DANGEROUS_URL_PATTERN.test(trimmed)) return false;
50
+ if (DANGEROUS_SCRIPT_PATTERN.test(trimmed)) return false;
51
+ return true;
52
+ }
53
+ var SafeUrlStringSchema = z.string().min(1, "URL cannot be empty").refine(isSafeActionUrl, {
54
+ message: "URL contains disallowed or unsafe protocol/script pattern"
55
+ });
56
+ var SafeEventNameSchema = z.string().min(1, "Event name cannot be empty").regex(/^[a-zA-Z0-9_\-:]+$/, {
57
+ message: "Event name must contain only alphanumeric characters, dashes, colons, or underscores"
58
+ });
59
+ var ApiRequestStepPayloadSchema = z.object({
60
+ url: SafeUrlStringSchema,
61
+ method: z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]).default("GET"),
62
+ headers: z.record(z.string(), z.string()).optional(),
63
+ queryParams: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
64
+ body: z.union([z.string(), z.record(z.string(), z.unknown()), z.array(z.unknown())]).optional(),
65
+ bodyFormat: z.enum(["json", "form-data", "formData", "urlencoded", "url-encoded", "raw", "text"]).optional(),
66
+ bodyType: z.enum(["json", "form-data", "formData", "urlencoded", "url-encoded", "raw", "text"]).optional(),
67
+ timeout: z.number().positive("Timeout must be greater than 0 ms").optional(),
68
+ responseMapping: z.record(z.string(), z.string()).optional()
69
+ });
70
+ var NavigateStepPayloadSchema = z.object({
71
+ url: SafeUrlStringSchema,
72
+ target: z.enum(["_self", "_blank", "_parent", "_top"]).optional().default("_self"),
73
+ replace: z.boolean().optional(),
74
+ scroll: z.boolean().optional(),
75
+ behavior: z.enum(["smooth", "auto"]).optional()
76
+ });
77
+ var SetStateStepPayloadSchema = z.object({
78
+ key: z.string().min(1, "State key cannot be empty"),
79
+ value: z.unknown(),
80
+ scope: z.enum(["runtime", "document", "session", "local"]).optional().default("runtime")
81
+ });
82
+ var ResetFormStepPayloadSchema = z.object({
83
+ formId: z.string().optional()
84
+ });
85
+ var ShowToastStepPayloadSchema = z.object({
86
+ message: z.string().min(1, "Toast message cannot be empty"),
87
+ type: z.enum(["success", "error", "warning", "info"]).optional().default("info"),
88
+ variant: z.enum(["success", "error", "warning", "info"]).optional(),
89
+ title: z.string().optional(),
90
+ duration: z.number().nonnegative("Duration must be >= 0 ms").optional(),
91
+ position: z.enum(["top-right", "top-left", "bottom-right", "bottom-left", "top-center", "bottom-center"]).optional()
92
+ });
93
+ var OpenModalStepPayloadSchema = z.object({
94
+ modalId: z.string().optional(),
95
+ modalNodeId: z.string().optional(),
96
+ toggle: z.boolean().optional()
97
+ }).refine((data) => Boolean(data.modalId || data.modalNodeId), {
98
+ message: "Modal ID or Modal Node ID cannot be empty"
99
+ });
100
+ var CloseModalStepPayloadSchema = z.object({
101
+ modalId: z.string().optional(),
102
+ modalNodeId: z.string().optional()
103
+ });
104
+ var CopyClipboardStepPayloadSchema = z.object({
105
+ text: z.string().optional(),
106
+ value: z.unknown().optional(),
107
+ notify: z.boolean().optional(),
108
+ toastMessage: z.string().optional()
109
+ });
110
+ var CustomEventStepPayloadSchema = z.object({
111
+ eventName: SafeEventNameSchema,
112
+ detail: z.record(z.string(), z.unknown()).optional(),
113
+ bubbles: z.boolean().optional().default(true),
114
+ cancelable: z.boolean().optional().default(true)
115
+ });
116
+ var StepPayloadSchemas = {
117
+ api_request: ApiRequestStepPayloadSchema,
118
+ navigate: NavigateStepPayloadSchema,
119
+ set_state: SetStateStepPayloadSchema,
120
+ reset_form: ResetFormStepPayloadSchema,
121
+ show_toast: ShowToastStepPayloadSchema,
122
+ open_modal: OpenModalStepPayloadSchema,
123
+ close_modal: CloseModalStepPayloadSchema,
124
+ copy_clipboard: CopyClipboardStepPayloadSchema,
125
+ custom_event: CustomEventStepPayloadSchema
126
+ };
127
+ var ActionStepSchema = z.lazy(
128
+ () => z.object({
129
+ id: z.string().min(1, "Step ID cannot be empty"),
130
+ type: ActionStepTypeSchema,
131
+ label: z.string().optional(),
132
+ payload: z.record(z.string(), z.unknown()).optional().default({}),
133
+ condition: ActionStepConditionSchema.optional(),
134
+ timeout: z.number().positive().optional(),
135
+ continueOnError: z.boolean().optional(),
136
+ onSuccess: z.array(ActionStepSchema).optional(),
137
+ onError: z.array(ActionStepSchema).optional()
138
+ })
139
+ );
140
+ var ActionPipelineSchema = z.object({
141
+ id: z.string().min(1, "Pipeline ID cannot be empty"),
142
+ trigger: ActionTriggerTypeSchema,
143
+ label: z.string().optional(),
144
+ debounceMs: z.number().nonnegative("Debounce ms must be >= 0").optional(),
145
+ preventDuplicate: z.boolean().optional(),
146
+ enabled: z.boolean().optional().default(true),
147
+ steps: z.array(ActionStepSchema).min(1, "Action pipeline must contain at least one step")
148
+ });
149
+ function isActionTriggerType(value) {
150
+ return ActionTriggerTypeSchema.safeParse(value).success;
151
+ }
152
+ function isActionStepType(value) {
153
+ return ActionStepTypeSchema.safeParse(value).success;
154
+ }
155
+ function isConditionOperator(value) {
156
+ return ConditionOperatorSchema.safeParse(value).success;
157
+ }
158
+ function isActionStepCondition(value) {
159
+ return ActionStepConditionSchema.safeParse(value).success;
160
+ }
161
+ function isActionStep(value) {
162
+ return ActionStepSchema.safeParse(value).success;
163
+ }
164
+ function isActionPipeline(value) {
165
+ return ActionPipelineSchema.safeParse(value).success;
166
+ }
167
+ function validateActionStepPayload(type, payload) {
168
+ const schema = StepPayloadSchemas[type];
169
+ if (!schema) {
170
+ return {
171
+ success: false,
172
+ error: new z.ZodError([
173
+ {
174
+ code: z.ZodIssueCode.custom,
175
+ message: `Unknown step type: ${type}`,
176
+ path: ["type"]
177
+ }
178
+ ])
179
+ };
180
+ }
181
+ const result = schema.safeParse(payload);
182
+ if (result.success) {
183
+ return { success: true, data: result.data };
184
+ }
185
+ return { success: false, error: result.error };
186
+ }
187
+ function sanitizeActionUrl(url, fallback = "") {
188
+ if (typeof url !== "string") return fallback;
189
+ const trimmed = url.trim();
190
+ if (!isSafeActionUrl(trimmed)) {
191
+ return fallback;
192
+ }
193
+ return trimmed;
194
+ }
195
+ function sanitizeActionString(value, fallback = "") {
196
+ if (typeof value !== "string") return fallback;
197
+ if (DANGEROUS_SCRIPT_PATTERN.test(value)) {
198
+ return value.replace(/<\s*script[^>]*>[\s\S]*?<\s*\/\s*script\s*>/gi, "").replace(/<\s*script[^>]*>/gi, "");
199
+ }
200
+ return value;
201
+ }
202
+ function sanitizeActionValue(value) {
203
+ if (value === null || value === void 0) return value;
204
+ if (typeof value === "string") {
205
+ const trimmed = value.trim();
206
+ if (DANGEROUS_URL_PATTERN.test(trimmed)) {
207
+ return "";
208
+ }
209
+ return sanitizeActionString(value);
210
+ }
211
+ if (Array.isArray(value)) {
212
+ return value.map((item) => sanitizeActionValue(item));
213
+ }
214
+ if (typeof value === "object") {
215
+ const result = {};
216
+ for (const [k, v] of Object.entries(value)) {
217
+ if (/^(url|href|src|endpoint|targetUrl)$/i.test(k) && typeof v === "string") {
218
+ result[k] = sanitizeActionUrl(v);
219
+ } else {
220
+ result[k] = sanitizeActionValue(v);
221
+ }
222
+ }
223
+ return result;
224
+ }
225
+ return value;
226
+ }
227
+ function sanitizeActionPayload(payload) {
228
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
229
+ return {};
230
+ }
231
+ return sanitizeActionValue(payload);
232
+ }
233
+ function sanitizeActionStep(step) {
234
+ const sanitized = {
235
+ ...step,
236
+ label: step.label ? sanitizeActionString(step.label) : step.label,
237
+ payload: sanitizeActionPayload(step.payload)
238
+ };
239
+ if (step.condition && step.condition.value !== void 0) {
240
+ sanitized.condition = {
241
+ ...step.condition,
242
+ value: sanitizeActionValue(step.condition.value)
243
+ };
244
+ }
245
+ if (step.onSuccess && Array.isArray(step.onSuccess)) {
246
+ sanitized.onSuccess = step.onSuccess.map((s) => sanitizeActionStep(s));
247
+ }
248
+ if (step.onError && Array.isArray(step.onError)) {
249
+ sanitized.onError = step.onError.map((s) => sanitizeActionStep(s));
250
+ }
251
+ return sanitized;
252
+ }
253
+ function sanitizeActionPipeline(pipeline) {
254
+ return {
255
+ ...pipeline,
256
+ label: pipeline.label ? sanitizeActionString(pipeline.label) : pipeline.label,
257
+ steps: Array.isArray(pipeline.steps) ? pipeline.steps.map((s) => sanitizeActionStep(s)) : []
258
+ };
259
+ }
260
+
261
+ // src/form.ts
262
+ import { z as z2 } from "zod";
263
+ var ValidationRuleTypeSchema = z2.enum([
264
+ "required",
265
+ "email",
266
+ "url",
267
+ "min_length",
268
+ "max_length",
269
+ "numeric_min",
270
+ "numeric_max",
271
+ "pattern",
272
+ "match_field",
273
+ "custom_regex"
274
+ ]);
275
+ var ValidateOnEventSchema = z2.enum(["blur", "change", "submit"]);
276
+ var ValidationRuleSchema = z2.object({
277
+ type: ValidationRuleTypeSchema,
278
+ value: z2.unknown().optional(),
279
+ message: z2.string().min(1, "Validation error message cannot be empty")
280
+ });
281
+ var FormFieldBindingSchema = z2.object({
282
+ name: z2.string().min(1, "Field name cannot be empty"),
283
+ label: z2.string().optional(),
284
+ defaultValue: z2.unknown().optional(),
285
+ rules: z2.array(ValidationRuleSchema).optional().default([]),
286
+ validateOn: ValidateOnEventSchema.optional().default("blur"),
287
+ transform: z2.enum(["trim", "lowercase", "uppercase", "number"]).optional(),
288
+ disabled: z2.boolean().optional(),
289
+ required: z2.boolean().optional()
290
+ });
291
+ var FormConfigSchema = z2.object({
292
+ formId: z2.string().min(1, "Form ID cannot be empty"),
293
+ resetOnSubmit: z2.boolean().optional().default(false),
294
+ scrollToFirstError: z2.boolean().optional().default(true),
295
+ validateOn: ValidateOnEventSchema.optional().default("blur"),
296
+ initialValues: z2.record(z2.string(), z2.unknown()).optional(),
297
+ rules: z2.array(ValidationRuleSchema).optional()
298
+ });
299
+ function isValidationRuleType(value) {
300
+ return ValidationRuleTypeSchema.safeParse(value).success;
301
+ }
302
+ function isValidationRule(value) {
303
+ return ValidationRuleSchema.safeParse(value).success;
304
+ }
305
+ function isValidateOnEvent(value) {
306
+ return ValidateOnEventSchema.safeParse(value).success;
307
+ }
308
+ function isFormFieldBinding(value) {
309
+ return FormFieldBindingSchema.safeParse(value).success;
310
+ }
311
+ function isFormConfig(value) {
312
+ return FormConfigSchema.safeParse(value).success;
313
+ }
314
+
315
+ // src/document.ts
3
316
  var SCHEMA_NAME = "stora.page";
4
317
  var CURRENT_SCHEMA_VERSION = "1.0.0";
5
- var AssetReferenceSchema = z.object({
6
- type: z.literal("asset"),
7
- assetId: z.string().min(1, "Asset ID cannot be empty"),
8
- filename: z.string().optional(),
9
- mimeType: z.string().optional(),
10
- fallbackUrl: z.string().url().optional()
318
+ var AssetReferenceSchema = z3.object({
319
+ type: z3.literal("asset"),
320
+ assetId: z3.string().min(1, "Asset ID cannot be empty"),
321
+ filename: z3.string().optional(),
322
+ mimeType: z3.string().optional(),
323
+ fallbackUrl: z3.string().url().optional()
11
324
  });
12
- var VariableBindingSchema = z.object({
13
- type: z.literal("variable"),
14
- key: z.string().min(1, "Variable key cannot be empty"),
15
- fallback: z.unknown().optional()
325
+ var VariableBindingSchema = z3.object({
326
+ type: z3.literal("variable"),
327
+ key: z3.string().min(1, "Variable key cannot be empty"),
328
+ fallback: z3.unknown().optional()
16
329
  });
17
- var ActionBindingSchema = z.object({
18
- type: z.string().min(1, "Action type cannot be empty"),
19
- payload: z.record(z.string(), z.unknown()).optional()
330
+ var ActionBindingSchema = z3.object({
331
+ type: z3.string().min(1, "Action type cannot be empty"),
332
+ payload: z3.record(z3.string(), z3.unknown()).optional()
20
333
  });
21
334
  var DANGEROUS_STYLE_VALUE_PATTERN = /javascript:|expression\(|@import|<script|vbscript:|data:text\/html/i;
22
- var StyleValueSchema = z.union([
23
- z.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
335
+ var StyleValueSchema = z3.union([
336
+ z3.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
24
337
  message: "Style value contains a disallowed or unsafe pattern"
25
338
  }),
26
- z.number(),
27
- z.boolean(),
28
- z.null(),
29
- z.undefined()
339
+ z3.number(),
340
+ z3.boolean(),
341
+ z3.null(),
342
+ z3.undefined()
30
343
  ]);
31
- var StyleDefinitionSchema = z.record(z.string(), StyleValueSchema);
32
- var PseudoStateStylesSchema = z.record(z.string(), StyleDefinitionSchema);
33
- var ResponsiveStylesSchema = z.object({
344
+ var StyleDefinitionSchema = z3.record(z3.string(), StyleValueSchema);
345
+ var PseudoStateStylesSchema = z3.record(z3.string(), StyleDefinitionSchema);
346
+ var ResponsiveStylesSchema = z3.object({
34
347
  base: StyleDefinitionSchema.optional(),
35
348
  desktop: StyleDefinitionSchema.optional(),
36
349
  tablet: StyleDefinitionSchema.optional(),
37
350
  mobile: StyleDefinitionSchema.optional(),
38
351
  states: PseudoStateStylesSchema.optional()
39
352
  }).passthrough().default({});
40
- var AnimationConfigSchema = z.object({
41
- type: z.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
353
+ var AnimationConfigSchema = z3.object({
354
+ type: z3.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
42
355
  message: "Animation type contains a disallowed or unsafe pattern"
43
356
  }).optional().default("none"),
44
- duration: z.number().min(0, "Duration must be non-negative").optional().default(600),
45
- delay: z.number().min(0, "Delay must be non-negative").optional().default(0),
46
- easing: z.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
357
+ duration: z3.number().min(0, "Duration must be non-negative").optional().default(600),
358
+ delay: z3.number().min(0, "Delay must be non-negative").optional().default(0),
359
+ easing: z3.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
47
360
  message: "Animation easing contains a disallowed or unsafe pattern"
48
361
  }).optional().default("ease-out"),
49
- once: z.boolean().optional().default(true),
50
- hoverEffect: z.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
362
+ once: z3.boolean().optional().default(true),
363
+ hoverEffect: z3.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
51
364
  message: "Hover effect contains a disallowed or unsafe pattern"
52
365
  }).optional().default("none"),
53
- loopEffect: z.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
366
+ loopEffect: z3.string().refine((value) => !DANGEROUS_STYLE_VALUE_PATTERN.test(value), {
54
367
  message: "Loop effect contains a disallowed or unsafe pattern"
55
368
  }).optional().default("none")
56
369
  });
@@ -63,14 +376,16 @@ var DEFAULT_ANIMATION_CONFIG = {
63
376
  hoverEffect: "none",
64
377
  loopEffect: "none"
65
378
  };
66
- var NodeSchema = z.lazy(
67
- () => z.object({
68
- id: z.string().min(1, "Node ID must be a non-empty string"),
69
- type: z.string().min(1, "Node type must be a non-empty string"),
70
- props: z.record(z.string(), z.unknown()).optional().default({}),
379
+ var NodeSchema = z3.lazy(
380
+ () => z3.object({
381
+ id: z3.string().min(1, "Node ID must be a non-empty string"),
382
+ type: z3.string().min(1, "Node type must be a non-empty string"),
383
+ props: z3.record(z3.string(), z3.unknown()).optional().default({}),
71
384
  styles: ResponsiveStylesSchema.optional().default({}),
72
385
  animation: AnimationConfigSchema.optional(),
73
- children: z.array(NodeSchema).optional().default([])
386
+ actions: z3.array(ActionPipelineSchema).optional(),
387
+ formConfig: FormConfigSchema.optional(),
388
+ children: z3.array(NodeSchema).optional().default([])
74
389
  })
75
390
  );
76
391
  var RootPageNodeSchema = NodeSchema.refine(
@@ -80,23 +395,62 @@ var RootPageNodeSchema = NodeSchema.refine(
80
395
  path: ["type"]
81
396
  }
82
397
  );
83
- var DocumentMetadataSchema = z.object({
84
- title: z.string().min(1, "Title is required").default("Untitled Page"),
85
- description: z.string().optional().default(""),
86
- author: z.string().optional().default(""),
87
- createdAt: z.string().optional(),
88
- updatedAt: z.string().optional(),
89
- tags: z.array(z.string()).optional().default([]),
90
- category: z.string().optional().default("general"),
91
- version: z.string().optional().default("1.0.0"),
92
- custom: z.record(z.string(), z.unknown()).optional()
398
+ var DocumentMetadataSchema = z3.object({
399
+ title: z3.string().min(1, "Title is required").default("Untitled Page"),
400
+ description: z3.string().optional().default(""),
401
+ author: z3.string().optional().default(""),
402
+ createdAt: z3.string().optional(),
403
+ updatedAt: z3.string().optional(),
404
+ tags: z3.array(z3.string()).optional().default([]),
405
+ category: z3.string().optional().default("general"),
406
+ version: z3.string().optional().default("1.0.0"),
407
+ custom: z3.record(z3.string(), z3.unknown()).optional()
93
408
  });
94
- var PageDocumentSchema = z.object({
95
- schema: z.literal(SCHEMA_NAME),
96
- version: z.string().min(1, "Schema version is required").default(CURRENT_SCHEMA_VERSION),
409
+ var PageDocumentSchema = z3.object({
410
+ schema: z3.literal(SCHEMA_NAME),
411
+ version: z3.string().min(1, "Schema version is required").default(CURRENT_SCHEMA_VERSION),
97
412
  metadata: DocumentMetadataSchema.optional(),
98
413
  document: RootPageNodeSchema
99
414
  });
415
+ var PROJECT_SCHEMA_NAME = "stora.project";
416
+ var CURRENT_PROJECT_SCHEMA_VERSION = "1.0.0";
417
+ var ARTBOARD_REFERENCE_NODE_TYPE = "artboard-reference";
418
+ var ArtboardTypeSchema = z3.enum(["page", "component"]);
419
+ var ArtboardSchema = z3.object({
420
+ id: z3.string().min(1, "Artboard ID is required"),
421
+ name: z3.string().min(1, "Artboard name is required"),
422
+ artboardType: ArtboardTypeSchema,
423
+ /** Route for page artboards (e.g. "/about"). */
424
+ slug: z3.string().optional(),
425
+ /**
426
+ * For component artboards: the id that `open_modal` / `close_modal` actions target.
427
+ * Mirrors the detached node's own `props.modalId` so ModalManager keeps working unchanged.
428
+ */
429
+ triggerId: z3.string().optional(),
430
+ /**
431
+ * Viewport/breakpoint this artboard is currently authored at. Declared inline
432
+ * (rather than imported) because viewport is otherwise an editor/renderer concept.
433
+ */
434
+ viewport: z3.enum(["desktop", "tablet", "mobile"]).optional(),
435
+ /** Persisted artboard width in px, matching the canvas ViewportResizer's `width`. */
436
+ width: z3.number().positive().optional(),
437
+ /**
438
+ * Free position on the builder's infinite canvas, in unscaled canvas px. Absent means
439
+ * "not placed yet" — the canvas then falls back to laying the artboard out in a row.
440
+ */
441
+ position: z3.object({
442
+ x: z3.number(),
443
+ y: z3.number()
444
+ }).optional(),
445
+ document: PageDocumentSchema
446
+ });
447
+ var ProjectDocumentSchema = z3.object({
448
+ schema: z3.literal(PROJECT_SCHEMA_NAME),
449
+ version: z3.string().min(1, "Schema version is required").default(CURRENT_PROJECT_SCHEMA_VERSION),
450
+ metadata: DocumentMetadataSchema.optional(),
451
+ artboards: z3.array(ArtboardSchema).min(1, "A project needs at least one artboard"),
452
+ activeArtboardId: z3.string().optional()
453
+ });
100
454
  function isAssetReference(value) {
101
455
  return AssetReferenceSchema.safeParse(value).success;
102
456
  }
@@ -109,6 +463,17 @@ function isActionBinding(value) {
109
463
  function isAnimationConfig(value) {
110
464
  return AnimationConfigSchema.safeParse(value).success;
111
465
  }
466
+ function isProjectDocument(value) {
467
+ return ProjectDocumentSchema.safeParse(value).success;
468
+ }
469
+ function looksLikeProjectDocument(value) {
470
+ if (typeof value !== "object" || value === null) return false;
471
+ const candidate = value;
472
+ return candidate.schema === PROJECT_SCHEMA_NAME || Array.isArray(candidate.artboards);
473
+ }
474
+ function isArtboardReferenceNode(node) {
475
+ return node.type === ARTBOARD_REFERENCE_NODE_TYPE;
476
+ }
112
477
  function generateDeterministicNodeId(prefix, indexOrKey) {
113
478
  const cleanPrefix = prefix.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
114
479
  return `${cleanPrefix}_${indexOrKey}`;
@@ -140,30 +505,30 @@ function validateNodeIdUniqueness(node) {
140
505
  }
141
506
 
142
507
  // src/manifest.ts
143
- import { z as z2 } from "zod";
144
- var ManifestAssetItemSchema = z2.object({
145
- id: z2.string().min(1, "Asset ID cannot be empty"),
146
- path: z2.string().min(1, "Asset archive path cannot be empty"),
147
- mimeType: z2.string().min(1, "MIME type cannot be empty"),
148
- size: z2.number().nonnegative("Asset size must be non-negative"),
149
- checksum: z2.string().optional()
508
+ import { z as z4 } from "zod";
509
+ var ManifestAssetItemSchema = z4.object({
510
+ id: z4.string().min(1, "Asset ID cannot be empty"),
511
+ path: z4.string().min(1, "Asset archive path cannot be empty"),
512
+ mimeType: z4.string().min(1, "MIME type cannot be empty"),
513
+ size: z4.number().nonnegative("Asset size must be non-negative"),
514
+ checksum: z4.string().optional()
150
515
  });
151
- var ManifestSchema = z2.object({
152
- schema: z2.literal(SCHEMA_NAME).default(SCHEMA_NAME),
153
- schemaVersion: z2.string().min(1, "Schema version cannot be empty").default("1.0.0"),
154
- packageVersion: z2.string().min(1, "Package version cannot be empty").default("1.0.0"),
155
- builderCompatibility: z2.string().default(">=0.1.0"),
156
- requiredComponents: z2.array(z2.string()).default([]),
157
- requiredCapabilities: z2.array(z2.string()).default([]),
158
- assets: z2.array(ManifestAssetItemSchema).default([]),
159
- createdAt: z2.string().optional()
516
+ var ManifestSchema = z4.object({
517
+ schema: z4.literal(SCHEMA_NAME).default(SCHEMA_NAME),
518
+ schemaVersion: z4.string().min(1, "Schema version cannot be empty").default("1.0.0"),
519
+ packageVersion: z4.string().min(1, "Package version cannot be empty").default("1.0.0"),
520
+ builderCompatibility: z4.string().default(">=0.1.0"),
521
+ requiredComponents: z4.array(z4.string()).default([]),
522
+ requiredCapabilities: z4.array(z4.string()).default([]),
523
+ assets: z4.array(ManifestAssetItemSchema).default([]),
524
+ createdAt: z4.string().optional()
160
525
  });
161
526
  function isManifest(value) {
162
527
  return ManifestSchema.safeParse(value).success;
163
528
  }
164
529
 
165
530
  // src/template.ts
166
- import { z as z3 } from "zod";
531
+ import { z as z5 } from "zod";
167
532
  var DANGEROUS_URI_PATTERN = /^(javascript:|vbscript:|data:(?!image\/))|<script/i;
168
533
  function isSafeThumbnailUrl(url) {
169
534
  if (!url || typeof url !== "string") return false;
@@ -172,49 +537,49 @@ function isSafeThumbnailUrl(url) {
172
537
  if (DANGEROUS_URI_PATTERN.test(trimmed)) return false;
173
538
  return true;
174
539
  }
175
- var SafeThumbnailUrlSchema = z3.string().min(1, "Thumbnail URL cannot be empty").refine(
540
+ var SafeThumbnailUrlSchema = z5.string().min(1, "Thumbnail URL cannot be empty").refine(
176
541
  (url) => isSafeThumbnailUrl(url),
177
542
  { message: "Thumbnail URL contains an unsafe protocol or payload" }
178
543
  );
179
- var SafeThumbnailObjectSchema = z3.object({
544
+ var SafeThumbnailObjectSchema = z5.object({
180
545
  url: SafeThumbnailUrlSchema,
181
- alt: z3.string().optional(),
182
- width: z3.number().positive().optional(),
183
- height: z3.number().positive().optional()
546
+ alt: z5.string().optional(),
547
+ width: z5.number().positive().optional(),
548
+ height: z5.number().positive().optional()
184
549
  });
185
- var SafeThumbnailSchema = z3.union([
550
+ var SafeThumbnailSchema = z5.union([
186
551
  AssetReferenceSchema,
187
552
  SafeThumbnailObjectSchema,
188
553
  SafeThumbnailUrlSchema
189
554
  ]);
190
- var TemplatePackageRefSchema = z3.object({
191
- path: z3.string().optional(),
555
+ var TemplatePackageRefSchema = z5.object({
556
+ path: z5.string().optional(),
192
557
  url: SafeThumbnailUrlSchema.optional(),
193
- checksum: z3.string().optional(),
194
- format: z3.enum(["stora", "json"]).default("stora")
558
+ checksum: z5.string().optional(),
559
+ format: z5.enum(["stora", "json"]).default("stora")
195
560
  });
196
- var TemplateRequirementsSchema = z3.object({
197
- requiredComponents: z3.array(z3.string()).default([]),
198
- requiredCapabilities: z3.array(z3.string()).default([])
561
+ var TemplateRequirementsSchema = z5.object({
562
+ requiredComponents: z5.array(z5.string()).default([]),
563
+ requiredCapabilities: z5.array(z5.string()).default([])
199
564
  });
200
- var TemplateRecordSchema = z3.object({
201
- id: z3.string().min(1, "Template ID must be a non-empty string"),
202
- name: z3.string().min(1, "Template name must be a non-empty string"),
203
- description: z3.string().optional().default(""),
204
- category: z3.string().optional().default("general"),
205
- tags: z3.array(z3.string()).optional().default([]),
565
+ var TemplateRecordSchema = z5.object({
566
+ id: z5.string().min(1, "Template ID must be a non-empty string"),
567
+ name: z5.string().min(1, "Template name must be a non-empty string"),
568
+ description: z5.string().optional().default(""),
569
+ category: z5.string().optional().default("general"),
570
+ tags: z5.array(z5.string()).optional().default([]),
206
571
  thumbnail: SafeThumbnailSchema.optional(),
207
- author: z3.string().optional().default(""),
208
- version: z3.string().optional().default("1.0.0"),
572
+ author: z5.string().optional().default(""),
573
+ version: z5.string().optional().default("1.0.0"),
209
574
  document: PageDocumentSchema.optional(),
210
575
  packageReference: TemplatePackageRefSchema.optional(),
211
576
  requirements: TemplateRequirementsSchema.default({
212
577
  requiredComponents: [],
213
578
  requiredCapabilities: []
214
579
  }),
215
- createdAt: z3.string().optional(),
216
- updatedAt: z3.string().optional(),
217
- custom: z3.record(z3.string(), z3.unknown()).optional()
580
+ createdAt: z5.string().optional(),
581
+ updatedAt: z5.string().optional(),
582
+ custom: z5.record(z5.string(), z5.unknown()).optional()
218
583
  });
219
584
  function isTemplateRecord(value) {
220
585
  return TemplateRecordSchema.safeParse(value).success;
@@ -534,6 +899,111 @@ var PAGE_DOCUMENT_JSON_SCHEMA_V1 = {
534
899
  },
535
900
  additionalProperties: false
536
901
  },
902
+ actionStep: {
903
+ type: "object",
904
+ required: ["id", "type"],
905
+ properties: {
906
+ id: {
907
+ type: "string",
908
+ minLength: 1
909
+ },
910
+ type: {
911
+ type: "string",
912
+ enum: [
913
+ "api_request",
914
+ "navigate",
915
+ "set_state",
916
+ "reset_form",
917
+ "show_toast",
918
+ "open_modal",
919
+ "close_modal",
920
+ "copy_clipboard",
921
+ "custom_event"
922
+ ]
923
+ },
924
+ label: { type: "string" },
925
+ payload: { type: "object" },
926
+ condition: {
927
+ type: "object",
928
+ required: ["field", "operator"],
929
+ properties: {
930
+ field: { type: "string", minLength: 1 },
931
+ operator: {
932
+ type: "string",
933
+ enum: [
934
+ "equals",
935
+ "not_equals",
936
+ "contains",
937
+ "not_contains",
938
+ "is_truthy",
939
+ "is_falsy",
940
+ "gt",
941
+ "gte",
942
+ "lt",
943
+ "lte",
944
+ "regex"
945
+ ]
946
+ },
947
+ value: {}
948
+ },
949
+ additionalProperties: false
950
+ },
951
+ timeout: { type: "number", minimum: 1 },
952
+ continueOnError: { type: "boolean" },
953
+ onSuccess: {
954
+ type: "array",
955
+ items: { $ref: "#/definitions/actionStep" }
956
+ },
957
+ onError: {
958
+ type: "array",
959
+ items: { $ref: "#/definitions/actionStep" }
960
+ }
961
+ },
962
+ additionalProperties: false
963
+ },
964
+ actionPipeline: {
965
+ type: "object",
966
+ required: ["id", "trigger", "steps"],
967
+ properties: {
968
+ id: {
969
+ type: "string",
970
+ minLength: 1
971
+ },
972
+ trigger: {
973
+ type: "string",
974
+ enum: ["click", "submit", "change", "blur", "focus", "load"]
975
+ },
976
+ label: { type: "string" },
977
+ debounceMs: { type: "number", minimum: 0 },
978
+ preventDuplicate: { type: "boolean" },
979
+ enabled: { type: "boolean", default: true },
980
+ steps: {
981
+ type: "array",
982
+ items: { $ref: "#/definitions/actionStep" },
983
+ minItems: 1
984
+ }
985
+ },
986
+ additionalProperties: false
987
+ },
988
+ formConfig: {
989
+ type: "object",
990
+ required: ["formId"],
991
+ properties: {
992
+ formId: {
993
+ type: "string",
994
+ minLength: 1
995
+ },
996
+ resetOnSubmit: { type: "boolean", default: false },
997
+ scrollToFirstError: { type: "boolean", default: true },
998
+ validateOn: {
999
+ type: "string",
1000
+ enum: ["blur", "change", "submit"],
1001
+ default: "blur"
1002
+ },
1003
+ initialValues: { type: "object" }
1004
+ },
1005
+ additionalProperties: false
1006
+ },
537
1007
  node: {
538
1008
  type: "object",
539
1009
  required: ["id", "type"],
@@ -559,6 +1029,17 @@ var PAGE_DOCUMENT_JSON_SCHEMA_V1 = {
559
1029
  animation: {
560
1030
  $ref: "#/definitions/animationConfig"
561
1031
  },
1032
+ actions: {
1033
+ type: "array",
1034
+ items: {
1035
+ $ref: "#/definitions/actionPipeline"
1036
+ },
1037
+ description: "Multi-step action pipelines bound to component events"
1038
+ },
1039
+ formConfig: {
1040
+ $ref: "#/definitions/formConfig",
1041
+ description: "Form container configuration and behavior"
1042
+ },
562
1043
  children: {
563
1044
  type: "array",
564
1045
  items: {
@@ -895,47 +1376,334 @@ var TEMPLATE_RECORD_JSON_SCHEMA_V1 = {
895
1376
  function getTemplateRecordJsonSchema() {
896
1377
  return TEMPLATE_RECORD_JSON_SCHEMA_V1;
897
1378
  }
1379
+ var ACTION_PIPELINE_JSON_SCHEMA_V1 = {
1380
+ $schema: "http://json-schema.org/draft-07/schema#",
1381
+ $id: "https://schema.stora.page/v1/action-pipeline.json",
1382
+ title: "StoraActionPipeline",
1383
+ description: "Action Pipeline schema definition for KUBUILD event workflows",
1384
+ type: "object",
1385
+ required: ["id", "trigger", "steps"],
1386
+ additionalProperties: false,
1387
+ properties: {
1388
+ id: {
1389
+ type: "string",
1390
+ minLength: 1,
1391
+ description: "Unique pipeline identifier"
1392
+ },
1393
+ trigger: {
1394
+ type: "string",
1395
+ enum: ["click", "submit", "change", "blur", "focus", "load"],
1396
+ description: "DOM or component event that triggers the action pipeline"
1397
+ },
1398
+ label: {
1399
+ type: "string",
1400
+ description: "Human readable label for the action pipeline"
1401
+ },
1402
+ debounceMs: {
1403
+ type: "number",
1404
+ minimum: 0,
1405
+ description: "Debounce duration in milliseconds"
1406
+ },
1407
+ preventDuplicate: {
1408
+ type: "boolean",
1409
+ description: "Prevent concurrent duplicate pipeline executions"
1410
+ },
1411
+ enabled: {
1412
+ type: "boolean",
1413
+ default: true,
1414
+ description: "Whether the pipeline is currently active"
1415
+ },
1416
+ steps: {
1417
+ type: "array",
1418
+ items: { $ref: "#/definitions/actionStep" },
1419
+ minItems: 1,
1420
+ description: "Ordered sequence of action steps to execute"
1421
+ }
1422
+ },
1423
+ definitions: {
1424
+ actionStep: {
1425
+ type: "object",
1426
+ required: ["id", "type"],
1427
+ properties: {
1428
+ id: {
1429
+ type: "string",
1430
+ minLength: 1
1431
+ },
1432
+ type: {
1433
+ type: "string",
1434
+ enum: [
1435
+ "api_request",
1436
+ "navigate",
1437
+ "set_state",
1438
+ "reset_form",
1439
+ "show_toast",
1440
+ "open_modal",
1441
+ "close_modal",
1442
+ "copy_clipboard",
1443
+ "custom_event"
1444
+ ]
1445
+ },
1446
+ label: { type: "string" },
1447
+ payload: { type: "object" },
1448
+ condition: {
1449
+ type: "object",
1450
+ required: ["field", "operator"],
1451
+ properties: {
1452
+ field: { type: "string", minLength: 1 },
1453
+ operator: {
1454
+ type: "string",
1455
+ enum: [
1456
+ "equals",
1457
+ "not_equals",
1458
+ "contains",
1459
+ "not_contains",
1460
+ "is_truthy",
1461
+ "is_falsy",
1462
+ "gt",
1463
+ "gte",
1464
+ "lt",
1465
+ "lte",
1466
+ "regex"
1467
+ ]
1468
+ },
1469
+ value: {}
1470
+ },
1471
+ additionalProperties: false
1472
+ },
1473
+ timeout: { type: "number", minimum: 1 },
1474
+ continueOnError: { type: "boolean" },
1475
+ onSuccess: {
1476
+ type: "array",
1477
+ items: { $ref: "#/definitions/actionStep" }
1478
+ },
1479
+ onError: {
1480
+ type: "array",
1481
+ items: { $ref: "#/definitions/actionStep" }
1482
+ }
1483
+ },
1484
+ additionalProperties: false
1485
+ }
1486
+ }
1487
+ };
1488
+ function getActionPipelineJsonSchema() {
1489
+ return ACTION_PIPELINE_JSON_SCHEMA_V1;
1490
+ }
1491
+ var FORM_CONFIG_JSON_SCHEMA_V1 = {
1492
+ $schema: "http://json-schema.org/draft-07/schema#",
1493
+ $id: "https://schema.stora.page/v1/form-config.json",
1494
+ title: "StoraFormConfig",
1495
+ description: "Form container configuration and behavior definition for KUBUILD",
1496
+ type: "object",
1497
+ required: ["formId"],
1498
+ additionalProperties: false,
1499
+ properties: {
1500
+ formId: {
1501
+ type: "string",
1502
+ minLength: 1,
1503
+ description: "Unique identifier for the form boundary"
1504
+ },
1505
+ resetOnSubmit: {
1506
+ type: "boolean",
1507
+ default: false,
1508
+ description: "Whether to reset form field values after successful submission"
1509
+ },
1510
+ scrollToFirstError: {
1511
+ type: "boolean",
1512
+ default: true,
1513
+ description: "Auto-scroll viewport to the first invalid input on validation failure"
1514
+ },
1515
+ validateOn: {
1516
+ type: "string",
1517
+ enum: ["blur", "change", "submit"],
1518
+ default: "blur",
1519
+ description: "Default trigger event for field validation evaluation"
1520
+ },
1521
+ initialValues: {
1522
+ type: "object",
1523
+ description: "Initial state values populated in form fields"
1524
+ }
1525
+ }
1526
+ };
1527
+ function getFormConfigJsonSchema() {
1528
+ return FORM_CONFIG_JSON_SCHEMA_V1;
1529
+ }
1530
+ var FORM_FIELD_BINDING_JSON_SCHEMA_V1 = {
1531
+ $schema: "http://json-schema.org/draft-07/schema#",
1532
+ $id: "https://schema.stora.page/v1/form-field-binding.json",
1533
+ title: "StoraFormFieldBinding",
1534
+ description: "Form field binding definition connecting input elements to form state runtime",
1535
+ type: "object",
1536
+ required: ["name"],
1537
+ additionalProperties: false,
1538
+ properties: {
1539
+ name: {
1540
+ type: "string",
1541
+ minLength: 1,
1542
+ description: "Field state property key"
1543
+ },
1544
+ label: {
1545
+ type: "string",
1546
+ description: "Display label for the field"
1547
+ },
1548
+ defaultValue: {
1549
+ description: "Initial default value if not overridden by form initialValues"
1550
+ },
1551
+ rules: {
1552
+ type: "array",
1553
+ items: { $ref: "#/definitions/validationRule" },
1554
+ default: [],
1555
+ description: "Validation rules applied to the field value"
1556
+ },
1557
+ validateOn: {
1558
+ type: "string",
1559
+ enum: ["blur", "change", "submit"],
1560
+ default: "blur",
1561
+ description: "Field-specific validation trigger timing"
1562
+ },
1563
+ transform: {
1564
+ type: "string",
1565
+ enum: ["trim", "lowercase", "uppercase", "number"],
1566
+ description: "Value transformation applied prior to validation and state storage"
1567
+ },
1568
+ disabled: {
1569
+ type: "boolean",
1570
+ description: "Whether the field is disabled"
1571
+ },
1572
+ required: {
1573
+ type: "boolean",
1574
+ description: "Whether the field is required (shorthand rule)"
1575
+ }
1576
+ },
1577
+ definitions: {
1578
+ validationRule: {
1579
+ type: "object",
1580
+ required: ["type", "message"],
1581
+ properties: {
1582
+ type: {
1583
+ type: "string",
1584
+ enum: [
1585
+ "required",
1586
+ "email",
1587
+ "url",
1588
+ "min_length",
1589
+ "max_length",
1590
+ "numeric_min",
1591
+ "numeric_max",
1592
+ "pattern",
1593
+ "match_field",
1594
+ "custom_regex"
1595
+ ]
1596
+ },
1597
+ value: {},
1598
+ message: {
1599
+ type: "string",
1600
+ minLength: 1
1601
+ }
1602
+ },
1603
+ additionalProperties: false
1604
+ }
1605
+ }
1606
+ };
1607
+ function getFormFieldBindingJsonSchema() {
1608
+ return FORM_FIELD_BINDING_JSON_SCHEMA_V1;
1609
+ }
898
1610
  export {
1611
+ ACTION_PIPELINE_JSON_SCHEMA_V1,
1612
+ ARTBOARD_REFERENCE_NODE_TYPE,
899
1613
  ActionBindingSchema,
1614
+ ActionPipelineSchema,
1615
+ ActionStepConditionSchema,
1616
+ ActionStepSchema,
1617
+ ActionStepTypeSchema,
1618
+ ActionTriggerSchema,
1619
+ ActionTriggerTypeSchema,
900
1620
  AnimationConfigSchema,
1621
+ ApiRequestStepPayloadSchema,
1622
+ ArtboardSchema,
1623
+ ArtboardTypeSchema,
901
1624
  AssetReferenceSchema,
1625
+ CURRENT_PROJECT_SCHEMA_VERSION,
902
1626
  CURRENT_SCHEMA_VERSION,
1627
+ CloseModalStepPayloadSchema,
1628
+ ConditionOperatorSchema,
1629
+ CopyClipboardStepPayloadSchema,
1630
+ CustomEventStepPayloadSchema,
903
1631
  DEFAULT_ANIMATION_CONFIG,
904
1632
  DocumentMetadataSchema,
1633
+ FORM_CONFIG_JSON_SCHEMA_V1,
1634
+ FORM_FIELD_BINDING_JSON_SCHEMA_V1,
1635
+ FormConfigSchema,
1636
+ FormFieldBindingSchema,
905
1637
  MANIFEST_JSON_SCHEMA_V1,
906
1638
  ManifestAssetItemSchema,
907
1639
  ManifestSchema,
1640
+ NavigateStepPayloadSchema,
908
1641
  NodeSchema,
1642
+ OpenModalStepPayloadSchema,
909
1643
  PAGE_DOCUMENT_JSON_SCHEMA_V1,
1644
+ PROJECT_SCHEMA_NAME,
910
1645
  PageDocumentSchema,
1646
+ ProjectDocumentSchema,
911
1647
  PseudoStateStylesSchema,
1648
+ ResetFormStepPayloadSchema,
912
1649
  ResponsiveStylesSchema,
913
1650
  RootPageNodeSchema,
914
1651
  SCHEMA_NAME,
915
1652
  SafeThumbnailObjectSchema,
916
1653
  SafeThumbnailSchema,
917
1654
  SafeThumbnailUrlSchema,
1655
+ SetStateStepPayloadSchema,
1656
+ ShowToastStepPayloadSchema,
1657
+ StepPayloadSchemas,
918
1658
  StyleDefinitionSchema,
919
1659
  StyleValueSchema,
920
1660
  TEMPLATE_RECORD_JSON_SCHEMA_V1,
921
1661
  TemplatePackageRefSchema,
922
1662
  TemplateRecordSchema,
923
1663
  TemplateRequirementsSchema,
1664
+ ValidateOnEventSchema,
1665
+ ValidationRuleSchema,
1666
+ ValidationRuleTypeSchema,
924
1667
  VariableBindingSchema,
925
1668
  collectNodeIds,
926
1669
  generateDeterministicNodeId,
1670
+ getActionPipelineJsonSchema,
1671
+ getFormConfigJsonSchema,
1672
+ getFormFieldBindingJsonSchema,
927
1673
  getManifestJsonSchema,
928
1674
  getPageDocumentJsonSchema,
929
1675
  getTemplateRecordJsonSchema,
930
1676
  isActionBinding,
1677
+ isActionPipeline,
1678
+ isActionStep,
1679
+ isActionStepCondition,
1680
+ isActionStepType,
1681
+ isActionTriggerType,
931
1682
  isAnimationConfig,
1683
+ isArtboardReferenceNode,
932
1684
  isAssetReference,
1685
+ isConditionOperator,
1686
+ isFormConfig,
1687
+ isFormFieldBinding,
933
1688
  isManifest,
1689
+ isProjectDocument,
1690
+ isSafeActionUrl,
934
1691
  isSafeThumbnail,
935
1692
  isSafeThumbnailUrl,
936
1693
  isTemplateRecord,
1694
+ isValidateOnEvent,
1695
+ isValidationRule,
1696
+ isValidationRuleType,
937
1697
  isVariableBinding,
1698
+ looksLikeProjectDocument,
1699
+ sanitizeActionPayload,
1700
+ sanitizeActionPipeline,
1701
+ sanitizeActionStep,
1702
+ sanitizeActionString,
1703
+ sanitizeActionUrl,
1704
+ sanitizeActionValue,
938
1705
  starterPageFixture,
1706
+ validateActionStepPayload,
939
1707
  validateNodeIdUniqueness
940
1708
  };
941
1709
  //# sourceMappingURL=index.js.map