@formspec/core 0.1.0-alpha.11 → 0.1.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.d.ts CHANGED
@@ -5,15 +5,32 @@
5
5
  * - Form element types (fields, groups, conditionals)
6
6
  * - Field and form state types
7
7
  * - Data source registry for dynamic enums
8
+ * - Canonical IR types (FormIR, FieldNode, TypeNode, ConstraintNode, AnnotationNode, etc.)
8
9
  *
9
10
  * @packageDocumentation
10
11
  */
11
12
 
13
+ /**
14
+ * Discriminated union of all annotation types.
15
+ * Annotations are value-influencing: they describe or present a field
16
+ * but do not affect which values are valid.
17
+ */
18
+ export declare type AnnotationNode = DisplayNameAnnotationNode | DescriptionAnnotationNode | PlaceholderAnnotationNode | DefaultValueAnnotationNode | DeprecatedAnnotationNode | FormatHintAnnotationNode | CustomAnnotationNode;
19
+
12
20
  /**
13
21
  * Union of all field types.
14
22
  */
15
23
  export declare type AnyField = TextField<string> | NumberField<string> | BooleanField<string> | StaticEnumField<string, readonly EnumOptionValue[]> | DynamicEnumField<string, string> | DynamicSchemaField<string> | ArrayField<string, readonly FormElement[]> | ObjectField<string, readonly FormElement[]>;
16
24
 
25
+ /** Array uniqueness constraint. */
26
+ export declare interface ArrayCardinalityConstraintNode {
27
+ readonly kind: "constraint";
28
+ readonly constraintKind: "uniqueItems";
29
+ readonly value: true;
30
+ readonly path?: PathTarget;
31
+ readonly provenance: Provenance;
32
+ }
33
+
17
34
  /**
18
35
  * An array field containing repeating items.
19
36
  *
@@ -41,6 +58,12 @@ export declare interface ArrayField<N extends string, Items extends readonly For
41
58
  readonly maxItems?: number;
42
59
  }
43
60
 
61
+ /** Array type with a single items type. */
62
+ export declare interface ArrayTypeNode {
63
+ readonly kind: "array";
64
+ readonly items: TypeNode;
65
+ }
66
+
44
67
  /**
45
68
  * A boolean checkbox field.
46
69
  *
@@ -59,6 +82,25 @@ export declare interface BooleanField<N extends string> {
59
82
  readonly required?: boolean;
60
83
  }
61
84
 
85
+ /**
86
+ * Built-in constraint names mapped to their expected value type for parsing.
87
+ * Constraints are surface-agnostic — they manifest as both TSDoc tags
88
+ * (e.g., `@Minimum 0`) and chain DSL options (e.g., `{ minimum: 0 }`).
89
+ */
90
+ export declare const BUILTIN_CONSTRAINT_DEFINITIONS: {
91
+ readonly Minimum: "number";
92
+ readonly Maximum: "number";
93
+ readonly ExclusiveMinimum: "number";
94
+ readonly ExclusiveMaximum: "number";
95
+ readonly MinLength: "number";
96
+ readonly MaxLength: "number";
97
+ readonly Pattern: "string";
98
+ readonly EnumOptions: "json";
99
+ };
100
+
101
+ /** Type of a built-in constraint name. */
102
+ export declare type BuiltinConstraintName = keyof typeof BUILTIN_CONSTRAINT_DEFINITIONS;
103
+
62
104
  /**
63
105
  * A conditional wrapper that shows/hides elements based on another field's value.
64
106
  *
@@ -77,27 +119,23 @@ export declare interface Conditional<FieldName extends string, Value, Elements e
77
119
  readonly elements: Elements;
78
120
  }
79
121
 
122
+ /** Conditional visibility based on another field's value. */
123
+ export declare interface ConditionalLayoutNode {
124
+ readonly kind: "conditional";
125
+ /** The field whose value triggers visibility. */
126
+ readonly fieldName: string;
127
+ /** The value that makes the condition true (SHOW). */
128
+ readonly value: JsonValue;
129
+ /** Elements shown when the condition is met. */
130
+ readonly elements: readonly FormIRElement[];
131
+ readonly provenance: Provenance;
132
+ }
133
+
80
134
  /**
81
- * Constraint decorator names that are valid as TSDoc tags, mapped to
82
- * their expected value type for parsing.
83
- *
84
- * Both `@formspec/build` (schema generation) and `@formspec/eslint-plugin`
85
- * (lint-time validation) import this to determine which JSDoc tags to
86
- * recognize and how to parse their values.
135
+ * Discriminated union of all constraint types.
136
+ * Constraints are set-influencing: they narrow the set of valid values.
87
137
  */
