@formspec/constraints 0.1.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +263 -0
  2. package/dist/__tests__/loader.test.d.ts +2 -0
  3. package/dist/__tests__/loader.test.d.ts.map +1 -0
  4. package/dist/__tests__/loader.test.js +133 -0
  5. package/dist/__tests__/loader.test.js.map +1 -0
  6. package/dist/__tests__/validators.test.d.ts +2 -0
  7. package/dist/__tests__/validators.test.d.ts.map +1 -0
  8. package/dist/__tests__/validators.test.js +404 -0
  9. package/dist/__tests__/validators.test.js.map +1 -0
  10. package/dist/browser.d.ts +51 -0
  11. package/dist/browser.d.ts.map +1 -0
  12. package/dist/browser.js +65 -0
  13. package/dist/browser.js.map +1 -0
  14. package/dist/constraints.d.ts +544 -0
  15. package/dist/defaults.d.ts +15 -0
  16. package/dist/defaults.d.ts.map +1 -0
  17. package/dist/defaults.js +106 -0
  18. package/dist/defaults.js.map +1 -0
  19. package/dist/index.d.ts +44 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +46 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/loader.d.ts +81 -0
  24. package/dist/loader.d.ts.map +1 -0
  25. package/dist/loader.js +140 -0
  26. package/dist/loader.js.map +1 -0
  27. package/dist/types.d.ts +200 -0
  28. package/dist/types.d.ts.map +1 -0
  29. package/dist/types.js +2 -0
  30. package/dist/types.js.map +1 -0
  31. package/dist/validators/field-options.d.ts +49 -0
  32. package/dist/validators/field-options.d.ts.map +1 -0
  33. package/dist/validators/field-options.js +79 -0
  34. package/dist/validators/field-options.js.map +1 -0
  35. package/dist/validators/field-types.d.ts +38 -0
  36. package/dist/validators/field-types.d.ts.map +1 -0
  37. package/dist/validators/field-types.js +93 -0
  38. package/dist/validators/field-types.js.map +1 -0
  39. package/dist/validators/formspec.d.ts +54 -0
  40. package/dist/validators/formspec.d.ts.map +1 -0
  41. package/dist/validators/formspec.js +152 -0
  42. package/dist/validators/formspec.js.map +1 -0
  43. package/dist/validators/index.d.ts +5 -0
  44. package/dist/validators/index.d.ts.map +1 -0
  45. package/dist/validators/index.js +5 -0
  46. package/dist/validators/index.js.map +1 -0
  47. package/dist/validators/layout.d.ts +39 -0
  48. package/dist/validators/layout.d.ts.map +1 -0
  49. package/dist/validators/layout.js +107 -0
  50. package/dist/validators/layout.js.map +1 -0
  51. package/formspec.schema.json +245 -0
  52. package/package.json +57 -0
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @formspec/constraints
3
+ *
4
+ * Constraint validation for FormSpec - restrict features based on target
5
+ * environment capabilities.
6
+ *
7
+ * This package provides:
8
+ * - Type definitions for constraint configuration
9
+ * - YAML/TypeScript config file loading
10
+ * - Core validation logic for FormSpec elements
11
+ * - JSON Schema for .formspec.yml validation
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { loadConfig, validateFormSpecElements } from '@formspec/constraints';
16
+ * import { formspec, field } from '@formspec/dsl';
17
+ *
18
+ * // Load constraints from .formspec.yml
19
+ * const { config } = await loadConfig();
20
+ *
21
+ * // Create a form
22
+ * const form = formspec(
23
+ * field.text("name"),
24
+ * field.dynamicEnum("country", "countries"),
25
+ * );
26
+ *
27
+ * // Validate against constraints
28
+ * const result = validateFormSpecElements(form.elements, { constraints: config });
29
+ *
30
+ * if (!result.valid) {
31
+ * console.error('Validation failed:', result.issues);
32
+ * }
33
+ * ```
34
+ *
35
+ * @packageDocumentation
36
+ */
37
+ export type { Severity, FieldTypeConstraints, LayoutConstraints, LayoutTypeConstraints, RuleEffectConstraints, RuleConstraints, UISchemaConstraints, FieldOptionConstraints, ControlOptionConstraints, ConstraintConfig, ResolvedConstraintConfig, ResolvedUISchemaConstraints, ResolvedRuleConstraints, FormSpecConfig, ValidationIssue, ValidationResult, } from "./types.js";
38
+ export { loadConfig, loadConfigFromString, defineConstraints, type LoadConfigOptions, type LoadConfigResult, } from "./loader.js";
39
+ export { DEFAULT_CONSTRAINTS, DEFAULT_CONFIG, mergeWithDefaults } from "./defaults.js";
40
+ export { validateFormSpecElements, validateFormSpec, type FormSpecValidationOptions, } from "./validators/formspec.js";
41
+ export { validateFieldTypes, isFieldTypeAllowed, getFieldTypeSeverity, type FieldTypeContext, } from "./validators/field-types.js";
42
+ export { validateLayout, isLayoutTypeAllowed, isNestingDepthAllowed, type LayoutContext, } from "./validators/layout.js";
43
+ export { validateFieldOptions, extractFieldOptions, isFieldOptionAllowed, getFieldOptionSeverity, type FieldOptionsContext, type FieldOption, } from "./validators/field-options.js";
44
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,YAAY,EACV,QAAQ,EACR,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,EACxB,2BAA2B,EAC3B,uBAAuB,EACvB,cAAc,EACd,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGvF,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,yBAAyB,GAC/B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,gBAAgB,GACtB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,aAAa,GACnB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,WAAW,GACjB,MAAM,+BAA+B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @formspec/constraints
3
+ *
4
+ * Constraint validation for FormSpec - restrict features based on target
5
+ * environment capabilities.
6
+ *
7
+ * This package provides:
8
+ * - Type definitions for constraint configuration
9
+ * - YAML/TypeScript config file loading
10
+ * - Core validation logic for FormSpec elements
11
+ * - JSON Schema for .formspec.yml validation
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { loadConfig, validateFormSpecElements } from '@formspec/constraints';
16
+ * import { formspec, field } from '@formspec/dsl';
17
+ *
18
+ * // Load constraints from .formspec.yml
19
+ * const { config } = await loadConfig();
20
+ *
21
+ * // Create a form
22
+ * const form = formspec(
23
+ * field.text("name"),
24
+ * field.dynamicEnum("country", "countries"),
25
+ * );
26
+ *
27
+ * // Validate against constraints
28
+ * const result = validateFormSpecElements(form.elements, { constraints: config });
29
+ *
30
+ * if (!result.valid) {
31
+ * console.error('Validation failed:', result.issues);
32
+ * }
33
+ * ```
34
+ *
35
+ * @packageDocumentation
36
+ */
37
+ // Config loading
38
+ export { loadConfig, loadConfigFromString, defineConstraints, } from "./loader.js";
39
+ // Defaults
40
+ export { DEFAULT_CONSTRAINTS, DEFAULT_CONFIG, mergeWithDefaults } from "./defaults.js";
41
+ // Validators
42
+ export { validateFormSpecElements, validateFormSpec, } from "./validators/formspec.js";
43
+ export { validateFieldTypes, isFieldTypeAllowed, getFieldTypeSeverity, } from "./validators/field-types.js";
44
+ export { validateLayout, isLayoutTypeAllowed, isNestingDepthAllowed, } from "./validators/layout.js";
45
+ export { validateFieldOptions, extractFieldOptions, isFieldOptionAllowed, getFieldOptionSeverity, } from "./validators/field-options.js";
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAsBH,iBAAiB;AACjB,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,iBAAiB,GAGlB,MAAM,aAAa,CAAC;AAErB,WAAW;AACX,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvF,aAAa;AACb,OAAO,EACL,wBAAwB,EACxB,gBAAgB,GAEjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,GAErB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,qBAAqB,GAEtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GAGvB,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,81 @@
1
+ import type { ConstraintConfig, ResolvedConstraintConfig } from "./types.js";
2
+ /**
3
+ * Options for loading configuration.
4
+ */
5
+ export interface LoadConfigOptions {
6
+ /**
7
+ * The directory to search for config files.
8
+ * Defaults to process.cwd().
9
+ */
10
+ cwd?: string;
11
+ /**
12
+ * Explicit path to a config file.
13
+ * If provided, skips searching for default config file names.
14
+ */
15
+ configPath?: string;
16
+ /**
17
+ * Whether to search parent directories for config files.
18
+ * Defaults to true.
19
+ */
20
+ searchParents?: boolean;
21
+ }
22
+ /**
23
+ * Result of loading configuration.
24
+ */
25
+ export interface LoadConfigResult {
26
+ /** The loaded and merged configuration */
27
+ config: ResolvedConstraintConfig;
28
+ /** The path to the config file that was loaded (if any) */
29
+ configPath: string | null;
30
+ /** Whether a config file was found */
31
+ found: boolean;
32
+ }
33
+ /**
34
+ * Loads FormSpec constraint configuration from a .formspec.yml file.
35
+ *
36
+ * @param options - Options for loading configuration
37
+ * @returns The loaded configuration with defaults applied
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * // Load from current directory (searches for .formspec.yml)
42
+ * const result = await loadConfig();
43
+ *
44
+ * // Load from specific directory
45
+ * const result = await loadConfig({ cwd: '/path/to/project' });
46
+ *
47
+ * // Load from specific file
48
+ * const result = await loadConfig({ configPath: '/path/to/config.yml' });
49
+ * ```
50
+ */
51
+ export declare function loadConfig(options?: LoadConfigOptions): Promise<LoadConfigResult>;
52
+ /**
53
+ * Synchronously loads config from a pre-parsed YAML string.
54
+ * Useful for testing or when config is already available.
55
+ *
56
+ * @param yamlContent - The YAML content to parse
57
+ * @returns The parsed and merged configuration
58
+ */
59
+ export declare function loadConfigFromString(yamlContent: string): ResolvedConstraintConfig;
60
+ /**
61
+ * Creates a constraint configuration directly from an object.
62
+ * Useful for programmatic configuration without YAML.
63
+ *
64
+ * @param config - Partial constraint configuration
65
+ * @returns Complete configuration with defaults applied
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * const config = defineConstraints({
70
+ * fieldTypes: {
71
+ * dynamicEnum: 'error',
72
+ * dynamicSchema: 'error',
73
+ * },
74
+ * layout: {
75
+ * group: 'error',
76
+ * },
77
+ * });
78
+ * ```
79
+ */
80
+ export declare function defineConstraints(config: ConstraintConfig): ResolvedConstraintConfig;
81
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAkB,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAQ7F;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0CAA0C;IAC1C,MAAM,EAAE,wBAAwB,CAAC;IACjC,2DAA2D;IAC3D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,sCAAsC;IACtC,KAAK,EAAE,OAAO,CAAC;CAChB;AAqDD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgC3F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,wBAAwB,CAYlF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,wBAAwB,CAEpF"}
package/dist/loader.js ADDED
@@ -0,0 +1,140 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve, dirname } from "node:path";
3
+ import { parse as parseYaml } from "yaml";
4
+ import { mergeWithDefaults } from "./defaults.js";
5
+ /**
6
+ * Default config file names to search for (in order of priority).
7
+ */
8
+ const CONFIG_FILE_NAMES = [".formspec.yml", ".formspec.yaml", "formspec.yml"];
9
+ /**
10
+ * Searches for a config file in the given directory and optionally parent directories.
11
+ */
12
+ async function findConfigFile(startDir, searchParents) {
13
+ let currentDir = resolve(startDir);
14
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- intentional infinite loop with break conditions
15
+ while (true) {
16
+ for (const fileName of CONFIG_FILE_NAMES) {
17
+ const filePath = resolve(currentDir, fileName);
18
+ try {
19
+ await readFile(filePath);
20
+ return filePath;
21
+ }
22
+ catch {
23
+ // File doesn't exist, continue searching
24
+ }
25
+ }
26
+ if (!searchParents) {
27
+ break;
28
+ }
29
+ const parentDir = dirname(currentDir);
30
+ // Reached filesystem root when dirname returns same path
31
+ if (parentDir === currentDir) {
32
+ break;
33
+ }
34
+ currentDir = parentDir;
35
+ }
36
+ return null;
37
+ }
38
+ /**
39
+ * Parses a YAML config file and returns the FormSpecConfig.
40
+ */
41
+ async function parseConfigFile(filePath) {
42
+ const content = await readFile(filePath, "utf-8");
43
+ const parsed = parseYaml(content);
44
+ if (parsed === null || parsed === undefined) {
45
+ return {};
46
+ }
47
+ if (typeof parsed !== "object" || Array.isArray(parsed)) {
48
+ throw new Error(`Invalid config file at ${filePath}: expected an object, got ${typeof parsed}`);
49
+ }
50
+ return parsed;
51
+ }
52
+ /**
53
+ * Loads FormSpec constraint configuration from a .formspec.yml file.
54
+ *
55
+ * @param options - Options for loading configuration
56
+ * @returns The loaded configuration with defaults applied
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * // Load from current directory (searches for .formspec.yml)
61
+ * const result = await loadConfig();
62
+ *
63
+ * // Load from specific directory
64
+ * const result = await loadConfig({ cwd: '/path/to/project' });
65
+ *
66
+ * // Load from specific file
67
+ * const result = await loadConfig({ configPath: '/path/to/config.yml' });
68
+ * ```
69
+ */
70
+ export async function loadConfig(options = {}) {
71
+ const { cwd = process.cwd(), configPath, searchParents = true } = options;
72
+ let resolvedPath = null;
73
+ if (configPath) {
74
+ resolvedPath = resolve(cwd, configPath);
75
+ try {
76
+ await readFile(resolvedPath);
77
+ }
78
+ catch {
79
+ throw new Error(`Config file not found at ${resolvedPath}`);
80
+ }
81
+ }
82
+ else {
83
+ resolvedPath = await findConfigFile(cwd, searchParents);
84
+ }
85
+ if (!resolvedPath) {
86
+ return {
87
+ config: mergeWithDefaults(undefined),
88
+ configPath: null,
89
+ found: false,
90
+ };
91
+ }
92
+ const fileConfig = await parseConfigFile(resolvedPath);
93
+ const config = mergeWithDefaults(fileConfig.constraints);
94
+ return {
95
+ config,
96
+ configPath: resolvedPath,
97
+ found: true,
98
+ };
99
+ }
100
+ /**
101
+ * Synchronously loads config from a pre-parsed YAML string.
102
+ * Useful for testing or when config is already available.
103
+ *
104
+ * @param yamlContent - The YAML content to parse
105
+ * @returns The parsed and merged configuration
106
+ */
107
+ export function loadConfigFromString(yamlContent) {
108
+ const parsed = parseYaml(yamlContent);
109
+ if (parsed === null || parsed === undefined) {
110
+ return mergeWithDefaults(undefined);
111
+ }
112
+ if (typeof parsed !== "object" || Array.isArray(parsed)) {
113
+ throw new Error(`Invalid config content: expected an object, got ${typeof parsed}`);
114
+ }
115
+ return mergeWithDefaults(parsed.constraints);
116
+ }
117
+ /**
118
+ * Creates a constraint configuration directly from an object.
119
+ * Useful for programmatic configuration without YAML.
120
+ *
121
+ * @param config - Partial constraint configuration
122
+ * @returns Complete configuration with defaults applied
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * const config = defineConstraints({
127
+ * fieldTypes: {
128
+ * dynamicEnum: 'error',
129
+ * dynamicSchema: 'error',
130
+ * },
131
+ * layout: {
132
+ * group: 'error',
133
+ * },
134
+ * });
135
+ * ```
136
+ */
137
+ export function defineConstraints(config) {
138
+ return mergeWithDefaults(config);
139
+ }
140
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Severity level for constraint violations.
3
+ * - "error": Violation fails validation
4
+ * - "warn": Violation emits warning but passes
5
+ * - "off": Feature is allowed (no violation)
6
+ */
7
+ export type Severity = "error" | "warn" | "off";
8
+ /**
9
+ * Field type constraints - control which field types are allowed.
10
+ * Fine-grained control over each DSL field builder.
11
+ */
12
+ export interface FieldTypeConstraints {
13
+ /** field.text() - basic text input */
14
+ text?: Severity;
15
+ /** field.number() - numeric input */
16
+ number?: Severity;
17
+ /** field.boolean() - checkbox/toggle */
18
+ boolean?: Severity;
19
+ /** field.enum() with literal options */
20
+ staticEnum?: Severity;
21
+ /** field.dynamicEnum() - runtime-fetched options */
22
+ dynamicEnum?: Severity;
23
+ /** field.dynamicSchema() - runtime-fetched schema */
24
+ dynamicSchema?: Severity;
25
+ /** field.array() / field.arrayWithConfig() */
26
+ array?: Severity;
27
+ /** field.object() / field.objectWithConfig() */
28
+ object?: Severity;
29
+ }
30
+ /**
31
+ * Layout and structure constraints - control grouping, conditionals, nesting.
32
+ */
33
+ export interface LayoutConstraints {
34
+ /** group() - visual grouping of fields */
35
+ group?: Severity;
36
+ /** when() - conditional field visibility */
37
+ conditionals?: Severity;
38
+ /** Maximum nesting depth for objects/arrays (0 = flat only) */
39
+ maxNestingDepth?: number;
40
+ }
41
+ /**
42
+ * JSONForms layout type constraints.
43
+ */
44
+ export interface LayoutTypeConstraints {
45
+ /** VerticalLayout - stack elements vertically */
46
+ VerticalLayout?: Severity;
47
+ /** HorizontalLayout - arrange elements horizontally */
48
+ HorizontalLayout?: Severity;
49
+ /** Group - visual grouping with label */
50
+ Group?: Severity;
51
+ /** Categorization - tabbed/wizard interface */
52
+ Categorization?: Severity;
53
+ /** Category - individual tab/step in Categorization */
54
+ Category?: Severity;
55
+ }
56
+ /**
57
+ * JSONForms rule effect constraints.
58
+ */
59
+ export interface RuleEffectConstraints {
60
+ /** SHOW - show element when condition is true */
61
+ SHOW?: Severity;
62
+ /** HIDE - hide element when condition is true */
63
+ HIDE?: Severity;
64
+ /** ENABLE - enable element when condition is true */
65
+ ENABLE?: Severity;
66
+ /** DISABLE - disable element when condition is true */
67
+ DISABLE?: Severity;
68
+ }
69
+ /**
70
+ * JSONForms rule constraints.
71
+ */
72
+ export interface RuleConstraints {
73
+ /** Whether rules are enabled at all */
74
+ enabled?: Severity;
75
+ /** Fine-grained control over rule effects */
76
+ effects?: RuleEffectConstraints;
77
+ }
78
+ /**
79
+ * UI Schema feature constraints - control JSONForms-specific features.
80
+ */
81
+ export interface UISchemaConstraints {
82
+ /** Layout type constraints */
83
+ layouts?: LayoutTypeConstraints;
84
+ /** Rule (conditional) constraints */
85
+ rules?: RuleConstraints;
86
+ }
87
+ /**
88
+ * Field configuration option constraints - control which field options are allowed.
89
+ */
90
+ export interface FieldOptionConstraints {
91
+ /** label - field label text */
92
+ label?: Severity;
93
+ /** placeholder - input placeholder text */
94
+ placeholder?: Severity;
95
+ /** required - whether field is required */
96
+ required?: Severity;
97
+ /** minValue - minimum value for numbers */
98
+ minValue?: Severity;
99
+ /** maxValue - maximum value for numbers */
100
+ maxValue?: Severity;
101
+ /** minItems - minimum array length */
102
+ minItems?: Severity;
103
+ /** maxItems - maximum array length */
104
+ maxItems?: Severity;
105
+ }
106
+ /**
107
+ * Control options constraints - control which JSONForms Control.options are allowed.
108
+ * These are renderer-specific options that may not be universally supported.
109
+ */
110
+ export interface ControlOptionConstraints {
111
+ /** format - renderer format hint (e.g., "radio", "textarea") */
112
+ format?: Severity;
113
+ /** readonly - read-only mode */
114
+ readonly?: Severity;
115
+ /** multi - multi-select for enums */
116
+ multi?: Severity;
117
+ /** showUnfocusedDescription - show description when unfocused */
118
+ showUnfocusedDescription?: Severity;
119
+ /** hideRequiredAsterisk - hide required indicator */
120
+ hideRequiredAsterisk?: Severity;
121
+ /** Custom control options (extensible dictionary) */
122
+ custom?: Record<string, Severity>;
123
+ }
124
+ /**
125
+ * Complete constraint configuration for a FormSpec project.
126
+ */
127
+ export interface ConstraintConfig {
128
+ /** Field type constraints */
129
+ fieldTypes?: FieldTypeConstraints;
130
+ /** Layout and structure constraints */
131
+ layout?: LayoutConstraints;
132
+ /** UI Schema feature constraints */
133
+ uiSchema?: UISchemaConstraints;
134
+ /** Field configuration option constraints */
135
+ fieldOptions?: FieldOptionConstraints;
136
+ /** Control options constraints */
137
+ controlOptions?: ControlOptionConstraints;
138
+ }
139
+ /**
140
+ * Fully resolved rule constraints with all properties required.
141
+ */
142
+ export interface ResolvedRuleConstraints {
143
+ enabled: Severity;
144
+ effects: Required<RuleEffectConstraints>;
145
+ }
146
+ /**
147
+ * Fully resolved UI schema constraints with all properties required.
148
+ */
149
+ export interface ResolvedUISchemaConstraints {
150
+ layouts: Required<LayoutTypeConstraints>;
151
+ rules: ResolvedRuleConstraints;
152
+ }
153
+ /**
154
+ * Fully resolved constraint configuration with all properties required.
155
+ * This is the type returned by mergeWithDefaults().
156
+ */
157
+ export interface ResolvedConstraintConfig {
158
+ fieldTypes: Required<FieldTypeConstraints>;
159
+ layout: Required<LayoutConstraints>;
160
+ uiSchema: ResolvedUISchemaConstraints;
161
+ fieldOptions: Required<FieldOptionConstraints>;
162
+ controlOptions: Required<ControlOptionConstraints>;
163
+ }
164
+ /**
165
+ * Top-level FormSpec configuration file structure.
166
+ * The .formspec.yml file uses this structure.
167
+ */
168
+ export interface FormSpecConfig {
169
+ /** Constraint configuration */
170
+ constraints?: ConstraintConfig;
171
+ }
172
+ /**
173
+ * A single validation issue found during constraint checking.
174
+ */
175
+ export interface ValidationIssue {
176
+ /** Unique code identifying the issue type */
177
+ code: string;
178
+ /** Human-readable description of the issue */
179
+ message: string;
180
+ /** Severity level of this issue */
181
+ severity: "error" | "warning";
182
+ /** Which constraint category this issue belongs to */
183
+ category: "fieldTypes" | "layout" | "uiSchema" | "fieldOptions" | "controlOptions";
184
+ /** JSON pointer or field path to the issue location */
185
+ path?: string;
186
+ /** Name of the affected field (if applicable) */
187
+ fieldName?: string;
188
+ /** Type of the affected field (if applicable) */
189
+ fieldType?: string;
190
+ }
191
+ /**
192
+ * Result of validating a FormSpec or schema against constraints.
193
+ */
194
+ export interface ValidationResult {
195
+ /** Whether validation passed (no errors, warnings OK) */
196
+ valid: boolean;
197
+ /** List of all issues found */
198
+ issues: ValidationIssue[];
199
+ }
200
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import type { FieldOptionConstraints, Severity, ValidationIssue } from "../types.js";
2
+ /**
3
+ * Known field options that can be validated.
4
+ */
5
+ export type FieldOption = "label" | "placeholder" | "required" | "minValue" | "maxValue" | "minItems" | "maxItems";
6
+ /**
7
+ * Context for field option validation.
8
+ */
9
+ export interface FieldOptionsContext {
10
+ /** The field name */
11
+ fieldName: string;
12
+ /** Which options are present on this field */
13
+ presentOptions: FieldOption[];
14
+ /** Path to this field */
15
+ path?: string;
16
+ }
17
+ /**
18
+ * Validates field options against constraints.
19
+ *
20
+ * @param context - Information about the field and its options
21
+ * @param constraints - Field option constraints
22
+ * @returns Array of validation issues (empty if valid)
23
+ */
24
+ export declare function validateFieldOptions(context: FieldOptionsContext, constraints: FieldOptionConstraints): ValidationIssue[];
25
+ /**
26
+ * Extracts which options are present on a field object.
27
+ * Works with FormSpec field types.
28
+ *
29
+ * @param field - A field object with potential options
30
+ * @returns Array of present option names
31
+ */
32
+ export declare function extractFieldOptions(field: Record<string, unknown>): FieldOption[];
33
+ /**
34
+ * Checks if a specific field option is allowed.
35
+ *
36
+ * @param option - The option to check
37
+ * @param constraints - Field option constraints
38
+ * @returns true if allowed, false if disallowed
39
+ */
40
+ export declare function isFieldOptionAllowed(option: FieldOption, constraints: FieldOptionConstraints): boolean;
41
+ /**
42
+ * Gets the severity level for a field option.
43
+ *
44
+ * @param option - The option to check
45
+ * @param constraints - Field option constraints
46
+ * @returns Severity level, or "off" if not constrained
47
+ */
48
+ export declare function getFieldOptionSeverity(option: FieldOption, constraints: FieldOptionConstraints): Severity;
49
+ //# sourceMappingURL=field-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-options.d.ts","sourceRoot":"","sources":["../../src/validators/field-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAErF;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB,OAAO,GACP,aAAa,GACb,UAAU,GACV,UAAU,GACV,UAAU,GACV,UAAU,GACV,UAAU,CAAC;AAEf;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,cAAc,EAAE,WAAW,EAAE,CAAC;IAC9B,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,mBAAmB,EAC5B,WAAW,EAAE,sBAAsB,GAClC,eAAe,EAAE,CAWnB;AAqBD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,EAAE,CAajF;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,sBAAsB,GAClC,OAAO,CAGT;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,sBAAsB,GAClC,QAAQ,CAEV"}