@formspec/constraints 0.1.0-alpha.19 → 0.1.0-alpha.21

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.
@@ -40,6 +40,8 @@ import type { FormSpec } from '@formspec/core';
40
40
 
41
41
  /**
42
42
  * Complete constraint configuration for a FormSpec project.
43
+ *
44
+ * @public
43
45
  */
44
46
  export declare interface ConstraintConfig {
45
47
  /** Field type constraints */
@@ -57,6 +59,8 @@ export declare interface ConstraintConfig {
57
59
  /**
58
60
  * Control options constraints - control which JSONForms Control.options are allowed.
59
61
  * These are renderer-specific options that may not be universally supported.
62
+ *
63
+ * @public
60
64
  */
61
65
  export declare interface ControlOptionConstraints {
62
66
  /** format - renderer format hint (e.g., "radio", "textarea") */
@@ -75,12 +79,16 @@ export declare interface ControlOptionConstraints {
75
79
 
76
80
  /**
77
81
  * Default FormSpec configuration.
82
+ *
83
+ * @public
78
84
  */
79
85
  export declare const DEFAULT_CONFIG: FormSpecConfig;
80
86
 
81
87
  /**
82
88
  * Default constraint configuration that allows all features.
83
89
  * All constraints default to "off" (allowed).
90
+ *
91
+ * @public
84
92
  */
85
93
  export declare const DEFAULT_CONSTRAINTS: ResolvedConstraintConfig;
86
94
 
@@ -103,6 +111,8 @@ export declare const DEFAULT_CONSTRAINTS: ResolvedConstraintConfig;
103
111
  * },
104
112
  * });
105
113
  * ```
114
+ *
115
+ * @public
106
116
  */
107
117
  export declare function defineConstraints(config: ConstraintConfig): ResolvedConstraintConfig;
108
118
 
@@ -112,16 +122,22 @@ export declare function defineConstraints(config: ConstraintConfig): ResolvedCon
112
122
  *
113
123
  * @param field - A field object with potential options
114
124
  * @returns Array of present option names
125
+ *
126
+ * @public
115
127
  */
116
128
  export declare function extractFieldOptions(field: Record<string, unknown>): FieldOption[];
117
129
 
118
130
  /**
119
131
  * Known field options that can be validated.
132
+ *
133
+ * @public
120
134
  */
121
135
  export declare type FieldOption = "label" | "placeholder" | "required" | "minValue" | "maxValue" | "minItems" | "maxItems";
122
136
 
123
137
  /**
124
138
  * Field configuration option constraints - control which field options are allowed.
139
+ *
140
+ * @public
125
141
  */
126
142
  export declare interface FieldOptionConstraints {
127
143
  /** label - field label text */
@@ -142,6 +158,8 @@ export declare interface FieldOptionConstraints {
142
158
 
143
159
  /**
144
160
  * Context for field option validation.
161
+ *
162
+ * @public
145
163
  */
146
164
  export declare interface FieldOptionsContext {
147
165
  /** The field name */
@@ -155,6 +173,8 @@ export declare interface FieldOptionsContext {
155
173
  /**
156
174
  * Field type constraints - control which field types are allowed.
157
175
  * Fine-grained control over each DSL field builder.
176
+ *
177
+ * @public
158
178
  */
159
179
  export declare interface FieldTypeConstraints {
160
180
  /** field.text() - basic text input */
@@ -177,6 +197,8 @@ export declare interface FieldTypeConstraints {
177
197
 
178
198
  /**
179
199
  * Context for field type validation.
200
+ *
201
+ * @public
180
202
  */
181
203
  export declare interface FieldTypeContext {
182
204
  /** The _field discriminator value (e.g., "text", "number", "enum") */
@@ -190,6 +212,8 @@ export declare interface FieldTypeContext {
190
212
  /**
191
213
  * Top-level FormSpec configuration file structure.
192
214
  * The .formspec.yml file uses this structure.
215
+ *
216
+ * @public
193
217
  */
194
218
  export declare interface FormSpecConfig {
195
219
  /** Constraint configuration */
@@ -198,6 +222,8 @@ export declare interface FormSpecConfig {
198
222
 
199
223
  /**
200
224
  * Options for validating FormSpec elements.
225
+ *
226
+ * @public
201
227
  */
202
228
  export declare interface FormSpecValidationOptions {
203
229
  /** Constraint configuration (will be merged with defaults) */
@@ -210,6 +236,8 @@ export declare interface FormSpecValidationOptions {
210
236
  * @param option - The option to check
211
237
  * @param constraints - Field option constraints
212
238
  * @returns Severity level, or "off" if not constrained
239
+ *
240
+ * @public
213
241
  */
214
242
  export declare function getFieldOptionSeverity(option: FieldOption, constraints: FieldOptionConstraints): Severity;
215
243
 
@@ -219,6 +247,8 @@ export declare function getFieldOptionSeverity(option: FieldOption, constraints:
219
247
  * @param fieldType - The _field discriminator value
220
248
  * @param constraints - Field type constraints
221
249
  * @returns Severity level, or "off" if not constrained
250
+ *
251
+ * @public
222
252
  */
223
253
  export declare function getFieldTypeSeverity(fieldType: string, constraints: FieldTypeConstraints): Severity;
224
254
 
@@ -228,6 +258,8 @@ export declare function getFieldTypeSeverity(fieldType: string, constraints: Fie
228
258
  * @param option - The option to check
229
259
  * @param constraints - Field option constraints
230
260
  * @returns true if allowed, false if disallowed
261
+ *
262
+ * @public
231
263
  */
232
264
  export declare function isFieldOptionAllowed(option: FieldOption, constraints: FieldOptionConstraints): boolean;
233
265
 
@@ -238,6 +270,8 @@ export declare function isFieldOptionAllowed(option: FieldOption, constraints: F
238
270
  * @param fieldType - The _field discriminator value
239
271
  * @param constraints - Field type constraints
240
272
  * @returns true if allowed, false if disallowed
273
+ *
274
+ * @public
241
275
  */
242
276
  export declare function isFieldTypeAllowed(fieldType: string, constraints: FieldTypeConstraints): boolean;
243
277
 
@@ -247,6 +281,8 @@ export declare function isFieldTypeAllowed(fieldType: string, constraints: Field
247
281
  * @param layoutType - The type of layout element
248
282
  * @param constraints - Layout constraints
249
283
  * @returns true if allowed, false if disallowed
284
+ *
285
+ * @public
250
286
  */
251
287
  export declare function isLayoutTypeAllowed(layoutType: "group" | "conditional", constraints: LayoutConstraints): boolean;
252
288
 
@@ -256,11 +292,15 @@ export declare function isLayoutTypeAllowed(layoutType: "group" | "conditional",
256
292
  * @param depth - Current nesting depth
257
293
  * @param constraints - Layout constraints
258
294
  * @returns true if allowed, false if exceeds limit
295
+ *
296
+ * @public
259
297
  */
260
298
  export declare function isNestingDepthAllowed(depth: number, constraints: LayoutConstraints): boolean;
261
299
 
262
300
  /**
263
301
  * Layout and structure constraints - control grouping, conditionals, nesting.
302
+ *
303
+ * @public
264
304
  */
265
305
  export declare interface LayoutConstraints {
266
306
  /** group() - visual grouping of fields */
@@ -273,6 +313,8 @@ export declare interface LayoutConstraints {
273
313
 
274
314
  /**
275
315
  * Context for layout validation.
316
+ *
317
+ * @public
276
318
  */
277
319
  export declare interface LayoutContext {
278
320
  /** The type of layout element ("group" | "conditional") */
@@ -287,6 +329,8 @@ export declare interface LayoutContext {
287
329
 
288
330
  /**
289
331
  * JSONForms layout type constraints.
332
+ *
333
+ * @public
290
334
  */
291
335
  export declare interface LayoutTypeConstraints {
292
336
  /** VerticalLayout - stack elements vertically */
@@ -318,6 +362,8 @@ export declare interface LayoutTypeConstraints {
318
362
  * // Load from specific file
319
363
  * const result = await loadConfig({ configPath: '/path/to/config.yml' });
320
364
  * ```
365
+ *
366
+ * @public
321
367
  */
322
368
  export declare function loadConfig(options?: LoadConfigOptions): Promise<LoadConfigResult>;
323
369
 
@@ -327,11 +373,15 @@ export declare function loadConfig(options?: LoadConfigOptions): Promise<LoadCon
327
373
  *
328
374
  * @param yamlContent - The YAML content to parse
329
375
  * @returns The parsed and merged configuration
376
+ *
377
+ * @public
330
378
  */
331
379
  export declare function loadConfigFromString(yamlContent: string): ResolvedConstraintConfig;
332
380
 
333
381
  /**
334
382
  * Options for loading configuration.
383
+ *
384
+ * @public
335
385
  */
336
386
  export declare interface LoadConfigOptions {
337
387
  /**
@@ -353,6 +403,8 @@ export declare interface LoadConfigOptions {
353
403
 
354
404
  /**
355
405
  * Result of loading configuration.
406
+ *
407
+ * @public
356
408
  */
357
409
  export declare interface LoadConfigResult {
358
410
  /** The loaded and merged configuration */
@@ -365,12 +417,16 @@ export declare interface LoadConfigResult {
365
417
 
366
418
  /**
367
419
  * Merges user constraints with defaults, filling in any missing values.
420
+ *
421
+ * @public
368
422
  */
369
423
  export declare function mergeWithDefaults(config: ConstraintConfig | undefined): ResolvedConstraintConfig;
370
424
 
371
425
  /**
372
426
  * Fully resolved constraint configuration with all properties required.
373
427
  * This is the type returned by mergeWithDefaults().
428
+ *
429
+ * @public
374
430
  */
375
431
  export declare interface ResolvedConstraintConfig {
376
432
  fieldTypes: Required<FieldTypeConstraints>;
@@ -382,6 +438,8 @@ export declare interface ResolvedConstraintConfig {
382
438
 
383
439
  /**
384
440
  * Fully resolved rule constraints with all properties required.
441
+ *
442
+ * @public
385
443
  */
386
444
  export declare interface ResolvedRuleConstraints {
387
445
  enabled: Severity;
@@ -390,6 +448,8 @@ export declare interface ResolvedRuleConstraints {
390
448
 
391
449
  /**
392
450
  * Fully resolved UI schema constraints with all properties required.
451
+ *
452
+ * @public
393
453
  */
394
454
  export declare interface ResolvedUISchemaConstraints {
395
455
  layouts: Required<LayoutTypeConstraints>;
@@ -398,6 +458,8 @@ export declare interface ResolvedUISchemaConstraints {
398
458
 
399
459
  /**
400
460
  * JSONForms rule constraints.
461
+ *
462
+ * @public
401
463
  */
402
464
  export declare interface RuleConstraints {
403
465
  /** Whether rules are enabled at all */
@@ -408,6 +470,8 @@ export declare interface RuleConstraints {
408
470
 
409
471
  /**
410
472
  * JSONForms rule effect constraints.
473
+ *
474
+ * @public
411
475
  */
412
476
  export declare interface RuleEffectConstraints {
413
477
  /** SHOW - show element when condition is true */
@@ -425,11 +489,15 @@ export declare interface RuleEffectConstraints {
425
489
  * - "error": Violation fails validation
426
490
  * - "warn": Violation emits warning but passes
427
491
  * - "off": Feature is allowed (no violation)
492
+ *
493
+ * @public
428
494
  */
429
495
  export declare type Severity = "error" | "warn" | "off";
430
496
 
431
497
  /**
432
498
  * UI Schema feature constraints - control JSONForms-specific features.
499
+ *
500
+ * @public
433
501
  */
434
502
  export declare interface UISchemaConstraints {
435
503
  /** Layout type constraints */
@@ -444,6 +512,8 @@ export declare interface UISchemaConstraints {
444
512
  * @param context - Information about the field and its options
445
513
  * @param constraints - Field option constraints
446
514
  * @returns Array of validation issues (empty if valid)
515
+ *
516
+ * @public
447
517
  */
448
518
  export declare function validateFieldOptions(context: FieldOptionsContext, constraints: FieldOptionConstraints): ValidationIssue[];
449
519
 
@@ -453,6 +523,8 @@ export declare function validateFieldOptions(context: FieldOptionsContext, const
453
523
  * @param context - Information about the field being validated
454
524
  * @param constraints - Field type constraints
455
525
  * @returns Array of validation issues (empty if valid)
526
+ *
527
+ * @public
456
528
  */
457
529
  export declare function validateFieldTypes(context: FieldTypeContext, constraints: FieldTypeConstraints): ValidationIssue[];
458
530
 
@@ -462,6 +534,8 @@ export declare function validateFieldTypes(context: FieldTypeContext, constraint
462
534
  * @param formSpec - The FormSpec to validate
463
535
  * @param options - Validation options including constraints
464
536
  * @returns Validation result with all issues found
537
+ *
538
+ * @public
465
539
  */
466
540
  export declare function validateFormSpec(formSpec: FormSpec<readonly FormElement[]>, options?: FormSpecValidationOptions): ValidationResult;
467
541
 
@@ -499,6 +573,8 @@ export declare function validateFormSpec(formSpec: FormSpec<readonly FormElement
499
573
  * console.error('Validation failed:', result.issues);
500
574
  * }
501
575
  * ```
576
+ *
577
+ * @public
502
578
  */
503
579
  export declare function validateFormSpecElements(elements: readonly FormElement[], options?: FormSpecValidationOptions): ValidationResult;
504
580
 
@@ -508,11 +584,15 @@ export declare function validateFormSpecElements(elements: readonly FormElement[
508
584
  * @param context - Information about the layout element
509
585
  * @param constraints - Layout constraints
510
586
  * @returns Array of validation issues (empty if valid)
587
+ *
588
+ * @public
511
589
  */
512
590
  export declare function validateLayout(context: LayoutContext, constraints: LayoutConstraints): ValidationIssue[];
513
591
 
514
592
  /**
515
593
  * A single validation issue found during constraint checking.
594
+ *
595
+ * @public
516
596
  */
517
597
  export declare interface ValidationIssue {
518
598
  /** Unique code identifying the issue type */
@@ -533,6 +613,8 @@ export declare interface ValidationIssue {
533
613
 
534
614
  /**
535
615
  * Result of validating a FormSpec or schema against constraints.
616
+ *
617
+ * @public
536
618
  */
537
619
  export declare interface ValidationResult {
538
620
  /** Whether validation passed (no errors, warnings OK) */
@@ -2,14 +2,20 @@ import type { ConstraintConfig, FormSpecConfig, ResolvedConstraintConfig } from
2
2
  /**
3
3
  * Default constraint configuration that allows all features.
4
4
  * All constraints default to "off" (allowed).
5
+ *
6
+ * @public
5
7
  */
6
8
  export declare const DEFAULT_CONSTRAINTS: ResolvedConstraintConfig;
7
9
  /**
8
10
  * Default FormSpec configuration.
11
+ *
12
+ * @public
9
13
  */
10
14
  export declare const DEFAULT_CONFIG: FormSpecConfig;
11
15
  /**
12
16
  * Merges user constraints with defaults, filling in any missing values.
17
+ *
18
+ * @public
13
19
  */
14
20
  export declare function mergeWithDefaults(config: ConstraintConfig | undefined): ResolvedConstraintConfig;
15
21
  //# sourceMappingURL=defaults.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE7F;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,wBAmDjC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,cAE5B,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,GAAG,wBAAwB,CAwChG"}
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE7F;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,wBAmDjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,cAE5B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,GAAG,wBAAwB,CAwChG"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/loader.ts","../src/defaults.ts","../src/validators/field-types.ts","../src/validators/layout.ts","../src/validators/field-options.ts","../src/validators/formspec.ts"],"sourcesContent":["/**\n * \\@formspec/constraints\n *\n * Constraint validation for FormSpec - restrict features based on target\n * environment capabilities.\n *\n * This package provides:\n * - Type definitions for constraint configuration\n * - YAML/TypeScript config file loading\n * - Core validation logic for FormSpec elements\n * - JSON Schema for .formspec.yml validation\n *\n * @example\n * ```ts\n * import { loadConfig, validateFormSpecElements } from '@formspec/constraints';\n * import { formspec, field } from '@formspec/dsl';\n *\n * // Load constraints from .formspec.yml\n * const { config } = await loadConfig();\n *\n * // Create a form\n * const form = formspec(\n * field.text(\"name\"),\n * field.dynamicEnum(\"country\", \"countries\"),\n * );\n *\n * // Validate against constraints\n * const result = validateFormSpecElements(form.elements, { constraints: config });\n *\n * if (!result.valid) {\n * console.error('Validation failed:', result.issues);\n * }\n * ```\n *\n * @packageDocumentation\n */\n\n// Types\nexport type {\n Severity,\n FieldTypeConstraints,\n LayoutConstraints,\n LayoutTypeConstraints,\n RuleEffectConstraints,\n RuleConstraints,\n UISchemaConstraints,\n FieldOptionConstraints,\n ControlOptionConstraints,\n ConstraintConfig,\n ResolvedConstraintConfig,\n ResolvedUISchemaConstraints,\n ResolvedRuleConstraints,\n FormSpecConfig,\n ValidationIssue,\n ValidationResult,\n} from \"./types.js\";\n\n// Config loading\nexport {\n loadConfig,\n loadConfigFromString,\n defineConstraints,\n type LoadConfigOptions,\n type LoadConfigResult,\n} from \"./loader.js\";\n\n// Defaults\nexport { DEFAULT_CONSTRAINTS, DEFAULT_CONFIG, mergeWithDefaults } from \"./defaults.js\";\n\n// Validators\nexport {\n validateFormSpecElements,\n validateFormSpec,\n type FormSpecValidationOptions,\n} from \"./validators/formspec.js\";\n\nexport {\n validateFieldTypes,\n isFieldTypeAllowed,\n getFieldTypeSeverity,\n type FieldTypeContext,\n} from \"./validators/field-types.js\";\n\nexport {\n validateLayout,\n isLayoutTypeAllowed,\n isNestingDepthAllowed,\n type LayoutContext,\n} from \"./validators/layout.js\";\n\nexport {\n validateFieldOptions,\n extractFieldOptions,\n isFieldOptionAllowed,\n getFieldOptionSeverity,\n type FieldOptionsContext,\n type FieldOption,\n} from \"./validators/field-options.js\";\n","import { readFile } from \"node:fs/promises\";\nimport { resolve, dirname } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { FormSpecConfig, ConstraintConfig, ResolvedConstraintConfig } from \"./types.js\";\nimport { mergeWithDefaults } from \"./defaults.js\";\n\n/**\n * Default config file names to search for (in order of priority).\n */\nconst CONFIG_FILE_NAMES = [\".formspec.yml\", \".formspec.yaml\", \"formspec.yml\"];\n\n/**\n * Options for loading configuration.\n */\nexport interface LoadConfigOptions {\n /**\n * The directory to search for config files.\n * Defaults to process.cwd().\n */\n cwd?: string;\n\n /**\n * Explicit path to a config file.\n * If provided, skips searching for default config file names.\n */\n configPath?: string;\n\n /**\n * Whether to search parent directories for config files.\n * Defaults to true.\n */\n searchParents?: boolean;\n}\n\n/**\n * Result of loading configuration.\n */\nexport interface LoadConfigResult {\n /** The loaded and merged configuration */\n config: ResolvedConstraintConfig;\n /** The path to the config file that was loaded (if any) */\n configPath: string | null;\n /** Whether a config file was found */\n found: boolean;\n}\n\n/**\n * Searches for a config file in the given directory and optionally parent directories.\n */\nasync function findConfigFile(startDir: string, searchParents: boolean): Promise<string | null> {\n let currentDir = resolve(startDir);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- intentional infinite loop with break conditions\n while (true) {\n for (const fileName of CONFIG_FILE_NAMES) {\n const filePath = resolve(currentDir, fileName);\n try {\n await readFile(filePath);\n return filePath;\n } catch {\n // File doesn't exist, continue searching\n }\n }\n\n if (!searchParents) {\n break;\n }\n\n const parentDir = dirname(currentDir);\n // Reached filesystem root when dirname returns same path\n if (parentDir === currentDir) {\n break;\n }\n currentDir = parentDir;\n }\n\n return null;\n}\n\n/**\n * Parses a YAML config file and returns the FormSpecConfig.\n */\nasync function parseConfigFile(filePath: string): Promise<FormSpecConfig> {\n const content = await readFile(filePath, \"utf-8\");\n const parsed = parseYaml(content) as unknown;\n\n if (parsed === null || parsed === undefined) {\n return {};\n }\n\n if (typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`Invalid config file at ${filePath}: expected an object, got ${typeof parsed}`);\n }\n\n return parsed as FormSpecConfig;\n}\n\n/**\n * Loads FormSpec constraint configuration from a .formspec.yml file.\n *\n * @param options - Options for loading configuration\n * @returns The loaded configuration with defaults applied\n *\n * @example\n * ```ts\n * // Load from current directory (searches for .formspec.yml)\n * const result = await loadConfig();\n *\n * // Load from specific directory\n * const result = await loadConfig({ cwd: '/path/to/project' });\n *\n * // Load from specific file\n * const result = await loadConfig({ configPath: '/path/to/config.yml' });\n * ```\n */\nexport async function loadConfig(options: LoadConfigOptions = {}): Promise<LoadConfigResult> {\n const { cwd = process.cwd(), configPath, searchParents = true } = options;\n\n let resolvedPath: string | null = null;\n\n if (configPath) {\n resolvedPath = resolve(cwd, configPath);\n try {\n await readFile(resolvedPath);\n } catch {\n throw new Error(`Config file not found at ${resolvedPath}`);\n }\n } else {\n resolvedPath = await findConfigFile(cwd, searchParents);\n }\n\n if (!resolvedPath) {\n return {\n config: mergeWithDefaults(undefined),\n configPath: null,\n found: false,\n };\n }\n\n const fileConfig = await parseConfigFile(resolvedPath);\n const config = mergeWithDefaults(fileConfig.constraints);\n\n return {\n config,\n configPath: resolvedPath,\n found: true,\n };\n}\n\n/**\n * Synchronously loads config from a pre-parsed YAML string.\n * Useful for testing or when config is already available.\n *\n * @param yamlContent - The YAML content to parse\n * @returns The parsed and merged configuration\n */\nexport function loadConfigFromString(yamlContent: string): ResolvedConstraintConfig {\n const parsed = parseYaml(yamlContent) as FormSpecConfig | null | undefined;\n\n if (parsed === null || parsed === undefined) {\n return mergeWithDefaults(undefined);\n }\n\n if (typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`Invalid config content: expected an object, got ${typeof parsed}`);\n }\n\n return mergeWithDefaults(parsed.constraints);\n}\n\n/**\n * Creates a constraint configuration directly from an object.\n * Useful for programmatic configuration without YAML.\n *\n * @param config - Partial constraint configuration\n * @returns Complete configuration with defaults applied\n *\n * @example\n * ```ts\n * const config = defineConstraints({\n * fieldTypes: {\n * dynamicEnum: 'error',\n * dynamicSchema: 'error',\n * },\n * layout: {\n * group: 'error',\n * },\n * });\n * ```\n */\nexport function defineConstraints(config: ConstraintConfig): ResolvedConstraintConfig {\n return mergeWithDefaults(config);\n}\n","import type { ConstraintConfig, FormSpecConfig, ResolvedConstraintConfig } from \"./types.js\";\n\n/**\n * Default constraint configuration that allows all features.\n * All constraints default to \"off\" (allowed).\n */\nexport const DEFAULT_CONSTRAINTS: ResolvedConstraintConfig = {\n fieldTypes: {\n text: \"off\",\n number: \"off\",\n boolean: \"off\",\n staticEnum: \"off\",\n dynamicEnum: \"off\",\n dynamicSchema: \"off\",\n array: \"off\",\n object: \"off\",\n },\n layout: {\n group: \"off\",\n conditionals: \"off\",\n maxNestingDepth: Infinity,\n },\n uiSchema: {\n layouts: {\n VerticalLayout: \"off\",\n HorizontalLayout: \"off\",\n Group: \"off\",\n Categorization: \"off\",\n Category: \"off\",\n },\n rules: {\n enabled: \"off\",\n effects: {\n SHOW: \"off\",\n HIDE: \"off\",\n ENABLE: \"off\",\n DISABLE: \"off\",\n },\n },\n },\n fieldOptions: {\n label: \"off\",\n placeholder: \"off\",\n required: \"off\",\n minValue: \"off\",\n maxValue: \"off\",\n minItems: \"off\",\n maxItems: \"off\",\n },\n controlOptions: {\n format: \"off\",\n readonly: \"off\",\n multi: \"off\",\n showUnfocusedDescription: \"off\",\n hideRequiredAsterisk: \"off\",\n custom: {},\n },\n};\n\n/**\n * Default FormSpec configuration.\n */\nexport const DEFAULT_CONFIG: FormSpecConfig = {\n constraints: DEFAULT_CONSTRAINTS,\n};\n\n/**\n * Merges user constraints with defaults, filling in any missing values.\n */\nexport function mergeWithDefaults(config: ConstraintConfig | undefined): ResolvedConstraintConfig {\n if (!config) {\n return DEFAULT_CONSTRAINTS;\n }\n\n return {\n fieldTypes: {\n ...DEFAULT_CONSTRAINTS.fieldTypes,\n ...config.fieldTypes,\n },\n layout: {\n ...DEFAULT_CONSTRAINTS.layout,\n ...config.layout,\n },\n uiSchema: {\n layouts: {\n ...DEFAULT_CONSTRAINTS.uiSchema.layouts,\n ...config.uiSchema?.layouts,\n },\n rules: {\n enabled: config.uiSchema?.rules?.enabled ?? DEFAULT_CONSTRAINTS.uiSchema.rules.enabled,\n effects: {\n ...DEFAULT_CONSTRAINTS.uiSchema.rules.effects,\n ...config.uiSchema?.rules?.effects,\n },\n },\n },\n fieldOptions: {\n ...DEFAULT_CONSTRAINTS.fieldOptions,\n ...config.fieldOptions,\n },\n controlOptions: {\n ...DEFAULT_CONSTRAINTS.controlOptions,\n ...config.controlOptions,\n custom: {\n ...DEFAULT_CONSTRAINTS.controlOptions.custom,\n ...config.controlOptions?.custom,\n },\n },\n };\n}\n","import type { FieldTypeConstraints, Severity, ValidationIssue } from \"../types.js\";\n\n/**\n * Maps FormSpec field._field values to constraint config keys.\n */\nconst FIELD_TYPE_MAP: Record<string, keyof FieldTypeConstraints> = {\n text: \"text\",\n number: \"number\",\n boolean: \"boolean\",\n enum: \"staticEnum\",\n dynamic_enum: \"dynamicEnum\",\n dynamic_schema: \"dynamicSchema\",\n array: \"array\",\n object: \"object\",\n};\n\n/**\n * Human-readable names for field types.\n */\nconst FIELD_TYPE_NAMES: Record<string, string> = {\n text: \"text field\",\n number: \"number field\",\n boolean: \"boolean field\",\n enum: \"static enum field\",\n dynamic_enum: \"dynamic enum field\",\n dynamic_schema: \"dynamic schema field\",\n array: \"array field\",\n object: \"object field\",\n};\n\n/**\n * Context for field type validation.\n */\nexport interface FieldTypeContext {\n /** The _field discriminator value (e.g., \"text\", \"number\", \"enum\") */\n fieldType: string;\n /** The field name */\n fieldName: string;\n /** Optional path for nested fields */\n path?: string;\n}\n\n/**\n * Validates a field type against constraints.\n *\n * @param context - Information about the field being validated\n * @param constraints - Field type constraints\n * @returns Array of validation issues (empty if valid)\n */\nexport function validateFieldTypes(\n context: FieldTypeContext,\n constraints: FieldTypeConstraints\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n const constraintKey = FIELD_TYPE_MAP[context.fieldType];\n if (!constraintKey) {\n // Unknown field type, skip validation\n return issues;\n }\n\n const severity = constraints[constraintKey];\n if (severity && severity !== \"off\") {\n const fieldTypeName = FIELD_TYPE_NAMES[context.fieldType] ?? context.fieldType;\n issues.push(createFieldTypeIssue(context, fieldTypeName, severity));\n }\n\n return issues;\n}\n\n/**\n * Creates a validation issue for a disallowed field type.\n */\nfunction createFieldTypeIssue(\n context: FieldTypeContext,\n fieldTypeName: string,\n severity: Severity\n): ValidationIssue {\n const path = context.path ?? context.fieldName;\n return {\n code: \"DISALLOWED_FIELD_TYPE\",\n message: `Field \"${context.fieldName}\" uses ${fieldTypeName}, which is not allowed in this project`,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n category: \"fieldTypes\",\n path,\n fieldName: context.fieldName,\n fieldType: context.fieldType,\n };\n}\n\n/**\n * Checks if a field type is allowed by the constraints.\n * Useful for quick checks without generating issues.\n *\n * @param fieldType - The _field discriminator value\n * @param constraints - Field type constraints\n * @returns true if allowed, false if disallowed\n */\nexport function isFieldTypeAllowed(fieldType: string, constraints: FieldTypeConstraints): boolean {\n const constraintKey = FIELD_TYPE_MAP[fieldType];\n if (!constraintKey) {\n return true; // Unknown types are allowed by default\n }\n\n const severity = constraints[constraintKey];\n return !severity || severity === \"off\";\n}\n\n/**\n * Gets the severity level for a field type.\n *\n * @param fieldType - The _field discriminator value\n * @param constraints - Field type constraints\n * @returns Severity level, or \"off\" if not constrained\n */\nexport function getFieldTypeSeverity(\n fieldType: string,\n constraints: FieldTypeConstraints\n): Severity {\n const constraintKey = FIELD_TYPE_MAP[fieldType];\n if (!constraintKey) {\n return \"off\";\n }\n\n return constraints[constraintKey] ?? \"off\";\n}\n","import type { LayoutConstraints, Severity, ValidationIssue } from \"../types.js\";\n\n/**\n * Context for layout validation.\n */\nexport interface LayoutContext {\n /** The type of layout element (\"group\" | \"conditional\") */\n layoutType: \"group\" | \"conditional\";\n /** Optional label for the element (for groups) */\n label?: string;\n /** Current nesting depth */\n depth: number;\n /** Path to this element */\n path?: string;\n}\n\n/**\n * Validates a layout element against constraints.\n *\n * @param context - Information about the layout element\n * @param constraints - Layout constraints\n * @returns Array of validation issues (empty if valid)\n */\nexport function validateLayout(\n context: LayoutContext,\n constraints: LayoutConstraints\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n // Check if groups are allowed\n if (context.layoutType === \"group\") {\n const groupSeverity = constraints.group;\n if (groupSeverity && groupSeverity !== \"off\") {\n issues.push(createGroupIssue(context, groupSeverity));\n }\n }\n\n // Check if conditionals are allowed\n if (context.layoutType === \"conditional\") {\n const conditionalSeverity = constraints.conditionals;\n if (conditionalSeverity && conditionalSeverity !== \"off\") {\n issues.push(createConditionalIssue(context, conditionalSeverity));\n }\n }\n\n // Check nesting depth (applies to both groups and fields within nested structures)\n const maxDepth = constraints.maxNestingDepth;\n if (maxDepth !== undefined && context.depth > maxDepth) {\n issues.push(createNestingDepthIssue(context, maxDepth));\n }\n\n return issues;\n}\n\n/**\n * Creates a validation issue for a disallowed group.\n */\nfunction createGroupIssue(context: LayoutContext, severity: Severity): ValidationIssue {\n const labelInfo = context.label ? ` \"${context.label}\"` : \"\";\n const issue: ValidationIssue = {\n code: \"DISALLOWED_GROUP\",\n message: `Group${labelInfo} is not allowed - visual grouping is not supported in this project`,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n category: \"layout\",\n };\n if (context.path !== undefined) {\n issue.path = context.path;\n }\n return issue;\n}\n\n/**\n * Creates a validation issue for a disallowed conditional.\n */\nfunction createConditionalIssue(context: LayoutContext, severity: Severity): ValidationIssue {\n const issue: ValidationIssue = {\n code: \"DISALLOWED_CONDITIONAL\",\n message: `Conditional visibility (when/is) is not allowed in this project`,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n category: \"layout\",\n };\n if (context.path !== undefined) {\n issue.path = context.path;\n }\n return issue;\n}\n\n/**\n * Creates a validation issue for exceeding nesting depth.\n */\nfunction createNestingDepthIssue(context: LayoutContext, maxDepth: number): ValidationIssue {\n const issue: ValidationIssue = {\n code: \"EXCEEDED_NESTING_DEPTH\",\n message: `Nesting depth ${String(context.depth)} exceeds maximum allowed depth of ${String(maxDepth)}`,\n severity: \"error\",\n category: \"layout\",\n };\n if (context.path !== undefined) {\n issue.path = context.path;\n }\n return issue;\n}\n\n/**\n * Checks if a layout type is allowed by the constraints.\n *\n * @param layoutType - The type of layout element\n * @param constraints - Layout constraints\n * @returns true if allowed, false if disallowed\n */\nexport function isLayoutTypeAllowed(\n layoutType: \"group\" | \"conditional\",\n constraints: LayoutConstraints\n): boolean {\n if (layoutType === \"group\") {\n const severity = constraints.group;\n return !severity || severity === \"off\";\n }\n\n // layoutType === \"conditional\"\n const severity = constraints.conditionals;\n return !severity || severity === \"off\";\n}\n\n/**\n * Checks if a nesting depth is allowed.\n *\n * @param depth - Current nesting depth\n * @param constraints - Layout constraints\n * @returns true if allowed, false if exceeds limit\n */\nexport function isNestingDepthAllowed(depth: number, constraints: LayoutConstraints): boolean {\n const maxDepth = constraints.maxNestingDepth;\n if (maxDepth === undefined) {\n return true;\n }\n return depth <= maxDepth;\n}\n","import type { FieldOptionConstraints, Severity, ValidationIssue } from \"../types.js\";\n\n/**\n * Known field options that can be validated.\n */\nexport type FieldOption =\n | \"label\"\n | \"placeholder\"\n | \"required\"\n | \"minValue\"\n | \"maxValue\"\n | \"minItems\"\n | \"maxItems\";\n\n/**\n * Context for field option validation.\n */\nexport interface FieldOptionsContext {\n /** The field name */\n fieldName: string;\n /** Which options are present on this field */\n presentOptions: FieldOption[];\n /** Path to this field */\n path?: string;\n}\n\n/**\n * Validates field options against constraints.\n *\n * @param context - Information about the field and its options\n * @param constraints - Field option constraints\n * @returns Array of validation issues (empty if valid)\n */\nexport function validateFieldOptions(\n context: FieldOptionsContext,\n constraints: FieldOptionConstraints\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n for (const option of context.presentOptions) {\n const severity = constraints[option];\n if (severity && severity !== \"off\") {\n issues.push(createFieldOptionIssue(context, option, severity));\n }\n }\n\n return issues;\n}\n\n/**\n * Creates a validation issue for a disallowed field option.\n */\nfunction createFieldOptionIssue(\n context: FieldOptionsContext,\n option: FieldOption,\n severity: Severity\n): ValidationIssue {\n const path = context.path ?? context.fieldName;\n return {\n code: \"DISALLOWED_FIELD_OPTION\",\n message: `Field \"${context.fieldName}\" uses the \"${option}\" option, which is not allowed in this project`,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n category: \"fieldOptions\",\n path,\n fieldName: context.fieldName,\n };\n}\n\n/**\n * Extracts which options are present on a field object.\n * Works with FormSpec field types.\n *\n * @param field - A field object with potential options\n * @returns Array of present option names\n */\nexport function extractFieldOptions(field: Record<string, unknown>): FieldOption[] {\n const options: FieldOption[] = [];\n\n if (field[\"label\"] !== undefined) options.push(\"label\");\n if (field[\"placeholder\"] !== undefined) options.push(\"placeholder\");\n if (field[\"required\"] !== undefined) options.push(\"required\");\n // NumberField uses \"min\"/\"max\" in core types, map to \"minValue\"/\"maxValue\" constraints\n if (field[\"min\"] !== undefined || field[\"minValue\"] !== undefined) options.push(\"minValue\");\n if (field[\"max\"] !== undefined || field[\"maxValue\"] !== undefined) options.push(\"maxValue\");\n if (field[\"minItems\"] !== undefined) options.push(\"minItems\");\n if (field[\"maxItems\"] !== undefined) options.push(\"maxItems\");\n\n return options;\n}\n\n/**\n * Checks if a specific field option is allowed.\n *\n * @param option - The option to check\n * @param constraints - Field option constraints\n * @returns true if allowed, false if disallowed\n */\nexport function isFieldOptionAllowed(\n option: FieldOption,\n constraints: FieldOptionConstraints\n): boolean {\n const severity = constraints[option];\n return !severity || severity === \"off\";\n}\n\n/**\n * Gets the severity level for a field option.\n *\n * @param option - The option to check\n * @param constraints - Field option constraints\n * @returns Severity level, or \"off\" if not constrained\n */\nexport function getFieldOptionSeverity(\n option: FieldOption,\n constraints: FieldOptionConstraints\n): Severity {\n return constraints[option] ?? \"off\";\n}\n","import type { FormElement, FormSpec, AnyField } from \"@formspec/core\";\nimport type {\n ConstraintConfig,\n ResolvedConstraintConfig,\n ValidationIssue,\n ValidationResult,\n} from \"../types.js\";\nimport { mergeWithDefaults } from \"../defaults.js\";\nimport { validateFieldTypes } from \"./field-types.js\";\nimport { validateLayout } from \"./layout.js\";\nimport { validateFieldOptions, extractFieldOptions } from \"./field-options.js\";\n\n/**\n * Options for validating FormSpec elements.\n */\nexport interface FormSpecValidationOptions {\n /** Constraint configuration (will be merged with defaults) */\n constraints?: ConstraintConfig;\n}\n\n/**\n * Validates FormSpec elements against constraints.\n *\n * This is the main entry point for validating a form specification\n * against a constraint configuration. It walks through all elements\n * and checks each one against the configured constraints.\n *\n * @param elements - FormSpec elements to validate\n * @param options - Validation options including constraints\n * @returns Validation result with all issues found\n *\n * @example\n * ```ts\n * import { formspec, field, group } from '@formspec/dsl';\n * import { validateFormSpecElements, defineConstraints } from '@formspec/constraints';\n *\n * const form = formspec(\n * group(\"Contact\",\n * field.text(\"name\"),\n * field.dynamicEnum(\"country\", \"countries\"),\n * ),\n * );\n *\n * const result = validateFormSpecElements(form.elements, {\n * constraints: {\n * fieldTypes: { dynamicEnum: 'error' },\n * layout: { group: 'error' },\n * },\n * });\n *\n * if (!result.valid) {\n * console.error('Validation failed:', result.issues);\n * }\n * ```\n */\nexport function validateFormSpecElements(\n elements: readonly FormElement[],\n options: FormSpecValidationOptions = {}\n): ValidationResult {\n const constraints = mergeWithDefaults(options.constraints);\n const issues: ValidationIssue[] = [];\n\n // Walk through all elements\n walkElements(elements, constraints, issues, \"\", 0);\n\n return {\n valid: !issues.some((issue) => issue.severity === \"error\"),\n issues,\n };\n}\n\n/**\n * Validates a complete FormSpec against constraints.\n *\n * @param formSpec - The FormSpec to validate\n * @param options - Validation options including constraints\n * @returns Validation result with all issues found\n */\nexport function validateFormSpec(\n formSpec: FormSpec<readonly FormElement[]>,\n options: FormSpecValidationOptions = {}\n): ValidationResult {\n return validateFormSpecElements(formSpec.elements, options);\n}\n\n/**\n * Recursively walks through FormSpec elements and validates each one.\n */\nfunction walkElements(\n elements: readonly FormElement[],\n constraints: ResolvedConstraintConfig,\n issues: ValidationIssue[],\n pathPrefix: string,\n depth: number\n): void {\n for (const element of elements) {\n const elementPath = pathPrefix;\n\n if (element._type === \"field\") {\n validateField(element, constraints, issues, elementPath, depth);\n } else if (element._type === \"group\") {\n validateGroup(element, constraints, issues, elementPath, depth);\n } else {\n // element._type === \"conditional\"\n validateConditional(element, constraints, issues, elementPath, depth);\n }\n }\n}\n\n/**\n * Validates a field element.\n */\nfunction validateField(\n field: AnyField,\n constraints: ResolvedConstraintConfig,\n issues: ValidationIssue[],\n pathPrefix: string,\n depth: number\n): void {\n const fieldPath = pathPrefix ? `${pathPrefix}/${field.name}` : field.name;\n\n // Validate field type\n const fieldTypeIssues = validateFieldTypes(\n {\n fieldType: field._field,\n fieldName: field.name,\n path: fieldPath,\n },\n constraints.fieldTypes\n );\n issues.push(...fieldTypeIssues);\n\n // Validate field options\n const presentOptions = extractFieldOptions(field as unknown as Record<string, unknown>);\n if (presentOptions.length > 0) {\n const optionIssues = validateFieldOptions(\n {\n fieldName: field.name,\n presentOptions,\n path: fieldPath,\n },\n constraints.fieldOptions\n );\n issues.push(...optionIssues);\n }\n\n // Check nesting depth for array/object fields\n if (field._field === \"array\" || field._field === \"object\") {\n const layoutIssues = validateLayout(\n {\n layoutType: \"group\", // Arrays/objects contribute to nesting depth\n depth: depth + 1,\n path: fieldPath,\n },\n constraints.layout\n );\n // Only add nesting depth issues, not group issues\n issues.push(...layoutIssues.filter((issue) => issue.code === \"EXCEEDED_NESTING_DEPTH\"));\n\n // Recursively validate nested elements\n if (field._field === \"array\" && \"items\" in field) {\n walkElements(\n field.items as readonly FormElement[],\n constraints,\n issues,\n `${fieldPath}[]`,\n depth + 1\n );\n } else if (\"properties\" in field) {\n // field._field === \"object\"\n walkElements(\n field.properties as readonly FormElement[],\n constraints,\n issues,\n fieldPath,\n depth + 1\n );\n }\n }\n}\n\n/**\n * Validates a group element.\n */\nfunction validateGroup(\n group: {\n readonly _type: \"group\";\n readonly label: string;\n readonly elements: readonly FormElement[];\n },\n constraints: ResolvedConstraintConfig,\n issues: ValidationIssue[],\n pathPrefix: string,\n depth: number\n): void {\n const groupPath = pathPrefix ? `${pathPrefix}/[group:${group.label}]` : `[group:${group.label}]`;\n\n // Validate group usage\n const layoutIssues = validateLayout(\n {\n layoutType: \"group\",\n label: group.label,\n depth,\n path: groupPath,\n },\n constraints.layout\n );\n issues.push(...layoutIssues);\n\n // Recursively validate nested elements (groups don't increase nesting depth for schema)\n walkElements(group.elements, constraints, issues, pathPrefix, depth);\n}\n\n/**\n * Validates a conditional element.\n */\nfunction validateConditional(\n conditional: {\n readonly _type: \"conditional\";\n readonly field: string;\n readonly value: unknown;\n readonly elements: readonly FormElement[];\n },\n constraints: ResolvedConstraintConfig,\n issues: ValidationIssue[],\n pathPrefix: string,\n depth: number\n): void {\n const condPath = pathPrefix\n ? `${pathPrefix}/[when:${conditional.field}=${String(conditional.value)}]`\n : `[when:${conditional.field}=${String(conditional.value)}]`;\n\n // Validate conditional usage\n const layoutIssues = validateLayout(\n {\n layoutType: \"conditional\",\n depth,\n path: condPath,\n },\n constraints.layout\n );\n issues.push(...layoutIssues);\n\n // Recursively validate nested elements\n walkElements(conditional.elements, constraints, issues, pathPrefix, depth);\n}\n"],"mappings":"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","names":["parseYaml","severity"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/loader.ts","../src/defaults.ts","../src/validators/field-types.ts","../src/validators/layout.ts","../src/validators/field-options.ts","../src/validators/formspec.ts"],"sourcesContent":["/**\n * \\@formspec/constraints\n *\n * Constraint validation for FormSpec - restrict features based on target\n * environment capabilities.\n *\n * This package provides:\n * - Type definitions for constraint configuration\n * - YAML/TypeScript config file loading\n * - Core validation logic for FormSpec elements\n * - JSON Schema for .formspec.yml validation\n *\n * @example\n * ```ts\n * import { loadConfig, validateFormSpecElements } from '@formspec/constraints';\n * import { formspec, field } from '@formspec/dsl';\n *\n * // Load constraints from .formspec.yml\n * const { config } = await loadConfig();\n *\n * // Create a form\n * const form = formspec(\n * field.text(\"name\"),\n * field.dynamicEnum(\"country\", \"countries\"),\n * );\n *\n * // Validate against constraints\n * const result = validateFormSpecElements(form.elements, { constraints: config });\n *\n * if (!result.valid) {\n * console.error('Validation failed:', result.issues);\n * }\n * ```\n *\n * @packageDocumentation\n */\n\n// Types\nexport type {\n Severity,\n FieldTypeConstraints,\n LayoutConstraints,\n LayoutTypeConstraints,\n RuleEffectConstraints,\n RuleConstraints,\n UISchemaConstraints,\n FieldOptionConstraints,\n ControlOptionConstraints,\n ConstraintConfig,\n ResolvedConstraintConfig,\n ResolvedUISchemaConstraints,\n ResolvedRuleConstraints,\n FormSpecConfig,\n ValidationIssue,\n ValidationResult,\n} from \"./types.js\";\n\n// Config loading\nexport {\n loadConfig,\n loadConfigFromString,\n defineConstraints,\n type LoadConfigOptions,\n type LoadConfigResult,\n} from \"./loader.js\";\n\n// Defaults\nexport { DEFAULT_CONSTRAINTS, DEFAULT_CONFIG, mergeWithDefaults } from \"./defaults.js\";\n\n// Validators\nexport {\n validateFormSpecElements,\n validateFormSpec,\n type FormSpecValidationOptions,\n} from \"./validators/formspec.js\";\n\nexport {\n validateFieldTypes,\n isFieldTypeAllowed,\n getFieldTypeSeverity,\n type FieldTypeContext,\n} from \"./validators/field-types.js\";\n\nexport {\n validateLayout,\n isLayoutTypeAllowed,\n isNestingDepthAllowed,\n type LayoutContext,\n} from \"./validators/layout.js\";\n\nexport {\n validateFieldOptions,\n extractFieldOptions,\n isFieldOptionAllowed,\n getFieldOptionSeverity,\n type FieldOptionsContext,\n type FieldOption,\n} from \"./validators/field-options.js\";\n","import { readFile } from \"node:fs/promises\";\nimport { resolve, dirname } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { FormSpecConfig, ConstraintConfig, ResolvedConstraintConfig } from \"./types.js\";\nimport { mergeWithDefaults } from \"./defaults.js\";\n\n/**\n * Default config file names to search for (in order of priority).\n */\nconst CONFIG_FILE_NAMES = [\".formspec.yml\", \".formspec.yaml\", \"formspec.yml\"];\n\n/**\n * Options for loading configuration.\n *\n * @public\n */\nexport interface LoadConfigOptions {\n /**\n * The directory to search for config files.\n * Defaults to process.cwd().\n */\n cwd?: string;\n\n /**\n * Explicit path to a config file.\n * If provided, skips searching for default config file names.\n */\n configPath?: string;\n\n /**\n * Whether to search parent directories for config files.\n * Defaults to true.\n */\n searchParents?: boolean;\n}\n\n/**\n * Result of loading configuration.\n *\n * @public\n */\nexport interface LoadConfigResult {\n /** The loaded and merged configuration */\n config: ResolvedConstraintConfig;\n /** The path to the config file that was loaded (if any) */\n configPath: string | null;\n /** Whether a config file was found */\n found: boolean;\n}\n\n/**\n * Searches for a config file in the given directory and optionally parent directories.\n */\nasync function findConfigFile(startDir: string, searchParents: boolean): Promise<string | null> {\n let currentDir = resolve(startDir);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- intentional infinite loop with break conditions\n while (true) {\n for (const fileName of CONFIG_FILE_NAMES) {\n const filePath = resolve(currentDir, fileName);\n try {\n await readFile(filePath);\n return filePath;\n } catch {\n // File doesn't exist, continue searching\n }\n }\n\n if (!searchParents) {\n break;\n }\n\n const parentDir = dirname(currentDir);\n // Reached filesystem root when dirname returns same path\n if (parentDir === currentDir) {\n break;\n }\n currentDir = parentDir;\n }\n\n return null;\n}\n\n/**\n * Parses a YAML config file and returns the FormSpecConfig.\n */\nasync function parseConfigFile(filePath: string): Promise<FormSpecConfig> {\n const content = await readFile(filePath, \"utf-8\");\n const parsed = parseYaml(content) as unknown;\n\n if (parsed === null || parsed === undefined) {\n return {};\n }\n\n if (typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`Invalid config file at ${filePath}: expected an object, got ${typeof parsed}`);\n }\n\n return parsed as FormSpecConfig;\n}\n\n/**\n * Loads FormSpec constraint configuration from a .formspec.yml file.\n *\n * @param options - Options for loading configuration\n * @returns The loaded configuration with defaults applied\n *\n * @example\n * ```ts\n * // Load from current directory (searches for .formspec.yml)\n * const result = await loadConfig();\n *\n * // Load from specific directory\n * const result = await loadConfig({ cwd: '/path/to/project' });\n *\n * // Load from specific file\n * const result = await loadConfig({ configPath: '/path/to/config.yml' });\n * ```\n *\n * @public\n */\nexport async function loadConfig(options: LoadConfigOptions = {}): Promise<LoadConfigResult> {\n const { cwd = process.cwd(), configPath, searchParents = true } = options;\n\n let resolvedPath: string | null = null;\n\n if (configPath) {\n resolvedPath = resolve(cwd, configPath);\n try {\n await readFile(resolvedPath);\n } catch {\n throw new Error(`Config file not found at ${resolvedPath}`);\n }\n } else {\n resolvedPath = await findConfigFile(cwd, searchParents);\n }\n\n if (!resolvedPath) {\n return {\n config: mergeWithDefaults(undefined),\n configPath: null,\n found: false,\n };\n }\n\n const fileConfig = await parseConfigFile(resolvedPath);\n const config = mergeWithDefaults(fileConfig.constraints);\n\n return {\n config,\n configPath: resolvedPath,\n found: true,\n };\n}\n\n/**\n * Synchronously loads config from a pre-parsed YAML string.\n * Useful for testing or when config is already available.\n *\n * @param yamlContent - The YAML content to parse\n * @returns The parsed and merged configuration\n *\n * @public\n */\nexport function loadConfigFromString(yamlContent: string): ResolvedConstraintConfig {\n const parsed = parseYaml(yamlContent) as FormSpecConfig | null | undefined;\n\n if (parsed === null || parsed === undefined) {\n return mergeWithDefaults(undefined);\n }\n\n if (typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`Invalid config content: expected an object, got ${typeof parsed}`);\n }\n\n return mergeWithDefaults(parsed.constraints);\n}\n\n/**\n * Creates a constraint configuration directly from an object.\n * Useful for programmatic configuration without YAML.\n *\n * @param config - Partial constraint configuration\n * @returns Complete configuration with defaults applied\n *\n * @example\n * ```ts\n * const config = defineConstraints({\n * fieldTypes: {\n * dynamicEnum: 'error',\n * dynamicSchema: 'error',\n * },\n * layout: {\n * group: 'error',\n * },\n * });\n * ```\n *\n * @public\n */\nexport function defineConstraints(config: ConstraintConfig): ResolvedConstraintConfig {\n return mergeWithDefaults(config);\n}\n","import type { ConstraintConfig, FormSpecConfig, ResolvedConstraintConfig } from \"./types.js\";\n\n/**\n * Default constraint configuration that allows all features.\n * All constraints default to \"off\" (allowed).\n *\n * @public\n */\nexport const DEFAULT_CONSTRAINTS: ResolvedConstraintConfig = {\n fieldTypes: {\n text: \"off\",\n number: \"off\",\n boolean: \"off\",\n staticEnum: \"off\",\n dynamicEnum: \"off\",\n dynamicSchema: \"off\",\n array: \"off\",\n object: \"off\",\n },\n layout: {\n group: \"off\",\n conditionals: \"off\",\n maxNestingDepth: Infinity,\n },\n uiSchema: {\n layouts: {\n VerticalLayout: \"off\",\n HorizontalLayout: \"off\",\n Group: \"off\",\n Categorization: \"off\",\n Category: \"off\",\n },\n rules: {\n enabled: \"off\",\n effects: {\n SHOW: \"off\",\n HIDE: \"off\",\n ENABLE: \"off\",\n DISABLE: \"off\",\n },\n },\n },\n fieldOptions: {\n label: \"off\",\n placeholder: \"off\",\n required: \"off\",\n minValue: \"off\",\n maxValue: \"off\",\n minItems: \"off\",\n maxItems: \"off\",\n },\n controlOptions: {\n format: \"off\",\n readonly: \"off\",\n multi: \"off\",\n showUnfocusedDescription: \"off\",\n hideRequiredAsterisk: \"off\",\n custom: {},\n },\n};\n\n/**\n * Default FormSpec configuration.\n *\n * @public\n */\nexport const DEFAULT_CONFIG: FormSpecConfig = {\n constraints: DEFAULT_CONSTRAINTS,\n};\n\n/**\n * Merges user constraints with defaults, filling in any missing values.\n *\n * @public\n */\nexport function mergeWithDefaults(config: ConstraintConfig | undefined): ResolvedConstraintConfig {\n if (!config) {\n return DEFAULT_CONSTRAINTS;\n }\n\n return {\n fieldTypes: {\n ...DEFAULT_CONSTRAINTS.fieldTypes,\n ...config.fieldTypes,\n },\n layout: {\n ...DEFAULT_CONSTRAINTS.layout,\n ...config.layout,\n },\n uiSchema: {\n layouts: {\n ...DEFAULT_CONSTRAINTS.uiSchema.layouts,\n ...config.uiSchema?.layouts,\n },\n rules: {\n enabled: config.uiSchema?.rules?.enabled ?? DEFAULT_CONSTRAINTS.uiSchema.rules.enabled,\n effects: {\n ...DEFAULT_CONSTRAINTS.uiSchema.rules.effects,\n ...config.uiSchema?.rules?.effects,\n },\n },\n },\n fieldOptions: {\n ...DEFAULT_CONSTRAINTS.fieldOptions,\n ...config.fieldOptions,\n },\n controlOptions: {\n ...DEFAULT_CONSTRAINTS.controlOptions,\n ...config.controlOptions,\n custom: {\n ...DEFAULT_CONSTRAINTS.controlOptions.custom,\n ...config.controlOptions?.custom,\n },\n },\n };\n}\n","import type { FieldTypeConstraints, Severity, ValidationIssue } from \"../types.js\";\n\n/**\n * Maps FormSpec field._field values to constraint config keys.\n */\nconst FIELD_TYPE_MAP: Record<string, keyof FieldTypeConstraints> = {\n text: \"text\",\n number: \"number\",\n boolean: \"boolean\",\n enum: \"staticEnum\",\n dynamic_enum: \"dynamicEnum\",\n dynamic_schema: \"dynamicSchema\",\n array: \"array\",\n object: \"object\",\n};\n\n/**\n * Human-readable names for field types.\n */\nconst FIELD_TYPE_NAMES: Record<string, string> = {\n text: \"text field\",\n number: \"number field\",\n boolean: \"boolean field\",\n enum: \"static enum field\",\n dynamic_enum: \"dynamic enum field\",\n dynamic_schema: \"dynamic schema field\",\n array: \"array field\",\n object: \"object field\",\n};\n\n/**\n * Context for field type validation.\n *\n * @public\n */\nexport interface FieldTypeContext {\n /** The _field discriminator value (e.g., \"text\", \"number\", \"enum\") */\n fieldType: string;\n /** The field name */\n fieldName: string;\n /** Optional path for nested fields */\n path?: string;\n}\n\n/**\n * Validates a field type against constraints.\n *\n * @param context - Information about the field being validated\n * @param constraints - Field type constraints\n * @returns Array of validation issues (empty if valid)\n *\n * @public\n */\nexport function validateFieldTypes(\n context: FieldTypeContext,\n constraints: FieldTypeConstraints\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n const constraintKey = FIELD_TYPE_MAP[context.fieldType];\n if (!constraintKey) {\n // Unknown field type, skip validation\n return issues;\n }\n\n const severity = constraints[constraintKey];\n if (severity && severity !== \"off\") {\n const fieldTypeName = FIELD_TYPE_NAMES[context.fieldType] ?? context.fieldType;\n issues.push(createFieldTypeIssue(context, fieldTypeName, severity));\n }\n\n return issues;\n}\n\n/**\n * Creates a validation issue for a disallowed field type.\n */\nfunction createFieldTypeIssue(\n context: FieldTypeContext,\n fieldTypeName: string,\n severity: Severity\n): ValidationIssue {\n const path = context.path ?? context.fieldName;\n return {\n code: \"DISALLOWED_FIELD_TYPE\",\n message: `Field \"${context.fieldName}\" uses ${fieldTypeName}, which is not allowed in this project`,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n category: \"fieldTypes\",\n path,\n fieldName: context.fieldName,\n fieldType: context.fieldType,\n };\n}\n\n/**\n * Checks if a field type is allowed by the constraints.\n * Useful for quick checks without generating issues.\n *\n * @param fieldType - The _field discriminator value\n * @param constraints - Field type constraints\n * @returns true if allowed, false if disallowed\n *\n * @public\n */\nexport function isFieldTypeAllowed(fieldType: string, constraints: FieldTypeConstraints): boolean {\n const constraintKey = FIELD_TYPE_MAP[fieldType];\n if (!constraintKey) {\n return true; // Unknown types are allowed by default\n }\n\n const severity = constraints[constraintKey];\n return !severity || severity === \"off\";\n}\n\n/**\n * Gets the severity level for a field type.\n *\n * @param fieldType - The _field discriminator value\n * @param constraints - Field type constraints\n * @returns Severity level, or \"off\" if not constrained\n *\n * @public\n */\nexport function getFieldTypeSeverity(\n fieldType: string,\n constraints: FieldTypeConstraints\n): Severity {\n const constraintKey = FIELD_TYPE_MAP[fieldType];\n if (!constraintKey) {\n return \"off\";\n }\n\n return constraints[constraintKey] ?? \"off\";\n}\n","import type { LayoutConstraints, Severity, ValidationIssue } from \"../types.js\";\n\n/**\n * Context for layout validation.\n *\n * @public\n */\nexport interface LayoutContext {\n /** The type of layout element (\"group\" | \"conditional\") */\n layoutType: \"group\" | \"conditional\";\n /** Optional label for the element (for groups) */\n label?: string;\n /** Current nesting depth */\n depth: number;\n /** Path to this element */\n path?: string;\n}\n\n/**\n * Validates a layout element against constraints.\n *\n * @param context - Information about the layout element\n * @param constraints - Layout constraints\n * @returns Array of validation issues (empty if valid)\n *\n * @public\n */\nexport function validateLayout(\n context: LayoutContext,\n constraints: LayoutConstraints\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n // Check if groups are allowed\n if (context.layoutType === \"group\") {\n const groupSeverity = constraints.group;\n if (groupSeverity && groupSeverity !== \"off\") {\n issues.push(createGroupIssue(context, groupSeverity));\n }\n }\n\n // Check if conditionals are allowed\n if (context.layoutType === \"conditional\") {\n const conditionalSeverity = constraints.conditionals;\n if (conditionalSeverity && conditionalSeverity !== \"off\") {\n issues.push(createConditionalIssue(context, conditionalSeverity));\n }\n }\n\n // Check nesting depth (applies to both groups and fields within nested structures)\n const maxDepth = constraints.maxNestingDepth;\n if (maxDepth !== undefined && context.depth > maxDepth) {\n issues.push(createNestingDepthIssue(context, maxDepth));\n }\n\n return issues;\n}\n\n/**\n * Creates a validation issue for a disallowed group.\n */\nfunction createGroupIssue(context: LayoutContext, severity: Severity): ValidationIssue {\n const labelInfo = context.label ? ` \"${context.label}\"` : \"\";\n const issue: ValidationIssue = {\n code: \"DISALLOWED_GROUP\",\n message: `Group${labelInfo} is not allowed - visual grouping is not supported in this project`,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n category: \"layout\",\n };\n if (context.path !== undefined) {\n issue.path = context.path;\n }\n return issue;\n}\n\n/**\n * Creates a validation issue for a disallowed conditional.\n */\nfunction createConditionalIssue(context: LayoutContext, severity: Severity): ValidationIssue {\n const issue: ValidationIssue = {\n code: \"DISALLOWED_CONDITIONAL\",\n message: `Conditional visibility (when/is) is not allowed in this project`,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n category: \"layout\",\n };\n if (context.path !== undefined) {\n issue.path = context.path;\n }\n return issue;\n}\n\n/**\n * Creates a validation issue for exceeding nesting depth.\n */\nfunction createNestingDepthIssue(context: LayoutContext, maxDepth: number): ValidationIssue {\n const issue: ValidationIssue = {\n code: \"EXCEEDED_NESTING_DEPTH\",\n message: `Nesting depth ${String(context.depth)} exceeds maximum allowed depth of ${String(maxDepth)}`,\n severity: \"error\",\n category: \"layout\",\n };\n if (context.path !== undefined) {\n issue.path = context.path;\n }\n return issue;\n}\n\n/**\n * Checks if a layout type is allowed by the constraints.\n *\n * @param layoutType - The type of layout element\n * @param constraints - Layout constraints\n * @returns true if allowed, false if disallowed\n *\n * @public\n */\nexport function isLayoutTypeAllowed(\n layoutType: \"group\" | \"conditional\",\n constraints: LayoutConstraints\n): boolean {\n if (layoutType === \"group\") {\n const severity = constraints.group;\n return !severity || severity === \"off\";\n }\n\n // layoutType === \"conditional\"\n const severity = constraints.conditionals;\n return !severity || severity === \"off\";\n}\n\n/**\n * Checks if a nesting depth is allowed.\n *\n * @param depth - Current nesting depth\n * @param constraints - Layout constraints\n * @returns true if allowed, false if exceeds limit\n *\n * @public\n */\nexport function isNestingDepthAllowed(depth: number, constraints: LayoutConstraints): boolean {\n const maxDepth = constraints.maxNestingDepth;\n if (maxDepth === undefined) {\n return true;\n }\n return depth <= maxDepth;\n}\n","import type { FieldOptionConstraints, Severity, ValidationIssue } from \"../types.js\";\n\n/**\n * Known field options that can be validated.\n *\n * @public\n */\nexport type FieldOption =\n | \"label\"\n | \"placeholder\"\n | \"required\"\n | \"minValue\"\n | \"maxValue\"\n | \"minItems\"\n | \"maxItems\";\n\n/**\n * Context for field option validation.\n *\n * @public\n */\nexport interface FieldOptionsContext {\n /** The field name */\n fieldName: string;\n /** Which options are present on this field */\n presentOptions: FieldOption[];\n /** Path to this field */\n path?: string;\n}\n\n/**\n * Validates field options against constraints.\n *\n * @param context - Information about the field and its options\n * @param constraints - Field option constraints\n * @returns Array of validation issues (empty if valid)\n *\n * @public\n */\nexport function validateFieldOptions(\n context: FieldOptionsContext,\n constraints: FieldOptionConstraints\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n for (const option of context.presentOptions) {\n const severity = constraints[option];\n if (severity && severity !== \"off\") {\n issues.push(createFieldOptionIssue(context, option, severity));\n }\n }\n\n return issues;\n}\n\n/**\n * Creates a validation issue for a disallowed field option.\n */\nfunction createFieldOptionIssue(\n context: FieldOptionsContext,\n option: FieldOption,\n severity: Severity\n): ValidationIssue {\n const path = context.path ?? context.fieldName;\n return {\n code: \"DISALLOWED_FIELD_OPTION\",\n message: `Field \"${context.fieldName}\" uses the \"${option}\" option, which is not allowed in this project`,\n severity: severity === \"error\" ? \"error\" : \"warning\",\n category: \"fieldOptions\",\n path,\n fieldName: context.fieldName,\n };\n}\n\n/**\n * Extracts which options are present on a field object.\n * Works with FormSpec field types.\n *\n * @param field - A field object with potential options\n * @returns Array of present option names\n *\n * @public\n */\nexport function extractFieldOptions(field: Record<string, unknown>): FieldOption[] {\n const options: FieldOption[] = [];\n\n if (field[\"label\"] !== undefined) options.push(\"label\");\n if (field[\"placeholder\"] !== undefined) options.push(\"placeholder\");\n if (field[\"required\"] !== undefined) options.push(\"required\");\n // NumberField uses \"min\"/\"max\" in core types, map to \"minValue\"/\"maxValue\" constraints\n if (field[\"min\"] !== undefined || field[\"minValue\"] !== undefined) options.push(\"minValue\");\n if (field[\"max\"] !== undefined || field[\"maxValue\"] !== undefined) options.push(\"maxValue\");\n if (field[\"minItems\"] !== undefined) options.push(\"minItems\");\n if (field[\"maxItems\"] !== undefined) options.push(\"maxItems\");\n\n return options;\n}\n\n/**\n * Checks if a specific field option is allowed.\n *\n * @param option - The option to check\n * @param constraints - Field option constraints\n * @returns true if allowed, false if disallowed\n *\n * @public\n */\nexport function isFieldOptionAllowed(\n option: FieldOption,\n constraints: FieldOptionConstraints\n): boolean {\n const severity = constraints[option];\n return !severity || severity === \"off\";\n}\n\n/**\n * Gets the severity level for a field option.\n *\n * @param option - The option to check\n * @param constraints - Field option constraints\n * @returns Severity level, or \"off\" if not constrained\n *\n * @public\n */\nexport function getFieldOptionSeverity(\n option: FieldOption,\n constraints: FieldOptionConstraints\n): Severity {\n return constraints[option] ?? \"off\";\n}\n","import type { FormElement, FormSpec, AnyField } from \"@formspec/core\";\nimport type {\n ConstraintConfig,\n ResolvedConstraintConfig,\n ValidationIssue,\n ValidationResult,\n} from \"../types.js\";\nimport { mergeWithDefaults } from \"../defaults.js\";\nimport { validateFieldTypes } from \"./field-types.js\";\nimport { validateLayout } from \"./layout.js\";\nimport { validateFieldOptions, extractFieldOptions } from \"./field-options.js\";\n\n/**\n * Options for validating FormSpec elements.\n *\n * @public\n */\nexport interface FormSpecValidationOptions {\n /** Constraint configuration (will be merged with defaults) */\n constraints?: ConstraintConfig;\n}\n\n/**\n * Validates FormSpec elements against constraints.\n *\n * This is the main entry point for validating a form specification\n * against a constraint configuration. It walks through all elements\n * and checks each one against the configured constraints.\n *\n * @param elements - FormSpec elements to validate\n * @param options - Validation options including constraints\n * @returns Validation result with all issues found\n *\n * @example\n * ```ts\n * import { formspec, field, group } from '@formspec/dsl';\n * import { validateFormSpecElements, defineConstraints } from '@formspec/constraints';\n *\n * const form = formspec(\n * group(\"Contact\",\n * field.text(\"name\"),\n * field.dynamicEnum(\"country\", \"countries\"),\n * ),\n * );\n *\n * const result = validateFormSpecElements(form.elements, {\n * constraints: {\n * fieldTypes: { dynamicEnum: 'error' },\n * layout: { group: 'error' },\n * },\n * });\n *\n * if (!result.valid) {\n * console.error('Validation failed:', result.issues);\n * }\n * ```\n *\n * @public\n */\nexport function validateFormSpecElements(\n elements: readonly FormElement[],\n options: FormSpecValidationOptions = {}\n): ValidationResult {\n const constraints = mergeWithDefaults(options.constraints);\n const issues: ValidationIssue[] = [];\n\n // Walk through all elements\n walkElements(elements, constraints, issues, \"\", 0);\n\n return {\n valid: !issues.some((issue) => issue.severity === \"error\"),\n issues,\n };\n}\n\n/**\n * Validates a complete FormSpec against constraints.\n *\n * @param formSpec - The FormSpec to validate\n * @param options - Validation options including constraints\n * @returns Validation result with all issues found\n *\n * @public\n */\nexport function validateFormSpec(\n formSpec: FormSpec<readonly FormElement[]>,\n options: FormSpecValidationOptions = {}\n): ValidationResult {\n return validateFormSpecElements(formSpec.elements, options);\n}\n\n/**\n * Recursively walks through FormSpec elements and validates each one.\n */\nfunction walkElements(\n elements: readonly FormElement[],\n constraints: ResolvedConstraintConfig,\n issues: ValidationIssue[],\n pathPrefix: string,\n depth: number\n): void {\n for (const element of elements) {\n const elementPath = pathPrefix;\n\n if (element._type === \"field\") {\n validateField(element, constraints, issues, elementPath, depth);\n } else if (element._type === \"group\") {\n validateGroup(element, constraints, issues, elementPath, depth);\n } else {\n // element._type === \"conditional\"\n validateConditional(element, constraints, issues, elementPath, depth);\n }\n }\n}\n\n/**\n * Validates a field element.\n */\nfunction validateField(\n field: AnyField,\n constraints: ResolvedConstraintConfig,\n issues: ValidationIssue[],\n pathPrefix: string,\n depth: number\n): void {\n const fieldPath = pathPrefix ? `${pathPrefix}/${field.name}` : field.name;\n\n // Validate field type\n const fieldTypeIssues = validateFieldTypes(\n {\n fieldType: field._field,\n fieldName: field.name,\n path: fieldPath,\n },\n constraints.fieldTypes\n );\n issues.push(...fieldTypeIssues);\n\n // Validate field options\n const presentOptions = extractFieldOptions(field as unknown as Record<string, unknown>);\n if (presentOptions.length > 0) {\n const optionIssues = validateFieldOptions(\n {\n fieldName: field.name,\n presentOptions,\n path: fieldPath,\n },\n constraints.fieldOptions\n );\n issues.push(...optionIssues);\n }\n\n // Check nesting depth for array/object fields\n if (field._field === \"array\" || field._field === \"object\") {\n const layoutIssues = validateLayout(\n {\n layoutType: \"group\", // Arrays/objects contribute to nesting depth\n depth: depth + 1,\n path: fieldPath,\n },\n constraints.layout\n );\n // Only add nesting depth issues, not group issues\n issues.push(...layoutIssues.filter((issue) => issue.code === \"EXCEEDED_NESTING_DEPTH\"));\n\n // Recursively validate nested elements\n if (field._field === \"array\" && \"items\" in field) {\n walkElements(\n field.items as readonly FormElement[],\n constraints,\n issues,\n `${fieldPath}[]`,\n depth + 1\n );\n } else if (\"properties\" in field) {\n // field._field === \"object\"\n walkElements(\n field.properties as readonly FormElement[],\n constraints,\n issues,\n fieldPath,\n depth + 1\n );\n }\n }\n}\n\n/**\n * Validates a group element.\n */\nfunction validateGroup(\n group: {\n readonly _type: \"group\";\n readonly label: string;\n readonly elements: readonly FormElement[];\n },\n constraints: ResolvedConstraintConfig,\n issues: ValidationIssue[],\n pathPrefix: string,\n depth: number\n): void {\n const groupPath = pathPrefix ? `${pathPrefix}/[group:${group.label}]` : `[group:${group.label}]`;\n\n // Validate group usage\n const layoutIssues = validateLayout(\n {\n layoutType: \"group\",\n label: group.label,\n depth,\n path: groupPath,\n },\n constraints.layout\n );\n issues.push(...layoutIssues);\n\n // Recursively validate nested elements (groups don't increase nesting depth for schema)\n walkElements(group.elements, constraints, issues, pathPrefix, depth);\n}\n\n/**\n * Validates a conditional element.\n */\nfunction validateConditional(\n conditional: {\n readonly _type: \"conditional\";\n readonly field: string;\n readonly value: unknown;\n readonly elements: readonly FormElement[];\n },\n constraints: ResolvedConstraintConfig,\n issues: ValidationIssue[],\n pathPrefix: string,\n depth: number\n): void {\n const condPath = pathPrefix\n ? `${pathPrefix}/[when:${conditional.field}=${String(conditional.value)}]`\n : `[when:${conditional.field}=${String(conditional.value)}]`;\n\n // Validate conditional usage\n const layoutIssues = validateLayout(\n {\n layoutType: \"conditional\",\n depth,\n path: condPath,\n },\n constraints.layout\n );\n issues.push(...layoutIssues);\n\n // Recursively validate nested elements\n walkElements(conditional.elements, constraints, issues, pathPrefix, depth);\n}\n"],"mappings":"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","names":["parseYaml","severity"]}