88
- export declare const CONSTRAINT_TAG_DEFINITIONS: {
89
- readonly Minimum: "number";
90
- readonly Maximum: "number";
91
- readonly ExclusiveMinimum: "number";
92
- readonly ExclusiveMaximum: "number";
93
- readonly MinLength: "number";
94
- readonly MaxLength: "number";
95
- readonly Pattern: "string";
96
- readonly EnumOptions: "json";
97
- };
98
-
99
- /** Type of a constraint tag name. */
100
- export declare type ConstraintTagName = keyof typeof CONSTRAINT_TAG_DEFINITIONS;
138
+ export declare type ConstraintNode = NumericConstraintNode | LengthConstraintNode | PatternConstraintNode | ArrayCardinalityConstraintNode | EnumMemberConstraintNode | CustomConstraintNode;
101
139
 
102
140
  /**
103
141
  * Creates initial field state with default values.
@@ -108,6 +146,110 @@ export declare type ConstraintTagName = keyof typeof CONSTRAINT_TAG_DEFINITIONS;
108
146
  */
109
147
  export declare function createInitialFieldState<T>(value: T): FieldState<T>;
110
148
 
149
+ /** Extension-registered custom annotation. */
150
+ export declare interface CustomAnnotationNode {
151
+ readonly kind: "annotation";
152
+ readonly annotationKind: "custom";
153
+ /** Extension-qualified ID: `"<vendor-prefix>/<extension-name>/<annotation-name>"` */
154
+ readonly annotationId: string;
155
+ readonly value: JsonValue;
156
+ readonly provenance: Provenance;
157
+ }
158
+
159
+ /**
160
+ * Registration for a custom annotation that may produce JSON Schema keywords.
161
+ *
162
+ * Custom annotations are referenced via {@link CustomAnnotationNode} in the IR.
163
+ * They describe or present a field but do not affect which values are valid.
164
+ */
165
+ export declare interface CustomAnnotationRegistration {
166
+ /** The annotation name, unique within the extension. */
167
+ readonly annotationName: string;
168
+ /**
169
+ * Optionally converts the annotation value into JSON Schema keywords.
170
+ * If omitted, the annotation has no JSON Schema representation (UI-only).
171
+ */
172
+ readonly toJsonSchema?: (value: JsonValue, vendorPrefix: string) => Record<string, unknown>;
173
+ }
174
+
175
+ /** Extension-registered custom constraint. */
176
+ export declare interface CustomConstraintNode {
177
+ readonly kind: "constraint";
178
+ readonly constraintKind: "custom";
179
+ /** Extension-qualified ID: `"<vendor-prefix>/<extension-name>/<constraint-name>"` */
180
+ readonly constraintId: string;
181
+ /** JSON-serializable payload defined by the extension. */
182
+ readonly payload: JsonValue;
183
+ /** How this constraint composes with others of the same `constraintId`. */
184
+ readonly compositionRule: "intersect" | "override";
185
+ readonly path?: PathTarget;
186
+ readonly provenance: Provenance;
187
+ }
188
+
189
+ /**
190
+ * Registration for a custom constraint that maps to JSON Schema keywords.
191
+ *
192
+ * Custom constraints are referenced via {@link CustomConstraintNode} in the IR.
193
+ */
194
+ export declare interface CustomConstraintRegistration {
195
+ /** The constraint name, unique within the extension. */
196
+ readonly constraintName: string;
197
+ /**
198
+ * How this constraint composes with other constraints of the same kind.
199
+ * - "intersect": combine with logical AND (both must hold)
200
+ * - "override": last writer wins
201
+ */
202
+ readonly compositionRule: "intersect" | "override";
203
+ /**
204
+ * TypeNode kinds this constraint is applicable to, or `null` for any type.
205
+ * Used by the validator to emit TYPE_MISMATCH diagnostics.
206
+ */
207
+ readonly applicableTypes: readonly TypeNode["kind"][] | null;
208
+ /**
209
+ * Converts the custom constraint's payload into JSON Schema keywords.
210
+ *
211
+ * @param payload - The opaque JSON payload from the {@link CustomConstraintNode}.
212
+ * @param vendorPrefix - The vendor prefix for extension keywords.
213
+ * @returns A JSON Schema fragment with the constraint keywords.
214
+ */
215
+ readonly toJsonSchema: (payload: JsonValue, vendorPrefix: string) => Record<string, unknown>;
216
+ }
217
+
218
+ /** Custom type registered by an extension. */
219
+ export declare interface CustomTypeNode {
220
+ readonly kind: "custom";
221
+ /**
222
+ * The extension-qualified type identifier.
223
+ * Format: `"<vendor-prefix>/<extension-name>/<type-name>"`
224
+ * e.g., `"x-stripe/monetary/MonetaryAmount"`
225
+ */
226
+ readonly typeId: string;
227
+ /**
228
+ * Opaque payload serialized by the extension that registered this type.
229
+ * Must be JSON-serializable.
230
+ */
231
+ readonly payload: JsonValue;
232
+ }
233
+
234
+ /**
235
+ * Registration for a custom type that maps to a JSON Schema representation.
236
+ *
237
+ * Custom types are referenced via {@link CustomTypeNode} in the IR and
238
+ * resolved to JSON Schema via `toJsonSchema` during generation.
239
+ */
240
+ export declare interface CustomTypeRegistration {
241
+ /** The type name, unique within the extension. */
242
+ readonly typeName: string;
243
+ /**
244
+ * Converts the custom type's payload into a JSON Schema fragment.
245
+ *
246
+ * @param payload - The opaque JSON payload from the {@link CustomTypeNode}.
247
+ * @param vendorPrefix - The vendor prefix for extension keywords (e.g., "x-stripe").
248
+ * @returns A JSON Schema fragment representing this type.
249
+ */
250
+ readonly toJsonSchema: (payload: JsonValue, vendorPrefix: string) => Record<string, unknown>;
251
+ }
252
+
111
253
  /**
112
254
  * A single option returned by a data source resolver.
113
255
  *
@@ -150,6 +292,72 @@ export declare type DataSourceValueType<Source extends string> = Source extends
150
292
  id: infer ID;
151
293
  } ? ID : string : string;
152
294
 
295
+ export declare interface DefaultValueAnnotationNode {
296
+ readonly kind: "annotation";
297
+ readonly annotationKind: "defaultValue";
298
+ /** Must be JSON-serializable and type-compatible (verified during Validate phase). */
299
+ readonly value: JsonValue;
300
+ readonly provenance: Provenance;
301
+ }
302
+
303
+ /**
304
+ * Defines a custom annotation registration. Currently an identity function
305
+ * that provides type-checking and IDE autocompletion.
306
+ *
307
+ * @param reg - The custom annotation registration.
308
+ * @returns The same registration, validated at the type level.
309
+ */
310
+ export declare function defineAnnotation(reg: CustomAnnotationRegistration): CustomAnnotationRegistration;
311
+
312
+ /**
313
+ * Defines a custom constraint registration. Currently an identity function
314
+ * that provides type-checking and IDE autocompletion.
315
+ *
316
+ * @param reg - The custom constraint registration.
317
+ * @returns The same registration, validated at the type level.
318
+ */
319
+ export declare function defineConstraint(reg: CustomConstraintRegistration): CustomConstraintRegistration;
320
+
321
+ /**
322
+ * Defines a custom type registration. Currently an identity function that
323
+ * provides type-checking and IDE autocompletion.
324
+ *
325
+ * @param reg - The custom type registration.
326
+ * @returns The same registration, validated at the type level.
327
+ */
328
+ export declare function defineCustomType(reg: CustomTypeRegistration): CustomTypeRegistration;
329
+
330
+ /**
331
+ * Defines a complete extension. Currently an identity function that provides
332
+ * type-checking and IDE autocompletion for the definition shape.
333
+ *
334
+ * @param def - The extension definition.
335
+ * @returns The same definition, validated at the type level.
336
+ */
337
+ export declare function defineExtension(def: ExtensionDefinition): ExtensionDefinition;
338
+
339
+ export declare interface DeprecatedAnnotationNode {
340
+ readonly kind: "annotation";
341
+ readonly annotationKind: "deprecated";
342
+ /** Optional deprecation message. */
343
+ readonly message?: string;
344
+ readonly provenance: Provenance;
345
+ }
346
+
347
+ export declare interface DescriptionAnnotationNode {
348
+ readonly kind: "annotation";
349
+ readonly annotationKind: "description";
350
+ readonly value: string;
351
+ readonly provenance: Provenance;
352
+ }
353
+
354
+ export declare interface DisplayNameAnnotationNode {
355
+ readonly kind: "annotation";
356
+ readonly annotationKind: "displayName";
357
+ readonly value: string;
358
+ readonly provenance: Provenance;
359
+ }
360
+
153
361
  /**
154
362
  * A field with dynamic enum options (fetched from a data source at runtime).
155
363
  *
@@ -191,6 +399,38 @@ export declare interface DynamicSchemaField<N extends string> {
191
399
  readonly label?: string;
192
400
  /** Whether this field is required for form submission */
