@dynamicforms/vue-forms 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -46,27 +46,44 @@ export declare class Action<T extends ActionValue = ActionValue, X extends objec
46
46
 
47
47
  declare type ActionExecutor = (field: FieldBase, supr: FieldActionExecute, ...params: any[]) => any;
48
48
 
49
- export declare class ActionsMap extends Map<symbol, FieldActionExecute> {
50
- private readonly eagerActions;
49
+ /**
50
+ * The actions one element has registered, grouped by the identifier they are triggered under. Within a group the
51
+ * actions stand in registration order and are run from the end backwards, so the newest registration is the
52
+ * outermost handler and reaches the ones before it through the `supr` it is handed. A handler that does not call
53
+ * `supr` ends the run there, and one that calls it may transform what it answers with.
54
+ */
55
+ export declare class ActionsMap {
56
+ /** every action registered here, in registration order across all identifiers */
51
57
  private readonly registeredActions;
52
- register(action: FieldActionBase): void;
58
+ /** the actions of one identifier, in registration order */
59
+ private readonly chains;
60
+ /** the eager actions of one identifier, in registration order; a subsequence of the chain under that identifier */
61
+ private readonly eagerChains;
62
+ /**
63
+ * Registers `action`. It becomes the outermost handler of its identifier, or - where `before` is given and is
64
+ * registered here under the same identifier - takes that action's place in the order, so `before` wraps it and
65
+ * reaches it through `supr`. That is what lets an action be added to a chain someone else built and still sit
66
+ * inside a handler already there.
67
+ */
68
+ register(action: FieldActionBase, before?: FieldActionBase): void;
53
69
  /**
54
- * True when triggering this action would run something: a handler registered under the identifier, or - on the
55
- * value change identifier, which trigger() answers with an eager pass as well - any eager action at all. A
56
- * caller that has to build the parameters before it can trigger asks this first and skips building them when
57
- * there is nothing to receive them.
70
+ * Drops `action` from this map and answers whether it was in it. The lists it stood in are replaced rather than
71
+ * written, so a run already walking one finishes on the list it started with and the removal takes effect from
72
+ * the next trigger.
58
73
  */
74
+ unregister(action: FieldActionBase): boolean;
75
+ /** True where something is registered under `identifier`. */
59
76
  willTrigger(identifier: symbol): boolean;
77
+ /** True where any eager action is registered, whatever identifier it stands under. */
78
+ get hasEager(): boolean;
79
+ /** Runs the actions registered under `ActionClass` and answers with what the outermost of them returned. */
60
80
  trigger<T extends FieldActionBase>(ActionClass: (abstract new (...args: any[]) => T) & {
61
81
  classIdentifier: symbol;
62
82
  }, field: FieldBase, ...params: any[]): any;
63
- /**
64
- * Runs the chain registered under one identifier and nothing else. A caller that has already run the eager
65
- * pass - a transaction runs it at the write, so that the validators have decided before it commits - reaches
66
- * the handlers through here, so the eager actions are not run a second time at the announcement.
67
- */
68
- triggerChain(identifier: symbol, field: FieldBase, ...params: any[]): any;
69
- triggerEager(field: FieldBase, ...params: any[]): any;
83
+ /** Runs the eager actions of every identifier, each group on its own. */
84
+ triggerEager(field: FieldBase, ...params: any[]): void;
85
+ /** Runs the eager actions registered under `identifier` and nothing else. */
86
+ triggerEagerFor(identifier: symbol, field: FieldBase, ...params: any[]): any;
70
87
  /** the validators registered in this map, in registration order */
71
88
  get validators(): Validator[];
72
89
  clone(): ActionsMap;
@@ -77,11 +94,15 @@ export declare class ActionsMap extends Map<symbol, FieldActionExecute> {
77
94
  */
78
95
  bindTo(owner: FieldBase): void;
79
96
  /**
80
- * A copy of this map carrying everything but the validators. It only leaves them out: releasing what a
81
- * validator installed elsewhere is the caller's to do, and only once the transaction that dropped them has
82
- * committed, because a rollback puts this map back with its validators in it.
97
+ * Walks `chain` from the end backwards, handing each action a `supr` that continues at the one before it. The
98
+ * closures exist for the length of the run and only as deep as the run actually goes, so a chain nobody walks to
99
+ * the bottom costs nothing for the part left unreached.
83
100
  */
84
- cloneWithoutValidators(): ActionsMap;
101
+ private step;
102
+ /** Walks a chain, answering null where there is none. An abort ends the run and is not an error to the caller. */
103
+ private run;
104
+ private insert;
105
+ private remove;
85
106
  }
86
107
 
87
108
  export declare interface ActionValue {
@@ -89,6 +110,12 @@ export declare interface ActionValue {
89
110
  icon?: string;
90
111
  }
91
112
 
113
+ /**
114
+ * The values a field may hold: a fixed list, or a reference or a callback for a list that is filled in or replaced
115
+ * after the validator is built - the options a server answers with, or the ones another field's value leaves open.
116
+ */
117
+ declare type AllowedValues<T> = T[] | Ref<T[]> | (() => T[]);
118
+
92
119
  /**
93
120
  * Depending on the useMarkdownInValidators setting, return either markdown or a plain string error message
94
121
  * @param mdErrorString markdown source to optionally be stripped
@@ -175,6 +202,7 @@ export declare class ConditionalStatementAction extends ValueChangedAction {
175
202
  static get classIdentifier(): symbol;
176
203
  get eager(): boolean;
177
204
  boundToBinding(binding: FieldBase): void;
205
+ unregisterFrom(binding: FieldBase): void;
178
206
  /**
179
207
  * The elements of `scope` this action controls: what each declaration it serves names within that record, and of
180
208
  * those the ones that took the action on. A row of a list that never took it on is not one this action drives.
@@ -247,8 +275,8 @@ export declare namespace DisplayMode {
247
275
  * walk their way down a structure over own keys, enumerable symbols included, and the `parent` slot is in the
248
276
  * state: reachable, it would take either walker back into the container the element came from.
249
277
  *
250
- * What they do reach on an element is `_actions` once something is registered, `_fields` on a `Group` and
251
- * `_itemTemplate` on a `List`. All three lead downwards only.
278
+ * What they do reach on an element is `_actions` once something is registered, `_fields` and the guarded view
279
+ * over it on a `Group`, and `_itemTemplate` on a `List`. All of them lead downwards only.
252
280
  */
253
281
  declare interface ElementSlots<T = any> {
254
282
  /** the value the element was given at construction; isChanged compares against it */
@@ -262,6 +290,8 @@ declare interface ElementSlots<T = any> {
262
290
  valueVersion: number;
263
291
  /** how many asynchronous validation runs are in flight on this element */
264
292
  validatingCount: number;
293
+ /** how many direct children answer `validating` with true; a child that starts or stops running moves it */
294
+ validatingChildren: number;
265
295
  /** the container that holds this element, absent while none does; takeChild writes it, releaseChild clears it */
266
296
  parent: FieldBase | undefined;
267
297
  /** the name the containing Group holds this element under; a List row carries none */
@@ -357,8 +387,10 @@ export declare abstract class FieldActionBase {
357
387
  */
358
388
  boundToBinding(binding: FieldBase): void;
359
389
  /**
360
- * Announces that `binding` no longer holds this action. It names one element rather than the action as a whole,
361
- * because the instance goes on serving every other element it was registered on.
390
+ * Announces that `binding` no longer holds this action, and takes back what running it left there. It names one
391
+ * element rather than the action as a whole, because the instance goes on serving every other element it was
392
+ * registered on. It runs inside the operation that drops the registration, so an operation that unwinds puts the
393
+ * registration and what this took back both back.
362
394
  */
363
395
  unregisterFrom(binding: FieldBase): void;
364
396
  }
@@ -475,16 +507,56 @@ export declare abstract class FieldBase<T = any, X extends object = {}> {
475
507
  * Puts a captured state back. It writes through the tracked view, so an effect that ran on the state the
476
508
  * transaction produced runs again on the state it is being returned to, and only for the slots that differ.
477
509
  *
478
- * validatingCount is the one slot the element keeps: it counts runs that are in flight, and a rollback cannot
479
- * un-start one. Put back, the count would no longer match the endValidating calls still to come.
510
+ * The two validation counters are the slots the element keeps: they count runs that are in flight, and a
511
+ * rollback cannot un-start one. Put back, the counts would no longer match the endValidating calls still to come.
480
512
  */
481
513
  protected [TxRestore](snapshot: TxSnapshot): void;
482
- /** true while at least one asynchronous validator is still running for this field */
514
+ /**
515
+ * True while an asynchronous validation is in flight on this element or on anything below it, so a form answers
516
+ * for the whole tree it holds. The answer is a pair of counters rather than a walk: the element's own runs, and
517
+ * how many of its children answer the same question with true. A child that starts running while it was idle,
518
+ * or stops while it was the last one running, moves its container's tally, and a tally that changes the
519
+ * container's own answer moves the one above it - the transition costs the nesting depth and the read costs
520
+ * nothing.
521
+ */
483
522
  get validating(): boolean;
484
523
  /** announces the start of one asynchronous validation run; validators pair it with endValidating */
485
524
  beginValidating(): void;
486
525
  /** announces the end of one asynchronous validation run */
487
526
  endValidating(): void;
527
+ /**
528
+ * Records that a child started or stopped answering `validating` with true, and carries the transition further
529
+ * up where it changes this element's own answer.
530
+ */
531
+ protected childValidatingChanged(started: boolean): void;
532
+ /**
533
+ * True while an `Action.execute()` at or below this element has yet to settle. An `Action` answers for its own
534
+ * runs; a container answers for the actions below it; anything else answers false, because an element that is
535
+ * not an action has nothing to execute.
536
+ *
537
+ * It states one thing and `validating` states the other, so a form that gates a submit button on the tree being
538
+ * idle reads both.
539
+ */
540
+ get busy(): boolean;
541
+ /**
542
+ * The composed answer a container gives `busy`, memoised by Vue. An `Action` counts its executions in a counter
543
+ * of its own, which no container is told about, so the answer is composed over the members instead of tallied;
544
+ * the computed keeps the walk from repeating while nothing it read has moved, and a member that is itself a
545
+ * container answers from its own computed.
546
+ */
547
+ protected get busyRead(): boolean;
548
+ /** What busyRead computes. A container answers over its members; anything else executes nothing. */
549
+ protected composeBusy(): boolean;
550
+ /**
551
+ * Resolves once nothing at or below this element is running - no asynchronous validation, no `Action.execute()`
552
+ * that has yet to settle. It is what a submit path awaits instead of polling `validating` and `busy`, and it
553
+ * resolves at once where nothing is running to begin with.
554
+ *
555
+ * It answers for the moment it resolves and makes no promise about the one after: work started later leaves the
556
+ * element running again, so a caller that has to act on a settled tree reads what it needs immediately after
557
+ * awaiting rather than at its leisure.
558
+ */
559
+ settled(): Promise<void>;
488
560
  /**
489
561
  * List of errors. The array handed out is the one the element holds, and a validator writes into it in place,
490
562
  * so a transaction that is open takes the element into its record here: a rollback that put back everything
@@ -646,7 +718,7 @@ export declare abstract class FieldBase<T = any, X extends object = {}> {
646
718
  protected [TxSettleValidity](tx: Transaction): void;
647
719
  validate(revalidate?: boolean): void;
648
720
  get valid(): boolean;
649
- get fullValue(): any;
721
+ get fullValue(): T;
650
722
  get isChanged(): boolean;
651
723
  /**
652
724
  * Brings a fresh element into the state a binding of `source` is in: it records what it was declared as, takes
@@ -664,6 +736,25 @@ export declare abstract class FieldBase<T = any, X extends object = {}> {
664
736
  */
665
737
  protected registerInitialActions(actions: FieldActionBase[]): void;
666
738
  registerAction(action: FieldActionBase): this;
739
+ /**
740
+ * Registers `action` so that `before` wraps it: `before` reaches it through the `supr` it is handed, instead of
741
+ * ending the run there. It is how an action is added to a chain someone else built and still sits inside a
742
+ * handler already registered, which registration order alone cannot arrange. `before` has to be registered on
743
+ * this element under the same identifier.
744
+ */
745
+ registerActionBefore(action: FieldActionBase, before: FieldActionBase): this;
746
+ /**
747
+ * Drops `action` from this element and answers whether it held it. The instance goes on serving every other
748
+ * element it was registered on: what is dropped is this element's registration, not the action. A validator
749
+ * withdraws the errors it put on this element as it goes, so the verdict the element reports is the one the
750
+ * validators it still holds reach.
751
+ */
752
+ unregisterAction(action: FieldActionBase): boolean;
753
+ /**
754
+ * Records that `action` is now registered on this element, and that a rollback has to take that back. The
755
+ * registration is not one of the state slots the snapshot covers, so it hands its own undo in.
756
+ */
757
+ private registered;
667
758
  triggerAction<T2 extends FieldActionBase>(actionClass: (abstract new (...args: any[]) => T2) & {
668
759
  classIdentifier: symbol;
669
760
  }, ...params: any[]): any;
@@ -682,6 +773,15 @@ declare interface FieldSlots<T = any> extends ElementSlots<T> {
682
773
  touched: boolean;
683
774
  }
684
775
 
776
+ /**
777
+ * What Group.fullValue reads back. The indexed access reads each field's fullValue getter, so a nested group
778
+ * contributes its own full structure rather than the partial one its `value` builds, and the recursion carries
779
+ * the guarantee all the way down: every key is present and none of them is null.
780
+ */
781
+ export declare type FieldsToFullValues<T extends GenericFieldsInterface> = {
782
+ [K in keyof T]: T[K]['fullValue'];
783
+ };
784
+
685
785
  export declare type FieldsToValues<T extends GenericFieldsInterface> = {
686
786
  [K in keyof T]: T[K]['value'];
687
787
  };
@@ -725,6 +825,7 @@ declare namespace Form {
725
825
  FieldBase,
726
826
  GenericFieldsInterface,
727
827
  FieldsToValues,
828
+ FieldsToFullValues,
728
829
  GroupValue,
729
830
  GroupValueInput,
730
831
  Group,
@@ -760,26 +861,79 @@ export declare const forms: {
760
861
  install(app: any, options?: Partial<FormsConfig>): void;
761
862
  };
762
863
 
763
- declare interface FormsConfig {
864
+ /**
865
+ * Global configuration for @dynamicforms/vue-forms.
866
+ *
867
+ * The configuration is module-global: `setConfig` and the plugin's `install` write the single record this module
868
+ * holds, so in a process running several Vue apps the configuration applied last is the one all of them read.
869
+ */
870
+ export declare interface FormsConfig {
871
+ /** Whether the built-in validators phrase their error messages as `MdString` markdown instead of plain text. */
764
872
  useMarkdownInValidators: boolean;
765
873
  }
766
874
 
767
875
  export declare type GenericFieldsInterface = Record<string, FieldBase>;
768
876
 
877
+ /** The current configuration. The object is the module's own record, and reading it again reports later writes. */
878
+ export declare function getConfig(): FormsConfig;
879
+
769
880
  export declare class Group<T extends GenericFieldsInterface = GenericFieldsInterface, X extends object = {}> extends FieldBase<GroupValue<T>, X> {
770
- protected get state(): ContainerSlots<GroupValue<T>>;
771
- protected get raw(): ContainerSlots<GroupValue<T>>;
881
+ protected get state(): GroupSlots<GroupValue<T>>;
882
+ protected get raw(): GroupSlots<GroupValue<T>>;
772
883
  private readonly _fields;
773
884
  constructor(fields: T, params?: IFieldParams<GroupValueInput<T>, X>);
885
+ /**
886
+ * The name array is copied as well: a rollback puts back the set of members the group held, not the array it
887
+ * went on to hold.
888
+ */
889
+ protected [TxCapture](): TxSnapshot;
774
890
  /**
775
891
  * The copy of a built value that serves as a baseline. The value getter hands out one object per version, and
776
892
  * a baseline holding that same object would report every value as its own original.
777
893
  */
778
894
  private static baseline;
779
- private addField;
895
+ /**
896
+ * Takes `field` into this group under `fieldName`. The group ends up holding it exactly as it holds a field the
897
+ * constructor was given: the field carries the back-reference and the name, its verdict counts towards the
898
+ * group's, and a rule of the field's that names another member of the form reaches it here.
899
+ *
900
+ * The change is announced through the ordinary path, so a group whose value the new field adds to says so once
901
+ * the transaction closes, and its verdict is re-formed over the members it now holds. The baseline behind
902
+ * `isChanged` is not rewritten: a group that gains a field holds something its original value does not carry,
903
+ * and reports itself changed until `originalValue` is written.
904
+ *
905
+ * @throws Error where this group already holds a field under `fieldName`.
906
+ * @throws TypeError where `field` already belongs to a container - pass a `bind()` of it instead.
907
+ */
908
+ addField(fieldName: string, field: FieldBase): this;
909
+ /**
910
+ * Takes the field held under `fieldName` out of this group and hands it back, answering undefined where the
911
+ * group holds no field of that name. The field is released whole: the back-reference and the name are gone, its
912
+ * verdict and its runs in flight no longer count towards the group's, and it is free to be taken by another
913
+ * container. What it holds - its value, its errors, the change history behind `isChanged` - is its own to report.
914
+ *
915
+ * The change is announced through the ordinary path, so the group's value and verdict settle over the members it
916
+ * has left. The baseline behind `isChanged` is not rewritten, the same way it is not on `addField`.
917
+ */
918
+ removeField(fieldName: string): FieldBase | undefined;
919
+ /**
920
+ * Writes one entry of the member map. The map is typed by the fields interface, which names the members a group
921
+ * of that type holds and no others, so both here and in dropEntry it is reached as the plain record it is.
922
+ */
923
+ private static setEntry;
924
+ /** Drops one entry out of the member map. */
925
+ private static dropEntry;
780
926
  private static isValidFields;
781
927
  static createFromFormData(data: Record<string, any> | null): Group;
782
928
  field<K extends keyof T>(fieldName: K): T[K] | null;
929
+ /**
930
+ * The typed map of this group's members. What it hands out is a view over the map the group holds: reading it
931
+ * reaches the members themselves, and every write to it is refused - addField() and removeField() are what
932
+ * change the set. The view is built the first time something asks for it.
933
+ *
934
+ * Reading through it is a tracked read of the set of members, so a template rendering off `group.fields`
935
+ * re-renders when a field is added or removed; what a member itself holds is tracked by that member.
936
+ */
783
937
  get fields(): T;
784
938
  protected get members(): FieldBase[];
785
939
  get value(): GroupValue<T>;
@@ -798,7 +952,7 @@ export declare class Group<T extends GenericFieldsInterface = GenericFieldsInter
798
952
  protected resetTo(source: FieldBase, value: any): void;
799
953
  get touched(): boolean;
800
954
  set touched(touched: boolean);
801
- get fullValue(): Record<string, any>;
955
+ get fullValue(): FieldsToFullValues<T>;
802
956
  /**
803
957
  * Records that a member changed its value, so that the transaction in progress works out at commit what this
804
958
  * group's own value became and announces it once. A mutation method calls it itself; you rarely need to.
@@ -813,6 +967,8 @@ export declare class Group<T extends GenericFieldsInterface = GenericFieldsInter
813
967
  protected refreshPreviousValue(): void;
814
968
  get valid(): boolean;
815
969
  protected composeValid(): boolean;
970
+ get busy(): boolean;
971
+ protected composeBusy(): boolean;
816
972
  validate(revalidate?: boolean): void;
817
973
  bind(data?: GroupValueInput<T>, overrides?: IBindParams<GroupValueInput<T>, X>): Group<T, X>;
818
974
  /**
@@ -822,8 +978,21 @@ export declare class Group<T extends GenericFieldsInterface = GenericFieldsInter
822
978
  rebind(data: GroupValueInput<T>): this;
823
979
  }
824
980
 
825
- /** what Group.value reads back: the full field map, or null when no field serializes */
826
- export declare type GroupValue<T extends GenericFieldsInterface> = FieldsToValues<T> | null;
981
+ /** what a Group holds beyond the container slots: the names of its members, in the order it took them */
982
+ declare interface GroupSlots<T = any> extends ContainerSlots<T> {
983
+ /**
984
+ * The names the group holds its members under. The member map itself is a plain object beside the state, so
985
+ * this array is what a reader inside an effect depends on: a member added or removed re-runs the effect, and a
986
+ * rolled-back transaction puts the set back with the rest of the slots.
987
+ */
988
+ fieldNames: string[];
989
+ }
990
+
991
+ /**
992
+ * What Group.value reads back: the values of the fields that serialize, or null when none does. Every key is
993
+ * optional, because a field that is disabled is left out of the object the group builds.
994
+ */
995
+ export declare type GroupValue<T extends GenericFieldsInterface> = Partial<FieldsToValues<T>> | null;
827
996
 
828
997
  /** what Group.value and the Group constructor accept: keys left out are simply not assigned */
829
998
  export declare type GroupValueInput<T extends GenericFieldsInterface> = Partial<FieldsToValues<T>> | null;
@@ -844,7 +1013,7 @@ export declare interface IFieldConstructorActionsList {
844
1013
  /**
845
1014
  * Parameters accepted by field constructors and by bind overrides.
846
1015
  *
847
- * Only writable members are listed. valid, validating, fullValue and isChanged are getter-only, so assigning
1016
+ * Only writable members are listed. valid, validating, busy, fullValue and isChanged are getter-only, so assigning
848
1017
  * them throws a TypeError. So are parent and fieldName: a container writes the slots behind them when it takes
849
1018
  * an element, and nobody else can, which is why the type rejects them.
850
1019
  */
@@ -872,7 +1041,7 @@ export declare type IFieldConstructorParams<T = any> = {
872
1041
  export declare type IFieldParams<T = any, X extends object = {}> = Partial<IFieldConstructorParams<T>> & Partial<NoInfer<X>>;
873
1042
 
874
1043
  declare class InAllowedValues<T = any> extends Validator {
875
- constructor(allowedValues: T[], message?: RenderContentRef);
1044
+ constructor(allowedValues: AllowedValues<T>, message?: RenderContentRef);
876
1045
  }
877
1046
 
878
1047
  export declare function isCallableFunction(msg?: RenderContentRef): msg is RenderContentCallable;
@@ -884,7 +1053,7 @@ export declare function isCallableFunction(msg?: RenderContentRef): msg is Rende
884
1053
  */
885
1054
  export declare function isSimpleComponentDef(msg?: RenderContentRef): msg is SimpleComponentDef;
886
1055
 
887
- declare class LengthInRange<T = any> extends Validator {
1056
+ declare class LengthInRange extends Validator {
888
1057
  constructor(minLength: number, maxLength: number, message?: RenderContentRef);
889
1058
  }
890
1059
 
@@ -906,9 +1075,13 @@ export declare class List<T extends GenericFieldsInterface = GenericFieldsInterf
906
1075
  * own values, or an empty group when the list has no template.
907
1076
  */
908
1077
  private createPaddingItem;
1078
+ /** Records that the set of rows changed, so `items` rebuilds the frozen array it hands out at the next read. */
1079
+ private rowsChanged;
1080
+ /** True where `next` is a different set of rows than `previous`: another count, or another row at a position. */
1081
+ private static rowsDiffer;
909
1082
  private setValueInternal;
910
1083
  get value(): ListValue;
911
- set value(newValue: Record<string, any>[]);
1084
+ set value(newValue: ListValue);
912
1085
  /**
913
1086
  * The rows are taken from `source` where the caller supplied no value of its own, so a list nested in a row that
914
1087
  * a whole-list assignment reuses ends up holding what the template gives it rather than what it held before.
@@ -931,8 +1104,31 @@ export declare class List<T extends GenericFieldsInterface = GenericFieldsInterf
931
1104
  protected refreshPreviousValue(): void;
932
1105
  get valid(): boolean;
933
1106
  protected composeValid(): boolean;
1107
+ get busy(): boolean;
1108
+ protected composeBusy(): boolean;
934
1109
  validate(revalidate?: boolean): void;
935
1110
  protected get members(): FieldBase[];
1111
+ /**
1112
+ * How many rows this list holds. The read is tracked, so a template rendering off it re-renders as rows come and
1113
+ * go.
1114
+ */
1115
+ /**
1116
+ * Every row of this list, each one built from all of its fields. Where `value` states what the list serializes -
1117
+ * rows composed of the fields that are enabled, and null where the list is empty - this states what the list
1118
+ * holds: the disabled fields are in it too, and an empty list reads back as an empty array rather than as null.
1119
+ */
1120
+ get fullValue(): FieldsToFullValues<T>[];
1121
+ get length(): number;
1122
+ /**
1123
+ * The rows this list holds, oldest position first. The array is a frozen copy: it states what the list held at
1124
+ * the read and nothing writes back through it - `push`, `insert`, `remove` and `clear` are what change the set -
1125
+ * so a caller may hold on to it. The rows in it are the live elements, so reading one reports what it holds now.
1126
+ *
1127
+ * The copy is built once per change of the set and handed to every reader until the next one: a write inside a
1128
+ * row changes what the list serializes without changing which rows it holds, and the array a reader took stays
1129
+ * the same one across such a write.
1130
+ */
1131
+ get items(): readonly Group<T>[];
936
1132
  get(index: number): Group<T> | undefined;
937
1133
  push(item: any): number;
938
1134
  pop(): Group<T> | undefined;
@@ -958,12 +1154,20 @@ export declare class ListItemRemovedAction extends FieldActionBase {
958
1154
  /** what a List holds beyond the container slots: the rows themselves */
959
1155
  declare interface ListSlots<T extends GenericFieldsInterface = GenericFieldsInterface> extends ContainerSlots<ListValue> {
960
1156
  rows: Group<T>[] | null;
1157
+ /**
1158
+ * Counts the changes to the set of rows. `items` rebuilds the frozen array it hands out when it moves, and
1159
+ * only then: a write inside a row changes what the list serializes without changing which rows it holds.
1160
+ */
1161
+ rowsVersion: number;
1162
+ /** the frozen array `items` last handed out, together with the rows version it was built from */
1163
+ cachedItems: readonly Group<T>[] | null;
1164
+ cachedItemsVersion: number;
961
1165
  }
962
1166
 
963
1167
  /** what List.value reads back: one plain object per item, or null when the list is empty */
964
1168
  export declare type ListValue = Record<string, any>[] | null;
965
1169
 
966
- declare class MaxLength<T = any> extends Validator {
1170
+ declare class MaxLength extends Validator {
967
1171
  constructor(maxLength: number, message?: RenderContentRef);
968
1172
  }
969
1173
 
@@ -982,7 +1186,7 @@ export declare class MdString extends String {
982
1186
 
983
1187
  export declare const MessagesWidget: DefineComponent<Props, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, ComponentProvideOptions, false, {}, any>;
984
1188
 
985
- declare class MinLength<T = any> extends Validator {
1189
+ declare class MinLength extends Validator {
986
1190
  constructor(minLength: number, message?: RenderContentRef);
987
1191
  }
988
1192
 
@@ -1030,7 +1234,7 @@ export declare namespace Operator {
1030
1234
  export function isLogicOperator(operator: Operator): boolean;
1031
1235
  }
1032
1236
 
1033
- declare class Pattern<T = any> extends Validator {
1237
+ declare class Pattern extends Validator {
1034
1238
  constructor(pattern: RegExp, message?: RenderContentRef);
1035
1239
  }
1036
1240
 
@@ -1059,10 +1263,23 @@ export declare type RenderContentNonCallable = string | MdString | SimpleCompone
1059
1263
  */
1060
1264
  export declare type RenderContentRef = RenderContent | Ref<RenderContent>;
1061
1265
 
1062
- declare class Required_2<T = any> extends Validator {
1063
- constructor(message?: RenderContentRef);
1266
+ declare class Required_2 extends Validator {
1267
+ constructor(options?: RequiredOptions);
1268
+ constructor(message?: RenderContentRef, options?: RequiredOptions);
1064
1269
  }
1065
1270
 
1271
+ /** How the value is read before its length is taken. */
1272
+ declare interface RequiredOptions {
1273
+ /**
1274
+ * Whether a string is trimmed before it is measured, so that whitespace alone is no value. Defaults to true.
1275
+ * Set it to false where the spaces are part of what the field holds.
1276
+ */
1277
+ trim?: boolean;
1278
+ }
1279
+
1280
+ /** Writes the members `newConfig` names and leaves the rest as they stand. */
1281
+ export declare function setConfig(newConfig: Partial<FormsConfig>): void;
1282
+
1066
1283
  /**
1067
1284
  * Interface for custom component content definition
1068
1285
  */
@@ -1076,6 +1293,14 @@ export declare class Statement {
1076
1293
  private readonly operand1;
1077
1294
  private readonly operator;
1078
1295
  private readonly operand2;
1296
+ /**
1297
+ * An operand is a nested `Statement`, a `FieldBase` whose value is compared, or a literal. `undefined` and a
1298
+ * function are neither: they are what a misspelled field name and a field accessor handed over uncalled answer
1299
+ * with, and a statement built from one compares nothing and never fires. The position names which of the two
1300
+ * operands is at fault, so the error points at the place the name was written.
1301
+ */
1302
+ private static validateOperand;
1303
+ /** @throws TypeError where an operand the statement reads is undefined or a function. */
1079
1304
  constructor(operand1: OperandType, operator: Operator, operand2: OperandType);
1080
1305
  /**
1081
1306
  * The value an operand contributes. A field operand names a field of the record the statement is evaluated in:
@@ -1100,10 +1325,10 @@ export declare class Statement {
1100
1325
 
1101
1326
  declare class Transaction {
1102
1327
  private readonly participants;
1103
- /** work that stands only where the transaction commits; a rollback simply never does it */
1104
- private readonly deferred;
1105
1328
  /** what a rollback has to put back beyond the state slots, newest last */
1106
1329
  private readonly undo;
1330
+ /** what the commit has to do once the change stands, in the order it was handed in */
1331
+ private readonly settled;
1107
1332
  /** true once the transaction has been unwound, which is what tells a run started inside it to say nothing */
1108
1333
  private unwound;
1109
1334
  /** the depth the announcement in progress is working at, -1 while none is */
@@ -1130,18 +1355,18 @@ declare class Transaction {
1130
1355
  recordStructural(element: FieldBase, event: TxStructuralEvent): void;
1131
1356
  /** True where the commit still owes this element a value announcement. */
1132
1357
  willAnnounceValue(element: FieldBase): boolean;
1133
- /**
1134
- * Registers work the commit does once everything has been announced. It is for what a rollback could not take
1135
- * back: releasing a listener, dropping a registration. Held until the commit, it is simply never done where the
1136
- * transaction unwinds instead.
1137
- */
1138
- whenCommitted(work: () => void): void;
1139
1358
  /**
1140
1359
  * Registers what a rollback has to put back beyond the element's state slots. The snapshot covers the slots,
1141
1360
  * which is everything an ordinary write touches; an operation that replaces something else the element carries
1142
1361
  * hands its own undo in here rather than making every snapshot carry the thing.
1143
1362
  */
1144
1363
  whenRolledBack(work: () => void): void;
1364
+ /**
1365
+ * Registers work that runs once the transaction has committed, and not at all where it is rolled back. It is
1366
+ * how an operation defers a step that cannot be taken back - cancelling work in flight, for one - until the
1367
+ * change it belongs to actually stands.
1368
+ */
1369
+ whenCommitted(work: () => void): void;
1145
1370
  /**
1146
1371
  * Notes that an element at or below the depth the pass in progress is working at has been enrolled. The pass
1147
1372
  * then gives up the rest of its batch and is started again over what is now owed, so the newcomer is reached
@@ -1251,6 +1476,13 @@ declare interface TxStructuralEvent {
1251
1476
  * Base validation error class with component rendering capabilities
1252
1477
  */
1253
1478
  export declare class ValidationError {
1479
+ code?: string | undefined;
1480
+ /**
1481
+ * Machine-readable identifier of what failed, in kebab-case. Every validator this library ships states one, so
1482
+ * code that reacts to a particular failure does not have to match the message text, which is translated and
1483
+ * configurable. It is optional: an error built by hand carries whatever its author gives it, or nothing.
1484
+ */
1485
+ constructor(code?: string | undefined);
1254
1486
  get componentName(): string;
1255
1487
  get componentBindings(): {};
1256
1488
  get componentBody(): string;
@@ -1264,7 +1496,7 @@ export declare class ValidationErrorRenderContent extends ValidationError {
1264
1496
  classes: ClassTypes;
1265
1497
  private text;
1266
1498
  private textType;
1267
- constructor(text: RenderContentRef, classes?: ClassTypes);
1499
+ constructor(text: RenderContentRef, classes?: ClassTypes, code?: string);
1268
1500
  get resolvedText(): RenderContentNonCallable;
1269
1501
  get getTextType(): "string" | "md" | "component";
1270
1502
  get componentName(): string;
@@ -1279,13 +1511,19 @@ export declare class ValidationErrorRenderContent extends ValidationError {
1279
1511
  export declare class ValidationErrorText extends ValidationError {
1280
1512
  text: string;
1281
1513
  classes: ClassTypes;
1282
- constructor(text: string, classes?: ClassTypes);
1514
+ constructor(text: string, classes?: ClassTypes, code?: string);
1283
1515
  get componentName(): string;
1284
1516
  get componentBody(): string;
1285
1517
  get extraClasses(): ClassTypes;
1286
1518
  }
1287
1519
 
1288
- export declare type ValidationFunction<T = any> = (newValue: T, oldValue: T, field: FieldBase<T>) => ValidationFunctionResult | Promise<ValidationFunctionResult>;
1520
+ /**
1521
+ * What a validator does with a value. `signal` aborts when the verdict this call would reach stops counting - a
1522
+ * newer run over the same field, a field the validator was taken off once that removal stands, a transaction that
1523
+ * was unwound - so an asynchronous check hands it to the work it commissions and stops paying for an answer nobody
1524
+ * will read. A function that has nothing to cancel ignores it.
1525
+ */
1526
+ export declare type ValidationFunction<T = any> = (newValue: T, oldValue: T, field: FieldBase<T>, signal: AbortSignal) => ValidationFunctionResult | Promise<ValidationFunctionResult>;
1289
1527
 
1290
1528
  export declare type ValidationFunctionResult = ValidationError[] | null;
1291
1529
 
@@ -1310,6 +1548,23 @@ export declare class Validator<T = any> extends ValueChangedAction {
1310
1548
  private claim;
1311
1549
  static get classIdentifier(): symbol;
1312
1550
  get eager(): boolean;
1551
+ /**
1552
+ * Takes this validator off `field`: the errors it put there are withdrawn and the verdict is re-formed over what
1553
+ * the validators the field still holds have to say. Withdrawing them is what keeps a field from staying invalid
1554
+ * on an error no validator is left to take back.
1555
+ *
1556
+ * A run in flight over the field is cancelled once the removal stands, and not before: a transaction that
1557
+ * unwinds puts the validator and its epoch back, and the run it never cancelled goes on to reach the verdict the
1558
+ * field is then owed. Cancelling it as the removal is made would leave the field reporting itself valid over a
1559
+ * value nothing checked, on the strength of a transaction that never happened.
1560
+ */
1561
+ unregisterFrom(field: FieldBase): void;
1562
+ /**
1563
+ * Cancels the asynchronous run in flight over `field`, where there is one. It is called once the field has
1564
+ * stopped holding this validator: the field's validation epoch has moved on by then, so the run's verdict is
1565
+ * already dropped and the signal it handed its check aborts with it.
1566
+ */
1567
+ protected abandonRun(field: FieldBase): void;
1313
1568
  /**
1314
1569
  * What this validator remembers about one of the fields it validates. One instance may be registered on several
1315
1570
  * fields - every row of a list carries the instances the item template carries - so each field has a record of
@@ -1329,6 +1584,8 @@ export declare class Validator<T = any> extends ValueChangedAction {
1329
1584
  /** What a validator remembers about one field it is registered on; a subclass widens it. */
1330
1585
  export declare interface ValidatorBindingState {
1331
1586
  run: number;
1587
+ /** cancels the asynchronous run in flight over the field, absent while no run is waiting for its verdict */
1588
+ abandon?: () => void;
1332
1589
  }
1333
1590
 
1334
1591
  export declare namespace Validators {
@@ -1336,7 +1593,9 @@ export declare namespace Validators {
1336
1593
  CompareTo,
1337
1594
  CompareToTarget,
1338
1595
  InAllowedValues,
1596
+ AllowedValues,
1339
1597
  Required_2 as Required,
1598
+ RequiredOptions,
1340
1599
  Pattern,
1341
1600
  ValidationFunctionResult,
1342
1601
  ValidationFunction,