193
401
  readonly required?: boolean;
402
+ /** Field names whose values are needed to configure the schema */
403
+ readonly params?: readonly string[];
404
+ }
405
+
406
+ /** Dynamic type — schema resolved at runtime from a named data source. */
407
+ export declare interface DynamicTypeNode {
408
+ readonly kind: "dynamic";
409
+ readonly dynamicKind: "enum" | "schema";
410
+ /** Key identifying the runtime data source or schema provider. */
411
+ readonly sourceKey: string;
412
+ /**
413
+ * For dynamic enums: field names whose current values are passed as
414
+ * parameters to the data source resolver.
415
+ */
416
+ readonly parameterFields: readonly string[];
417
+ }
418
+
419
+ /** A member of a static enum type. */
420
+ export declare interface EnumMember {
421
+ /** The serialized value stored in data. */
422
+ readonly value: string | number;
423
+ /** Optional per-member display name. */
424
+ readonly displayName?: string;
425
+ }
426
+
427
+ /** Enum member subset constraint (refinement — only narrows). */
428
+ export declare interface EnumMemberConstraintNode {
429
+ readonly kind: "constraint";
430
+ readonly constraintKind: "allowedMembers";
431
+ readonly members: readonly (string | number)[];
432
+ readonly path?: PathTarget;
433
+ readonly provenance: Provenance;
194
434
  }
195
435
 
196
436
  /**
@@ -208,6 +448,12 @@ export declare interface EnumOption {
208
448
  */
209
449
  export declare type EnumOptionValue = string | EnumOption;
210
450
 
451
+ /** Static enum type — members known at build time. */
452
+ export declare interface EnumTypeNode {
453
+ readonly kind: "enum";
454
+ readonly members: readonly EnumMember[];
455
+ }
456
+
211
457
  /**
212
458
  * Predicate types for conditional logic.
213
459
  *
@@ -228,6 +474,39 @@ export declare interface EqualsPredicate<K extends string, V> {
228
474
  readonly value: V;
229
475
  }
230
476
 
477
+ /**
478
+ * A complete extension definition bundling types, constraints, annotations,
479
+ * and vocabulary keywords.
480
+ *
481
+ * @example
482
+ * ```typescript
483
+ * const monetaryExtension = defineExtension({
484
+ * extensionId: "x-stripe/monetary",
485
+ * types: [
486
+ * defineCustomType({
487
+ * typeName: "Decimal",
488
+ * toJsonSchema: (_payload, prefix) => ({
489
+ * type: "string",
490
+ * [`${prefix}-decimal`]: true,
491
+ * }),
492
+ * }),
493
+ * ],
494
+ * });
495
+ * ```
496
+ */
497
+ export declare interface ExtensionDefinition {
498
+ /** Globally unique extension identifier, e.g., "x-stripe/monetary". */
499
+ readonly extensionId: string;
500
+ /** Custom type registrations provided by this extension. */
501
+ readonly types?: readonly CustomTypeRegistration[];
502
+ /** Custom constraint registrations provided by this extension. */
503
+ readonly constraints?: readonly CustomConstraintRegistration[];
504
+ /** Custom annotation registrations provided by this extension. */
505
+ readonly annotations?: readonly CustomAnnotationRegistration[];
506
+ /** Vocabulary keyword registrations provided by this extension. */
507
+ readonly vocabularyKeywords?: readonly VocabularyKeywordRegistration[];
508
+ }
509
+
231
510
  /**
232
511
  * Response from a data source resolver function.
233
512
  *
@@ -242,6 +521,31 @@ export declare interface FetchOptionsResponse<T = unknown> {
242
521
  readonly message?: string;
243
522
  }
244
523
 
524
+ /** A single form field after canonicalization. */
525
+ export declare interface FieldNode {
526
+ readonly kind: "field";
527
+ /** The field's key in the data schema. */
528
+ readonly name: string;
529
+ /** The resolved type of this field. */
530
+ readonly type: TypeNode;
531
+ /** Whether this field is required in the data schema. */
532
+ readonly required: boolean;
533
+ /** Set-influencing constraints, after merging. */
534
+ readonly constraints: readonly ConstraintNode[];
535
+ /** Value-influencing annotations, after merging. */
536
+ readonly annotations: readonly AnnotationNode[];
537
+ /** Where this field was declared. */
538
+ readonly provenance: Provenance;
539
+ /**
540
+ * Debug only — ordered list of constraint/annotation nodes that participated
541
+ * in merging, including dominated ones.
542
+ */
543
+ readonly mergeHistory?: readonly {
544
+ readonly node: ConstraintNode | AnnotationNode;
545
+ readonly dominated: boolean;
546
+ }[];
547
+ }
548
+
245
549
  /**
246
550
  * Represents the runtime state of a single form field.
247
551
  *
@@ -260,11 +564,49 @@ export declare interface FieldState<T> {
260
564
  readonly errors: readonly string[];
261
565
  }
262
566
 
567
+ /** UI rendering hint — does not affect schema validation. */
568
+ export declare interface FormatHintAnnotationNode {
569
+ readonly kind: "annotation";
570
+ readonly annotationKind: "formatHint";
571
+ /** Renderer-specific format identifier: "textarea", "radio", "date", "color", etc. */
572
+ readonly format: string;
573
+ readonly provenance: Provenance;
574
+ }
575
+
263
576
  /**
264
577
  * Union of all form element types (fields and structural elements).
265
578
  */
266
579
  export declare type FormElement = AnyField | Group<readonly FormElement[]> | Conditional<string, unknown, readonly FormElement[]>;
267
580
 
581
+ /**
582
+ * The complete Canonical Intermediate Representation for a form.
583
+ *
584
+ * Output of the Canonicalize phase; input to Validate, Generate (JSON Schema),
585
+ * and Generate (UI Schema) phases.
586
+ *
587
+ * Serializable to JSON — no live compiler objects.
588
+ */
589
+ export declare interface FormIR {
590
+ readonly kind: "form-ir";
591
+ /**
592
+ * Schema version for the IR format itself.
593
+ * Should equal `IR_VERSION`.
594
+ */
595
+ readonly irVersion: string;
596
+ /** Top-level elements of the form: fields and layout nodes. */
597
+ readonly elements: readonly FormIRElement[];
598
+ /**
599
+ * Registry of named types referenced by fields in this form.
600
+ * Keys are fully-qualified type names matching `ReferenceTypeNode.name`.
601
+ */
602
+ readonly typeRegistry: Readonly<Record<string, TypeDefinition>>;
603
+ /** Provenance of the form definition itself. */
604
+ readonly provenance: Provenance;
605
+ }
606
+
607
+ /** Union of all IR element types. */
608
+ export declare type FormIRElement = FieldNode | LayoutNode;
609
+
268
610
  /**
269
611
  * A complete form specification.
270
612
  *
@@ -275,12 +617,6 @@ export declare interface FormSpec<Elements extends readonly FormElement[]> {
275
617
  readonly elements: Elements;
276
618
  }
277
619
 
278
- /** Names of all built-in FormSpec decorators. */
279
- export declare const FORMSPEC_DECORATOR_NAMES: readonly ["Field", "Group", "ShowWhen", "EnumOptions", "Minimum", "Maximum", "ExclusiveMinimum", "ExclusiveMaximum", "MinLength", "MaxLength", "Pattern"];
280
-
281
- /** Type of a FormSpec decorator name. */
282
- export declare type FormSpecDecoratorName = (typeof FORMSPEC_DECORATOR_NAMES)[number];
283
-
284
620
  /**
285
621
  * Represents the runtime state of an entire form.
286
622
  *
@@ -315,6 +651,82 @@ export declare interface Group<Elements extends readonly FormElement[]> {
315
651
  readonly elements: Elements;
316
652
  }
317
653
 
654
+ /** A visual grouping of form elements. */
655
+ export declare interface GroupLayoutNode {
656
+ readonly kind: "group";
657
+ readonly label: string;
658
+ /** Elements contained in this group — may be fields or nested groups. */
659
+ readonly elements: readonly FormIRElement[];
660
+ readonly provenance: Provenance;
661
+ }
662
+
663
+ /**
664
+ * The current IR format version. Centralized here so all canonicalizers
665
+ * and consumers reference a single source of truth.
666
+ */
667
+ export declare const IR_VERSION: "0.1.0";
668
+
669
+ /** Narrows a FormElement to an array field. */
670
+ export declare function isArrayField(element: FormElement): element is ArrayField<string, readonly FormElement[]>;
671
+
672
+ /** Narrows a FormElement to a boolean checkbox field. */
673
+ export declare function isBooleanField(element: FormElement): element is BooleanField<string>;
674
+
675
+ /** Narrows a FormElement to a conditional wrapper. */
676
+ export declare function isConditional(element: FormElement): element is Conditional<string, unknown, readonly FormElement[]>;
677
+
678
+ /** Narrows a FormElement to a dynamic enum field. */
679
+ export declare function isDynamicEnumField(element: FormElement): element is DynamicEnumField<string, string>;
680
+
681
+ /** Narrows a FormElement to a dynamic schema field. */
682
+ export declare function isDynamicSchemaField(element: FormElement): element is DynamicSchemaField<string>;
683
+
684
+ /** Narrows a FormElement to any field type. */
685
+ export declare function isField(element: FormElement): element is AnyField;
686
+
687
+ /** Narrows a FormElement to a visual group. */
688
+ export declare function isGroup(element: FormElement): element is Group<readonly FormElement[]>;
689
+
690
+ /** Narrows a FormElement to a numeric input field. */
691
+ export declare function isNumberField(element: FormElement): element is NumberField<string>;
692
+
693
+ /** Narrows a FormElement to an object field. */
694
+ export declare function isObjectField(element: FormElement): element is ObjectField<string, readonly FormElement[]>;
695
+
696
+ /** Narrows a FormElement to a static enum field. */
697
+ export declare function isStaticEnumField(element: FormElement): element is StaticEnumField<string, readonly EnumOptionValue[]>;
698
+
699
+ /** Narrows a FormElement to a text input field. */
700
+ export declare function isTextField(element: FormElement): element is TextField<string>;
701
+
702
+ /**
703
+ * A JSON-serializable value. All IR nodes must be representable as JSON.
704
+ */
705
+ export declare type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
706
+ readonly [key: string]: JsonValue;
707
+ };
708
+
709
+ /** Union of layout node types. */
710
+ export declare type LayoutNode = GroupLayoutNode | ConditionalLayoutNode;
711
+
712
+ /**
713
+ * String length and array item count constraints.
714
+ *
715
+ * `minLength`/`maxLength` apply to strings; `minItems`/`maxItems` apply to
716
+ * arrays. They share the same node shape because the composition rules are
717
+ * identical.
718
+ *
719
+ * Type applicability: `minLength`/`maxLength` require `PrimitiveTypeNode("string")`;
720
+ * `minItems`/`maxItems` require `ArrayTypeNode`.
721
+ */
722
+ export declare interface LengthConstraintNode {
723
+ readonly kind: "constraint";
724
+ readonly constraintKind: "minLength" | "maxLength" | "minItems" | "maxItems";
725
+ readonly value: number;
726
+ readonly path?: PathTarget;
727
+ readonly provenance: Provenance;
728
+ }
729
+
318
730
  /**
319
731
  * A numeric input field.
320
732
  *
@@ -335,6 +747,27 @@ export declare interface NumberField<N extends string> {
335
747
  readonly max?: number;
336
748
  /** Whether this field is required for form submission */
337
749
  readonly required?: boolean;
750
+ /** Value must be a multiple of this number (use 1 for integer semantics) */
751
+ readonly multipleOf?: number;
752
+ }
753
+
754
+ /**
755
+ * Numeric constraints: bounds and multipleOf.
756
+ *
757
+ * `minimum` and `maximum` are inclusive; `exclusiveMinimum` and
758
+ * `exclusiveMaximum` are exclusive bounds (matching JSON Schema 2020-12
759
+ * semantics).
760
+ *
761
+ * Type applicability: may only attach to fields with `PrimitiveTypeNode("number")`
762
+ * or a `ReferenceTypeNode` that resolves to one.
763
+ */
764
+ export declare interface NumericConstraintNode {
765
+ readonly kind: "constraint";
766
+ readonly constraintKind: "minimum" | "maximum" | "exclusiveMinimum" | "exclusiveMaximum" | "multipleOf";
767
+ readonly value: number;
768
+ /** If present, targets a nested sub-field rather than the field itself. */
769
+ readonly path?: PathTarget;
770
+ readonly provenance: Provenance;
338
771
  }
339
772
 
340
773
  /**
@@ -360,6 +793,75 @@ export declare interface ObjectField<N extends string, Properties extends readon
360
793
  readonly required?: boolean;
361
794
  }
362
795
 
796
+ /** A named property within an object type. */
797
+ export declare interface ObjectProperty {
798
+ readonly name: string;
799
+ readonly type: TypeNode;
800
+ readonly optional: boolean;
801
+ /**
802
+ * Use-site constraints on this property.
803
+ * Distinct from constraints on the property's type — these are
804
+ * use-site constraints (e.g., `@minimum :amount 0` targets the
805
+ * `amount` property of a `MonetaryAmount` field).
806
+ */
807
+ readonly constraints: readonly ConstraintNode[];
808
+ /** Use-site annotations on this property. */
809
+ readonly annotations: readonly AnnotationNode[];
810
+ readonly provenance: Provenance;
811
+ }
812
+
813
+ /** Object type with named properties. */
814
+ export declare interface ObjectTypeNode {
815
+ readonly kind: "object";
816
+ /**
817
+ * Named properties of this object. Order is preserved from the source
818
+ * declaration for deterministic output.
819
+ */
820
+ readonly properties: readonly ObjectProperty[];
821
+ /**
822
+ * Whether additional properties beyond those listed are permitted.
823
+ * Defaults to false — object types in FormSpec are closed.
824
+ */
825
+ readonly additionalProperties: boolean;
826
+ }
827
+
828
+ /**
829
+ * A path targeting a sub-field within a complex type.
830
+ * Used by constraints and annotations to target nested properties.
831
+ */
832
+ export declare interface PathTarget {
833
+ /**
834
+ * Sequence of property names forming a path from the annotated field's type
835
+ * to the target sub-field.
836
+ * e.g., `["value"]` or `["address", "zip"]`
837
+ */
838
+ readonly segments: readonly string[];
839
+ }
840
+
841
+ /**
842
+ * String pattern constraint (ECMA-262 regex without delimiters).
843
+ *
844
+ * Multiple `pattern` constraints on the same field compose via intersection:
845
+ * all patterns must match simultaneously.
846
+ *
847
+ * Type applicability: requires `PrimitiveTypeNode("string")`.
848
+ */
849
+ export declare interface PatternConstraintNode {
850
+ readonly kind: "constraint";
851
+ readonly constraintKind: "pattern";
852
+ /** ECMA-262 regular expression, without delimiters. */
853
+ readonly pattern: string;
854
+ readonly path?: PathTarget;
855
+ readonly provenance: Provenance;
856
+ }
857
+
858
+ export declare interface PlaceholderAnnotationNode {
859
+ readonly kind: "annotation";
860
+ readonly annotationKind: "placeholder";
861
+ readonly value: string;
862
+ readonly provenance: Provenance;
863
+ }
864
+
363
865
  /**
364
866
  * Union of all predicate types.
365
867
  *
@@ -370,6 +872,55 @@ export declare interface ObjectField<N extends string, Properties extends readon
370
872
  */
371
873
  export declare type Predicate<K extends string = string, V = unknown> = EqualsPredicate<K, V>;
372
874
 
875
+ /**
876
+ * Primitive types mapping directly to JSON Schema primitives.
877
+ *
878
+ * Note: integer is NOT a primitive kind — integer semantics are expressed
879
+ * via a `multipleOf: 1` constraint on a number type.
880
+ */
881
+ export declare interface PrimitiveTypeNode {
882
+ readonly kind: "primitive";
883
+ readonly primitiveKind: "string" | "number" | "boolean" | "null";
884
+ }
885
+
886
+ /**
887
+ * Describes the origin of an IR node.
888
+ * Enables diagnostics that point to the source of a contradiction or error.
889
+ */
890
+ export declare interface Provenance {
891
+ /** The authoring surface that produced this node. */
892
+ readonly surface: "tsdoc" | "chain-dsl" | "extension" | "inferred";
893
+ /** Absolute path to the source file. */
894
+ readonly file: string;
895
+ /** 1-based line number in the source file. */
896
+ readonly line: number;
897
+ /** 0-based column number in the source file. */
898
+ readonly column: number;
899
+ /** Length of the source span in characters (for IDE underline ranges). */
900
+ readonly length?: number;
901
+ /**
902
+ * The specific tag, call, or construct that produced this node.
903
+ * Examples: `@minimum`, `field.number({ min: 0 })`, `optional`
904
+ */
905
+ readonly tagName?: string;
906
+ }
907
+
908
+ /** Named type reference — preserved as references for `$defs`/`$ref` emission. */
909
+ export declare interface ReferenceTypeNode {
910
+ readonly kind: "reference";
911
+ /**
912
+ * The fully-qualified name of the referenced type.
913
+ * For TypeScript interfaces/type aliases: `"<module>#<TypeName>"`.
914
+ * For built-in types: the primitive kind string.
915
+ */
916
+ readonly name: string;
917
+ /**
918
+ * Type arguments if this is a generic instantiation.
919
+ * e.g., `Array<string>` → `{ name: "Array", typeArguments: [PrimitiveTypeNode("string")] }`
920
+ */
921
+ readonly typeArguments: readonly TypeNode[];
922
+ }
923
+
373
924
  /**
374
925
  * A field with static enum options (known at compile time).
375
926
  *
@@ -417,6 +968,33 @@ export declare interface TextField<N extends string> {
417
968
  readonly placeholder?: string;
418
969
  /** Whether this field is required for form submission */
419
970
  readonly required?: boolean;
971
+ /** Minimum string length */
972
+ readonly minLength?: number;
973
+ /** Maximum string length */
974
+ readonly maxLength?: number;
975
+ /** Regular expression pattern the value must match */
976
+ readonly pattern?: string;
977
+ }
978
+
979
+ /** A named type definition stored in the type registry. */
980
+ export declare interface TypeDefinition {
981
+ /** The fully-qualified reference name (key in the registry). */
982
+ readonly name: string;
983
+ /** The resolved type node. */
984
+ readonly type: TypeNode;
985
+ /** Where this type was declared. */
986
+ readonly provenance: Provenance;
987
+ }
988
+
989
+ /**
990
+ * Discriminated union of all type representations in the IR.
991
+ */
992
+ export declare type TypeNode = PrimitiveTypeNode | EnumTypeNode | ArrayTypeNode | ObjectTypeNode | UnionTypeNode | ReferenceTypeNode | DynamicTypeNode | CustomTypeNode;
993
+
994
+ /** Union type for non-enum unions. Nullable types are `T | null` using this. */
995
+ export declare interface UnionTypeNode {
996
+ readonly kind: "union";
997
+ readonly members: readonly TypeNode[];
420
998
  }
421
999
 
422
1000
  /**
@@ -428,4 +1006,14 @@ export declare interface TextField<N extends string> {
428
1006
  */
429
1007
  export declare type Validity = "valid" | "invalid" | "unknown";
430
1008
 
1009
+ /**
1010
+ * Registration for a vocabulary keyword to include in a JSON Schema `$vocabulary` declaration.
1011
+ */
1012
+ export declare interface VocabularyKeywordRegistration {
1013
+ /** The keyword name (without vendor prefix). */
1014
+ readonly keyword: string;
1015
+ /** JSON Schema that describes the valid values for this keyword. */
1016
+ readonly schema: JsonValue;
1017
+ }
1018
+
431
1019
  export { }