@dynamicforms/vue-forms 0.12.0 → 0.16.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
@@ -4,10 +4,16 @@ import { DefineComponent } from 'vue';
4
4
  import { PublicProps } from 'vue';
5
5
  import { Ref } from 'vue';
6
6
 
7
+ /**
8
+ * Thrown from a handler to end the run it is in. What the trigger answers with is the exception itself rather than
9
+ * `null`, so a caller can tell a run a handler ended from one that reached no handler at all - and the setters
10
+ * that ask a `*Changing*` handler read it as a refusal and write nothing.
11
+ */
7
12
  export declare class AbortEventHandlingException extends Error {
8
13
  }
9
14
 
10
15
  export declare class Action<T extends ActionValue = ActionValue, X extends object = {}> extends Field<T, X> {
16
+ get [Symbol.toStringTag](): string;
11
17
  protected init(params?: IFieldParams<T, X>): void;
12
18
  /**
13
19
  * A copy of the value this action holds, carrying one member rewritten. A member handed undefined is dropped
@@ -47,18 +53,22 @@ export declare class Action<T extends ActionValue = ActionValue, X extends objec
47
53
  declare type ActionExecutor = (field: FieldBase, supr: FieldActionExecute, ...params: any[]) => any;
48
54
 
49
55
  /**
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
56
+ * The actions one declaration has registered, in registration order. A trigger walks them from the end backwards
57
+ * and passes over the ones registered under another identifier, so the newest registration of an identifier is its
52
58
  * outermost handler and reaches the ones before it through the `supr` it is handed. A handler that does not call
53
59
  * `supr` ends the run there, and one that calls it may transform what it answers with.
60
+ *
61
+ * The order is the whole index. An element carries a handful of actions, and a walk over that many is cheaper than
62
+ * a keyed lookup as well as smaller: measured over three actions, the walk is about twice as fast as `Map.get` and
63
+ * the two maps it replaces cost about 500 bytes per declaration.
54
64
  */
55
65
  export declare class ActionsMap {
56
- /** every action registered here, in registration order across all identifiers */
57
- private readonly registeredActions;
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;
66
+ /**
67
+ * Every action registered here, in registration order. It is replaced rather than written when an action is
68
+ * dropped, so a run already walking it finishes on the array it started with and the removal takes effect from
69
+ * the next trigger.
70
+ */
71
+ private actions;
62
72
  /**
63
73
  * Registers `action`. It becomes the outermost handler of its identifier, or - where `before` is given and is
64
74
  * registered here under the same identifier - takes that action's place in the order, so `before` wraps it and
@@ -66,11 +76,7 @@ export declare class ActionsMap {
66
76
  * inside a handler already there.
67
77
  */
68
78
  register(action: FieldActionBase, before?: FieldActionBase): void;
69
- /**
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.
73
- */
79
+ /** Drops `action` and answers whether it was registered here. */
74
80
  unregister(action: FieldActionBase): boolean;
75
81
  /** True where something is registered under `identifier`. */
76
82
  willTrigger(identifier: symbol): boolean;
@@ -80,29 +86,35 @@ export declare class ActionsMap {
80
86
  trigger<T extends FieldActionBase>(ActionClass: (abstract new (...args: any[]) => T) & {
81
87
  classIdentifier: symbol;
82
88
  }, field: FieldBase, ...params: any[]): any;
83
- /** Runs the eager actions of every identifier, each group on its own. */
89
+ /**
90
+ * Runs the eager actions of every identifier, each group on its own. A group is entered at its outermost eager
91
+ * action, which is the last one registered under that identifier.
92
+ */
84
93
  triggerEager(field: FieldBase, ...params: any[]): void;
85
94
  /** Runs the eager actions registered under `identifier` and nothing else. */
86
95
  triggerEagerFor(identifier: symbol, field: FieldBase, ...params: any[]): any;
87
- /** the validators registered in this map, in registration order */
96
+ /** the validators registered here, in registration order */
88
97
  get validators(): Validator[];
89
- clone(): ActionsMap;
90
98
  /**
91
- * Tells every action in this map that it serves `owner`. A copied map holds the instances the original holds, so
92
- * the element that took the copy on is announced to them here, the way registering an action announces the
93
- * element it was registered on.
99
+ * Tells every action here that it serves `owner`. A binding reads the map its declaration holds, so this is what
100
+ * announces the new element to the actions already in it - the way registering an action announces the elements
101
+ * it comes to serve.
94
102
  */
95
103
  bindTo(owner: FieldBase): void;
104
+ /** True where no eager action of `identifier` stands after `index`, which makes `index` the group's entry. */
105
+ private static outermostEager;
96
106
  /**
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.
107
+ * Enters a group at its outermost action. An abort ends the run and is answered with rather than raised: the
108
+ * exception is what the caller receives, so a run a handler ended is told apart from one that reached no handler
109
+ * and from one whose handler answered null.
100
110
  */
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
111
  private run;
104
- private insert;
105
- private remove;
112
+ /**
113
+ * Walks from `index` backwards to the first action of `identifier`, handing it a `supr` that continues at the one
114
+ * before it. The actions registered under other identifiers are passed over, so the array needs no grouping of
115
+ * its own; the closures exist for the length of the run and only as deep as the run actually goes.
116
+ */
117
+ private walk;
106
118
  }
107
119
 
108
120
  export declare interface ActionValue {
@@ -110,6 +122,12 @@ export declare interface ActionValue {
110
122
  icon?: string;
111
123
  }
112
124
 
125
+ /**
126
+ * The values a field may hold: a fixed list, or a reference or a callback for a list that is filled in or replaced
127
+ * after the validator is built - the options a server answers with, or the ones another field's value leaves open.
128
+ */
129
+ declare type AllowedValues<T> = T[] | Ref<T[]> | (() => T[]);
130
+
113
131
  /**
114
132
  * Depending on the useMarkdownInValidators setting, return either markdown or a plain string error message
115
133
  * @param mdErrorString markdown source to optionally be stripped
@@ -235,6 +253,9 @@ declare interface ContainerSlots<T = any> extends ElementSlots<T> {
235
253
  cachedValueVersion: number;
236
254
  }
237
255
 
256
+ /** What a form element's visibility is when nothing sets it. It is a starting value, never a fallback for input. */
257
+ export declare const defaultDisplayMode = DisplayMode.FULL;
258
+
238
259
  /**
239
260
  * DisplayMode enum provides an enumeration for supported ways of rendering a particular object in the DOM
240
261
  */
@@ -246,8 +267,22 @@ export declare enum DisplayMode {
246
267
  }
247
268
 
248
269
  export declare namespace DisplayMode {
270
+ /**
271
+ * Resolves a constant's name, case insensitive, to the constant it names. Anything else - a misspelled name, a
272
+ * value that is not a string - throws an `Error` naming it. A mode nobody defined is an error at the point it
273
+ * arrives, not a field that renders as `FULL` and is never questioned.
274
+ */
249
275
  export function fromString(mode: string): DisplayMode;
276
+ /**
277
+ * Resolves a DisplayMode number, or a constant's name (case insensitive), to a DisplayMode. A number that is
278
+ * none of the constants, a string that names none, and input that is neither a number nor a string all throw an
279
+ * `Error` naming the value. Ask `isDefined` where the answer is to be judged rather than raised.
280
+ */
250
281
  export function fromAny(mode: any): DisplayMode;
282
+ /**
283
+ * Answers whether the input is a DisplayMode: a number that is one of the constants, or a string naming one,
284
+ * case insensitive. This is the way to ask that does not throw; it answers `false` where `fromAny` raises.
285
+ */
251
286
  export function isDefined(mode: number | string): boolean;
252
287
  }
253
288
 
@@ -269,8 +304,8 @@ export declare namespace DisplayMode {
269
304
  * walk their way down a structure over own keys, enumerable symbols included, and the `parent` slot is in the
270
305
  * state: reachable, it would take either walker back into the container the element came from.
271
306
  *
272
- * What they do reach on an element is `_actions` once something is registered, `_fields` on a `Group` and
273
- * `_itemTemplate` on a `List`. All three lead downwards only.
307
+ * What they do reach on an element is `_actions` once something is registered, `_fields` and the guarded view
308
+ * over it on a `Group`, and `_itemTemplate` on a `List`. All of them lead downwards only.
274
309
  */
275
310
  declare interface ElementSlots<T = any> {
276
311
  /** the value the element was given at construction; isChanged compares against it */
@@ -284,6 +319,8 @@ declare interface ElementSlots<T = any> {
284
319
  valueVersion: number;
285
320
  /** how many asynchronous validation runs are in flight on this element */
286
321
  validatingCount: number;
322
+ /** how many direct children answer `validating` with true; a child that starts or stops running moves it */
323
+ validatingChildren: number;
287
324
  /** the container that holds this element, absent while none does; takeChild writes it, releaseChild clears it */
288
325
  parent: FieldBase | undefined;
289
326
  /** the name the containing Group holds this element under; a List row carries none */
@@ -308,8 +345,6 @@ declare interface ElementSlots<T = any> {
308
345
  invalidChildren: number;
309
346
  }
310
347
 
311
- export declare const EmptyField: Field<string, {}>;
312
-
313
348
  export declare class EnabledChangedAction extends FieldActionBase {
314
349
  constructor(executorFn: (field: FieldBase, supr: FieldActionExecute, newValue: boolean, oldValue: boolean) => void);
315
350
  static get classIdentifier(): symbol;
@@ -329,6 +364,7 @@ export declare class ExecuteAction extends FieldActionBase {
329
364
  }
330
365
 
331
366
  export declare class Field<T = any, X extends object = {}> extends FieldBase<T, X> {
367
+ get [Symbol.toStringTag](): string;
332
368
  protected get state(): FieldSlots<T>;
333
369
  protected get raw(): FieldSlots<T>;
334
370
  /** the value slot itself, without the notifications the value setter carries around a write */
@@ -347,6 +383,12 @@ export declare class Field<T = any, X extends object = {}> extends FieldBase<T,
347
383
  protected init(params?: IFieldParams<T, X>): void;
348
384
  get value(): T;
349
385
  set value(newValue: T);
386
+ /**
387
+ * The container that holds this field. A `List` holds rows and a row is a `Group`, so a field is never a `List`'s
388
+ * child and the link is a `Group` wherever there is one: `field.parent?.fields.other` is the sibling lookup, and
389
+ * it is typed here.
390
+ */
391
+ get parent(): Group | undefined;
350
392
  get touched(): boolean;
351
393
  set touched(touched: boolean);
352
394
  bind(data?: T, overrides?: IBindParams<T, X>): this;
@@ -404,6 +446,19 @@ export declare abstract class FieldBase<T = any, X extends object = {}> {
404
446
  */
405
447
  get __v_skip(): boolean;
406
448
  get __v_isReactive(): boolean;
449
+ /**
450
+ * Names the element's class, which is what a structural comparison of two elements answers on. An element keeps
451
+ * its state in private class fields, so a walk over own keys reaches none of it; lodash reads this tag through
452
+ * Object.prototype.toString before it compares anything, and a tag it does not know ends the comparison there.
453
+ * Two elements are therefore equal only where they are the same element. What they hold is compared as
454
+ * isEqual(a.value, b.value).
455
+ *
456
+ * It is an accessor on the prototype, so an element carries nothing for it, and `Object.prototype.toString`
457
+ * answers `[object Field]` rather than `[object Object]`. Each class states its own name as a literal: a
458
+ * minifier renames the class itself, so reading the constructor's name would report whatever the build called
459
+ * it.
460
+ */
461
+ get [Symbol.toStringTag](): string;
407
462
  constructor(slots: ElementSlots<T>);
408
463
  /** the tracked view of this element's state; a subclass narrows the return type to its own slots */
409
464
  protected get state(): ElementSlots<T>;
@@ -499,16 +554,56 @@ export declare abstract class FieldBase<T = any, X extends object = {}> {
499
554
  * Puts a captured state back. It writes through the tracked view, so an effect that ran on the state the
500
555
  * transaction produced runs again on the state it is being returned to, and only for the slots that differ.
501
556
  *
502
- * validatingCount is the one slot the element keeps: it counts runs that are in flight, and a rollback cannot
503
- * un-start one. Put back, the count would no longer match the endValidating calls still to come.
557
+ * The two validation counters are the slots the element keeps: they count runs that are in flight, and a
558
+ * rollback cannot un-start one. Put back, the counts would no longer match the endValidating calls still to come.
504
559
  */
505
560
  protected [TxRestore](snapshot: TxSnapshot): void;
506
- /** true while at least one asynchronous validator is still running for this field */
561
+ /**
562
+ * True while an asynchronous validation is in flight on this element or on anything below it, so a form answers
563
+ * for the whole tree it holds. The answer is a pair of counters rather than a walk: the element's own runs, and
564
+ * how many of its children answer the same question with true. A child that starts running while it was idle,
565
+ * or stops while it was the last one running, moves its container's tally, and a tally that changes the
566
+ * container's own answer moves the one above it - the transition costs the nesting depth and the read costs
567
+ * nothing.
568
+ */
507
569
  get validating(): boolean;
508
570
  /** announces the start of one asynchronous validation run; validators pair it with endValidating */
509
571
  beginValidating(): void;
510
572
  /** announces the end of one asynchronous validation run */
511
573
  endValidating(): void;
574
+ /**
575
+ * Records that a child started or stopped answering `validating` with true, and carries the transition further
576
+ * up where it changes this element's own answer.
577
+ */
578
+ protected childValidatingChanged(started: boolean): void;
579
+ /**
580
+ * True while an `Action.execute()` at or below this element has yet to settle. An `Action` answers for its own
581
+ * runs; a container answers for the actions below it; anything else answers false, because an element that is
582
+ * not an action has nothing to execute.
583
+ *
584
+ * It states one thing and `validating` states the other, so a form that gates a submit button on the tree being
585
+ * idle reads both.
586
+ */
587
+ get busy(): boolean;
588
+ /**
589
+ * The composed answer a container gives `busy`, memoised by Vue. An `Action` counts its executions in a counter
590
+ * of its own, which no container is told about, so the answer is composed over the members instead of tallied;
591
+ * the computed keeps the walk from repeating while nothing it read has moved, and a member that is itself a
592
+ * container answers from its own computed.
593
+ */
594
+ protected get busyRead(): boolean;
595
+ /** What busyRead computes. A container answers over its members; anything else executes nothing. */
596
+ protected composeBusy(): boolean;
597
+ /**
598
+ * Resolves once nothing at or below this element is running - no asynchronous validation, no `Action.execute()`
599
+ * that has yet to settle. It is what a submit path awaits instead of polling `validating` and `busy`, and it
600
+ * resolves at once where nothing is running to begin with.
601
+ *
602
+ * It answers for the moment it resolves and makes no promise about the one after: work started later leaves the
603
+ * element running again, so a caller that has to act on a settled tree reads what it needs immediately after
604
+ * awaiting rather than at its leisure.
605
+ */
606
+ settled(): Promise<void>;
512
607
  /**
513
608
  * List of errors. The array handed out is the one the element holds, and a validator writes into it in place,
514
609
  * so a transaction that is open takes the element into its record here: a rollback that put back everything
@@ -521,8 +616,17 @@ export declare abstract class FieldBase<T = any, X extends object = {}> {
521
616
  * undefined while no container holds it. The container writes it; everyone else reads it. The read is tracked,
522
617
  * so an effect rendering off the link - `v-if="field.parent"` - re-runs when a container takes the element or
523
618
  * releases it.
619
+ *
620
+ * A `List` holds rows and a row is a `Group`, so only a container can be a `List`'s child: `Field` narrows the
621
+ * type to `Group | undefined`, and the sibling lookup `field.parent?.fields.other` is typed on a field.
524
622
  */
525
- get parent(): Group | undefined;
623
+ get parent(): Group | List | undefined;
624
+ /**
625
+ * The same link at the type the internals reach through. The protected members a container is told things
626
+ * through - childValidatingChanged, childValidityChanged - are declared here, and a union of two subclasses is
627
+ * not an instance of this class, which is what a protected access needs.
628
+ */
629
+ private get container();
526
630
  /** when member of a Group, fieldName specifies the name of this field. Written by the group, read-only after */
527
631
  get fieldName(): string | undefined;
528
632
  /**
@@ -560,13 +664,30 @@ export declare abstract class FieldBase<T = any, X extends object = {}> {
560
664
  /** Runs this element's eager actions over the value it holds and re-forms the verdict they reach. */
561
665
  private rerunEagerActions;
562
666
  /**
563
- * The map this element registers its actions in, absent while it has registered none. Code that only triggers
564
- * reads this slot rather than `actions`, so an element with nothing registered stays free of a map: `actions`
565
- * allocates one for whoever asks, and a trigger over an empty map does nothing anyway.
667
+ * The map an element that was declared rather than bound registers its actions in, absent while it has registered
668
+ * none. A binding holds no map of its own: `boundActions` reads the one its declaration holds.
566
669
  */
567
670
  protected _actions?: ActionsMap;
671
+ /**
672
+ * The actions this element answers to, absent where nothing is registered. A binding holds the very map its
673
+ * declaration holds rather than a copy of it, so the slot is read directly: what a trigger costs is one property
674
+ * read, and a rule registered on a `List`'s item template reaches every row through the map they share - the
675
+ * rows built before the registration as much as the ones built after.
676
+ */
677
+ protected get boundActions(): ActionsMap | undefined;
678
+ /** Records that `binding` was made from this element, so a later registration can reach it. */
679
+ protected noteBinding(binding: FieldBase): void;
680
+ /**
681
+ * The bindings made from this element that are still alive, and this element itself. The dead references are
682
+ * dropped as they are found, which is what keeps the set from growing over the life of a long-lived declaration.
683
+ */
684
+ protected get boundElements(): FieldBase[];
685
+ /**
686
+ * The map registrations go into: the declaration's, since that is where behaviour lives. The map is made on the
687
+ * first registration and handed to every binding already made from the declaration, so all of them go on reading
688
+ * one map through a slot of their own.
689
+ */
568
690
  protected get actions(): ActionsMap;
569
- protected set actions(newValue: ActionsMap);
570
691
  /**
571
692
  * Counts the writes that have changed the value of this element or of anything below it. A container reads it
572
693
  * before answering from its value cache, and the read is an ordinary tracked one, so an effect that took a
@@ -653,6 +774,12 @@ export declare abstract class FieldBase<T = any, X extends object = {}> {
653
774
  * pass of its own.
654
775
  */
655
776
  protected get composesValue(): boolean;
777
+ /**
778
+ * Whether `child` composes its value from members of its own. It is what lets a container tell a nested
779
+ * container from a leaf without knowing the classes below it, the way `resetChild` reaches a reset it does not
780
+ * know the shape of.
781
+ */
782
+ protected childComposesValue(child: FieldBase): boolean;
656
783
  /**
657
784
  * Announces what this element's value became over the transaction. The pair carried is (value now, value at the
658
785
  * last announcement), so a value that went A -> B -> A within the transaction says nothing, and the events an
@@ -670,7 +797,7 @@ export declare abstract class FieldBase<T = any, X extends object = {}> {
670
797
  protected [TxSettleValidity](tx: Transaction): void;
671
798
  validate(revalidate?: boolean): void;
672
799
  get valid(): boolean;
673
- get fullValue(): any;
800
+ get fullValue(): T;
674
801
  get isChanged(): boolean;
675
802
  /**
676
803
  * Brings a fresh element into the state a binding of `source` is in: it records what it was declared as, takes
@@ -725,6 +852,15 @@ declare interface FieldSlots<T = any> extends ElementSlots<T> {
725
852
  touched: boolean;
726
853
  }
727
854
 
855
+ /**
856
+ * What Group.fullValue reads back. The indexed access reads each field's fullValue getter, so a nested group
857
+ * contributes its own full structure rather than the partial one its `value` builds, and the recursion carries
858
+ * the guarantee all the way down: every key is present and none of them is null.
859
+ */
860
+ export declare type FieldsToFullValues<T extends GenericFieldsInterface> = {
861
+ [K in keyof T]: T[K]['fullValue'];
862
+ };
863
+
728
864
  export declare type FieldsToValues<T extends GenericFieldsInterface> = {
729
865
  [K in keyof T]: T[K]['value'];
730
866
  };
@@ -732,6 +868,7 @@ export declare type FieldsToValues<T extends GenericFieldsInterface> = {
732
868
  declare namespace Form {
733
869
  export {
734
870
  DisplayMode,
871
+ defaultDisplayMode,
735
872
  transaction,
736
873
  TransactionControl,
737
874
  FieldActionBase,
@@ -758,7 +895,6 @@ declare namespace Form {
758
895
  MessagesWidget,
759
896
  Field,
760
897
  NullableField,
761
- EmptyField,
762
898
  IFieldConstructorActionsList,
763
899
  IFieldConstructorParams,
764
900
  IFieldParams,
@@ -768,6 +904,7 @@ declare namespace Form {
768
904
  FieldBase,
769
905
  GenericFieldsInterface,
770
906
  FieldsToValues,
907
+ FieldsToFullValues,
771
908
  GroupValue,
772
909
  GroupValueInput,
773
910
  Group,
@@ -790,11 +927,7 @@ declare namespace Form {
790
927
  ValidationError,
791
928
  ValidationErrorText,
792
929
  ValidationErrorRenderContent,
793
- RenderableValue,
794
- ValidationFunctionResult,
795
- ValidationFunction,
796
- ValidatorBindingState,
797
- Validator
930
+ RenderableValue
798
931
  }
799
932
  }
800
933
  export default Form;
@@ -803,26 +936,80 @@ export declare const forms: {
803
936
  install(app: any, options?: Partial<FormsConfig>): void;
804
937
  };
805
938
 
806
- declare interface FormsConfig {
939
+ /**
940
+ * Global configuration for @dynamicforms/vue-forms.
941
+ *
942
+ * The configuration is module-global: `setConfig` and the plugin's `install` write the single record this module
943
+ * holds, so in a process running several Vue apps the configuration applied last is the one all of them read.
944
+ */
945
+ export declare interface FormsConfig {
946
+ /** Whether the built-in validators phrase their error messages as `MdString` markdown instead of plain text. */
807
947
  useMarkdownInValidators: boolean;
808
948
  }
809
949
 
810
950
  export declare type GenericFieldsInterface = Record<string, FieldBase>;
811
951
 
952
+ /** The current configuration. The object is the module's own record, and reading it again reports later writes. */
953
+ export declare function getConfig(): FormsConfig;
954
+
812
955
  export declare class Group<T extends GenericFieldsInterface = GenericFieldsInterface, X extends object = {}> extends FieldBase<GroupValue<T>, X> {
813
- protected get state(): ContainerSlots<GroupValue<T>>;
814
- protected get raw(): ContainerSlots<GroupValue<T>>;
956
+ get [Symbol.toStringTag](): string;
957
+ protected get state(): GroupSlots<GroupValue<T>>;
958
+ protected get raw(): GroupSlots<GroupValue<T>>;
815
959
  private readonly _fields;
816
960
  constructor(fields: T, params?: IFieldParams<GroupValueInput<T>, X>);
961
+ /**
962
+ * The name array is copied as well: a rollback puts back the set of members the group held, not the array it
963
+ * went on to hold.
964
+ */
965
+ protected [TxCapture](): TxSnapshot;
817
966
  /**
818
967
  * The copy of a built value that serves as a baseline. The value getter hands out one object per version, and
819
968
  * a baseline holding that same object would report every value as its own original.
820
969
  */
821
970
  private static baseline;
822
- private addField;
971
+ /**
972
+ * Takes `field` into this group under `fieldName`. The group ends up holding it exactly as it holds a field the
973
+ * constructor was given: the field carries the back-reference and the name, its verdict counts towards the
974
+ * group's, and a rule of the field's that names another member of the form reaches it here.
975
+ *
976
+ * The change is announced through the ordinary path, so a group whose value the new field adds to says so once
977
+ * the transaction closes, and its verdict is re-formed over the members it now holds. The baseline behind
978
+ * `isChanged` is not rewritten: a group that gains a field holds something its original value does not carry,
979
+ * and reports itself changed until `originalValue` is written.
980
+ *
981
+ * @throws Error where this group already holds a field under `fieldName`.
982
+ * @throws TypeError where `field` already belongs to a container - pass a `bind()` of it instead.
983
+ */
984
+ addField(fieldName: string, field: FieldBase): this;
985
+ /**
986
+ * Takes the field held under `fieldName` out of this group and hands it back, answering undefined where the
987
+ * group holds no field of that name. The field is released whole: the back-reference and the name are gone, its
988
+ * verdict and its runs in flight no longer count towards the group's, and it is free to be taken by another
989
+ * container. What it holds - its value, its errors, the change history behind `isChanged` - is its own to report.
990
+ *
991
+ * The change is announced through the ordinary path, so the group's value and verdict settle over the members it
992
+ * has left. The baseline behind `isChanged` is not rewritten, the same way it is not on `addField`.
993
+ */
994
+ removeField(fieldName: string): FieldBase | undefined;
995
+ /**
996
+ * Writes one entry of the member map. The map is typed by the fields interface, which names the members a group
997
+ * of that type holds and no others, so both here and in dropEntry it is reached as the plain record it is.
998
+ */
999
+ private static setEntry;
1000
+ /** Drops one entry out of the member map. */
1001
+ private static dropEntry;
823
1002
  private static isValidFields;
824
1003
  static createFromFormData(data: Record<string, any> | null): Group;
825
1004
  field<K extends keyof T>(fieldName: K): T[K] | null;
1005
+ /**
1006
+ * The typed map of this group's members. What it hands out is a view over the map the group holds: reading it
1007
+ * reaches the members themselves, and every write to it is refused - addField() and removeField() are what
1008
+ * change the set. The view is built the first time something asks for it.
1009
+ *
1010
+ * Reading through it is a tracked read of the set of members, so a template rendering off `group.fields`
1011
+ * re-renders when a field is added or removed; what a member itself holds is tracked by that member.
1012
+ */
826
1013
  get fields(): T;
827
1014
  protected get members(): FieldBase[];
828
1015
  get value(): GroupValue<T>;
@@ -841,7 +1028,7 @@ export declare class Group<T extends GenericFieldsInterface = GenericFieldsInter
841
1028
  protected resetTo(source: FieldBase, value: any): void;
842
1029
  get touched(): boolean;
843
1030
  set touched(touched: boolean);
844
- get fullValue(): Record<string, any>;
1031
+ get fullValue(): FieldsToFullValues<T>;
845
1032
  /**
846
1033
  * Records that a member changed its value, so that the transaction in progress works out at commit what this
847
1034
  * group's own value became and announces it once. A mutation method calls it itself; you rarely need to.
@@ -856,7 +1043,16 @@ export declare class Group<T extends GenericFieldsInterface = GenericFieldsInter
856
1043
  protected refreshPreviousValue(): void;
857
1044
  get valid(): boolean;
858
1045
  protected composeValid(): boolean;
1046
+ get busy(): boolean;
1047
+ protected composeBusy(): boolean;
859
1048
  validate(revalidate?: boolean): void;
1049
+ /**
1050
+ * States that `res` was built from the members handed to it. A subclass whose constructor takes something other
1051
+ * than `(fields, params)` - one that composes its own members and passes them to super - never sees them, and
1052
+ * would answer with a binding carrying the declaration's data instead of the record's. That is a difference no
1053
+ * reader would find, so it is refused here rather than returned.
1054
+ */
1055
+ private static assertTookFields;
860
1056
  bind(data?: GroupValueInput<T>, overrides?: IBindParams<GroupValueInput<T>, X>): Group<T, X>;
861
1057
  /**
862
1058
  * A record need not name every member: a key it leaves out is taken from the declaration, the same way a member
@@ -865,19 +1061,32 @@ export declare class Group<T extends GenericFieldsInterface = GenericFieldsInter
865
1061
  rebind(data: GroupValueInput<T>): this;
866
1062
  }
867
1063
 
868
- /** what Group.value reads back: the full field map, or null when no field serializes */
869
- export declare type GroupValue<T extends GenericFieldsInterface> = FieldsToValues<T> | null;
1064
+ /** what a Group holds beyond the container slots: the names of its members, in the order it took them */
1065
+ declare interface GroupSlots<T = any> extends ContainerSlots<T> {
1066
+ /**
1067
+ * The names the group holds its members under. The member map itself is a plain object beside the state, so
1068
+ * this array is what a reader inside an effect depends on: a member added or removed re-runs the effect, and a
1069
+ * rolled-back transaction puts the set back with the rest of the slots.
1070
+ */
1071
+ fieldNames: string[];
1072
+ }
1073
+
1074
+ /**
1075
+ * What Group.value reads back: the values of the fields that serialize, or null when none does. Every key is
1076
+ * optional, because a field that is disabled is left out of the object the group builds.
1077
+ */
1078
+ export declare type GroupValue<T extends GenericFieldsInterface> = Partial<FieldsToValues<T>> | null;
870
1079
 
871
1080
  /** what Group.value and the Group constructor accept: keys left out are simply not assigned */
872
1081
  export declare type GroupValueInput<T extends GenericFieldsInterface> = Partial<FieldsToValues<T>> | null;
873
1082
 
874
1083
  /**
875
- * What bind() accepts beside the data it binds: everything a constructor takes except the value, which the data
876
- * argument states. Of the members named here, bind() reads `originalValue`, `enabled` and `visibility`;
877
- * `validators` and `actions` state what to register and are carried by the binding from its declaration, and the
878
- * rest are the element's to establish.
1084
+ * What bind() accepts beside the data it binds: the three members a binding takes over from the element it was
1085
+ * bound from, and the extended properties. What is left out is left out because a binding cannot honour it -
1086
+ * `validators` and `actions` are carried from the declaration rather than supplied, and `touched` and `errors`
1087
+ * are what the binding establishes for itself as it validates.
879
1088
  */
880
- export declare type IBindParams<T = any, X extends object = {}> = Partial<Omit<IFieldConstructorParams<T>, 'value'>> & Partial<NoInfer<X>>;
1089
+ export declare type IBindParams<T = any, X extends object = {}> = Partial<Pick<IFieldConstructorParams<T>, 'originalValue' | 'enabled' | 'visibility'>> & Partial<NoInfer<X>>;
881
1090
 
882
1091
  export declare interface IFieldConstructorActionsList {
883
1092
  actions?: FieldActionBase[];
@@ -887,7 +1096,7 @@ export declare interface IFieldConstructorActionsList {
887
1096
  /**
888
1097
  * Parameters accepted by field constructors and by bind overrides.
889
1098
  *
890
- * Only writable members are listed. valid, validating, fullValue and isChanged are getter-only, so assigning
1099
+ * Only writable members are listed. valid, validating, busy, fullValue and isChanged are getter-only, so assigning
891
1100
  * them throws a TypeError. So are parent and fieldName: a container writes the slots behind them when it takes
892
1101
  * an element, and nobody else can, which is why the type rejects them.
893
1102
  */
@@ -915,7 +1124,7 @@ export declare type IFieldConstructorParams<T = any> = {
915
1124
  export declare type IFieldParams<T = any, X extends object = {}> = Partial<IFieldConstructorParams<T>> & Partial<NoInfer<X>>;
916
1125
 
917
1126
  declare class InAllowedValues<T = any> extends Validator {
918
- constructor(allowedValues: T[], message?: RenderContentRef);
1127
+ constructor(allowedValues: AllowedValues<T>, message?: RenderContentRef);
919
1128
  }
920
1129
 
921
1130
  export declare function isCallableFunction(msg?: RenderContentRef): msg is RenderContentCallable;
@@ -927,11 +1136,12 @@ export declare function isCallableFunction(msg?: RenderContentRef): msg is Rende
927
1136
  */
928
1137
  export declare function isSimpleComponentDef(msg?: RenderContentRef): msg is SimpleComponentDef;
929
1138
 
930
- declare class LengthInRange<T = any> extends Validator {
1139
+ declare class LengthInRange extends Validator {
931
1140
  constructor(minLength: number, maxLength: number, message?: RenderContentRef);
932
1141
  }
933
1142
 
934
1143
  export declare class List<T extends GenericFieldsInterface = GenericFieldsInterface, X extends object = {}> extends FieldBase<ListValue, X> {
1144
+ get [Symbol.toStringTag](): string;
935
1145
  protected get state(): ListSlots<T>;
936
1146
  protected get raw(): ListSlots<T>;
937
1147
  private _itemTemplate?;
@@ -949,9 +1159,13 @@ export declare class List<T extends GenericFieldsInterface = GenericFieldsInterf
949
1159
  * own values, or an empty group when the list has no template.
950
1160
  */
951
1161
  private createPaddingItem;
1162
+ /** Records that the set of rows changed, so `items` rebuilds the frozen array it hands out at the next read. */
1163
+ private rowsChanged;
1164
+ /** True where `next` is a different set of rows than `previous`: another count, or another row at a position. */
1165
+ private static rowsDiffer;
952
1166
  private setValueInternal;
953
1167
  get value(): ListValue;
954
- set value(newValue: Record<string, any>[]);
1168
+ set value(newValue: ListValue);
955
1169
  /**
956
1170
  * The rows are taken from `source` where the caller supplied no value of its own, so a list nested in a row that
957
1171
  * a whole-list assignment reuses ends up holding what the template gives it rather than what it held before.
@@ -974,8 +1188,31 @@ export declare class List<T extends GenericFieldsInterface = GenericFieldsInterf
974
1188
  protected refreshPreviousValue(): void;
975
1189
  get valid(): boolean;
976
1190
  protected composeValid(): boolean;
1191
+ get busy(): boolean;
1192
+ protected composeBusy(): boolean;
977
1193
  validate(revalidate?: boolean): void;
978
1194
  protected get members(): FieldBase[];
1195
+ /**
1196
+ * How many rows this list holds. The read is tracked, so a template rendering off it re-renders as rows come and
1197
+ * go.
1198
+ */
1199
+ /**
1200
+ * Every row of this list, each one built from all of its fields. Where `value` states what the list serializes -
1201
+ * rows composed of the fields that are enabled, and null where the list is empty - this states what the list
1202
+ * holds: the disabled fields are in it too, and an empty list reads back as an empty array rather than as null.
1203
+ */
1204
+ get fullValue(): FieldsToFullValues<T>[];
1205
+ get length(): number;
1206
+ /**
1207
+ * The rows this list holds, oldest position first. The array is a frozen copy: it states what the list held at
1208
+ * the read and nothing writes back through it - `push`, `insert`, `remove` and `clear` are what change the set -
1209
+ * so a caller may hold on to it. The rows in it are the live elements, so reading one reports what it holds now.
1210
+ *
1211
+ * The copy is built once per change of the set and handed to every reader until the next one: a write inside a
1212
+ * row changes what the list serializes without changing which rows it holds, and the array a reader took stays
1213
+ * the same one across such a write.
1214
+ */
1215
+ get items(): readonly Group<T>[];
979
1216
  get(index: number): Group<T> | undefined;
980
1217
  push(item: any): number;
981
1218
  pop(): Group<T> | undefined;
@@ -1001,12 +1238,20 @@ export declare class ListItemRemovedAction extends FieldActionBase {
1001
1238
  /** what a List holds beyond the container slots: the rows themselves */
1002
1239
  declare interface ListSlots<T extends GenericFieldsInterface = GenericFieldsInterface> extends ContainerSlots<ListValue> {
1003
1240
  rows: Group<T>[] | null;
1241
+ /**
1242
+ * Counts the changes to the set of rows. `items` rebuilds the frozen array it hands out when it moves, and
1243
+ * only then: a write inside a row changes what the list serializes without changing which rows it holds.
1244
+ */
1245
+ rowsVersion: number;
1246
+ /** the frozen array `items` last handed out, together with the rows version it was built from */
1247
+ cachedItems: readonly Group<T>[] | null;
1248
+ cachedItemsVersion: number;
1004
1249
  }
1005
1250
 
1006
1251
  /** what List.value reads back: one plain object per item, or null when the list is empty */
1007
1252
  export declare type ListValue = Record<string, any>[] | null;
1008
1253
 
1009
- declare class MaxLength<T = any> extends Validator {
1254
+ declare class MaxLength extends Validator {
1010
1255
  constructor(maxLength: number, message?: RenderContentRef);
1011
1256
  }
1012
1257
 
@@ -1025,7 +1270,7 @@ export declare class MdString extends String {
1025
1270
 
1026
1271
  export declare const MessagesWidget: DefineComponent<Props, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, ComponentProvideOptions, false, {}, any>;
1027
1272
 
1028
- declare class MinLength<T = any> extends Validator {
1273
+ declare class MinLength extends Validator {
1029
1274
  constructor(minLength: number, message?: RenderContentRef);
1030
1275
  }
1031
1276
 
@@ -1073,7 +1318,7 @@ export declare namespace Operator {
1073
1318
  export function isLogicOperator(operator: Operator): boolean;
1074
1319
  }
1075
1320
 
1076
- declare class Pattern<T = any> extends Validator {
1321
+ declare class Pattern extends Validator {
1077
1322
  constructor(pattern: RegExp, message?: RenderContentRef);
1078
1323
  }
1079
1324
 
@@ -1102,10 +1347,23 @@ export declare type RenderContentNonCallable = string | MdString | SimpleCompone
1102
1347
  */
1103
1348
  export declare type RenderContentRef = RenderContent | Ref<RenderContent>;
1104
1349
 
1105
- declare class Required_2<T = any> extends Validator {
1106
- constructor(message?: RenderContentRef);
1350
+ declare class Required_2 extends Validator {
1351
+ constructor(options?: RequiredOptions);
1352
+ constructor(message?: RenderContentRef, options?: RequiredOptions);
1107
1353
  }
1108
1354
 
1355
+ /** How the value is read before its length is taken. */
1356
+ declare interface RequiredOptions {
1357
+ /**
1358
+ * Whether a string is trimmed before it is measured, so that whitespace alone is no value. Defaults to true.
1359
+ * Set it to false where the spaces are part of what the field holds.
1360
+ */
1361
+ trim?: boolean;
1362
+ }
1363
+
1364
+ /** Writes the members `newConfig` names and leaves the rest as they stand. */
1365
+ export declare function setConfig(newConfig: Partial<FormsConfig>): void;
1366
+
1109
1367
  /**
1110
1368
  * Interface for custom component content definition
1111
1369
  */
@@ -1119,6 +1377,21 @@ export declare class Statement {
1119
1377
  private readonly operand1;
1120
1378
  private readonly operator;
1121
1379
  private readonly operand2;
1380
+ /**
1381
+ * An operand is a nested `Statement`, a `FieldBase` whose value is compared, or a literal. `undefined` and a
1382
+ * function are neither: they are what a misspelled field name and a field accessor handed over uncalled answer
1383
+ * with, and a statement built from one compares nothing and never fires. The position names which of the two
1384
+ * operands is at fault, so the error points at the place the name was written.
1385
+ */
1386
+ private static validateOperand;
1387
+ /**
1388
+ * `NOT` reads one operand, so it is stated with one. Every other operator compares two and takes both, and so
1389
+ * does an operator held in a variable - `Operator.fromString()` answers with the type rather than with the
1390
+ * constant, and the compiler cannot tell from it which of the two forms is meant.
1391
+ *
1392
+ * @throws TypeError where an operand the statement reads is undefined or a function.
1393
+ */
1394
+ constructor(operand1: OperandType, operator: Operator.NOT);
1122
1395
  constructor(operand1: OperandType, operator: Operator, operand2: OperandType);
1123
1396
  /**
1124
1397
  * The value an operand contributes. A field operand names a field of the record the statement is evaluated in:
@@ -1145,6 +1418,8 @@ declare class Transaction {
1145
1418
  private readonly participants;
1146
1419
  /** what a rollback has to put back beyond the state slots, newest last */
1147
1420
  private readonly undo;
1421
+ /** what the commit has to do once the change stands, in the order it was handed in */
1422
+ private readonly settled;
1148
1423
  /** true once the transaction has been unwound, which is what tells a run started inside it to say nothing */
1149
1424
  private unwound;
1150
1425
  /** the depth the announcement in progress is working at, -1 while none is */
@@ -1177,6 +1452,12 @@ declare class Transaction {
1177
1452
  * hands its own undo in here rather than making every snapshot carry the thing.
1178
1453
  */
1179
1454
  whenRolledBack(work: () => void): void;
1455
+ /**
1456
+ * Registers work that runs once the transaction has committed, and not at all where it is rolled back. It is
1457
+ * how an operation defers a step that cannot be taken back - cancelling work in flight, for one - until the
1458
+ * change it belongs to actually stands.
1459
+ */
1460
+ whenCommitted(work: () => void): void;
1180
1461
  /**
1181
1462
  * Notes that an element at or below the depth the pass in progress is working at has been enrolled. The pass
1182
1463
  * then gives up the rest of its batch and is started again over what is now owed, so the newcomer is reached
@@ -1286,6 +1567,24 @@ declare interface TxStructuralEvent {
1286
1567
  * Base validation error class with component rendering capabilities
1287
1568
  */
1288
1569
  export declare class ValidationError {
1570
+ code?: string | undefined;
1571
+ /**
1572
+ * Machine-readable identifier of what failed, in kebab-case. Every validator this library ships states one, so
1573
+ * code that reacts to a particular failure does not have to match the message text, which is translated and
1574
+ * configurable. It is optional: an error built by hand carries whatever its author gives it, or nothing.
1575
+ */
1576
+ constructor(code?: string | undefined);
1577
+ /**
1578
+ * True where `other` is an error of this class that renders exactly as this one does and reports the same code.
1579
+ * Two runs of one validator over an unchanged value produce two instances of one message, and this is what lets
1580
+ * the validator hand the field back the instance it already holds rather than a fresh one: an equal error is not
1581
+ * a new error, and nothing re-renders over a verdict that did not move.
1582
+ *
1583
+ * The comparison is over what renders - the component, its bindings, its body and the classes - because that is
1584
+ * what a reader of `field.errors` sees. A structural comparison of the errors themselves answers nothing useful:
1585
+ * `ValidationErrorRenderContent` holds a Vue `computed`, and two of those are never structurally equal.
1586
+ */
1587
+ sameAs(other: ValidationError): boolean;
1289
1588
  get componentName(): string;
1290
1589
  get componentBindings(): {};
1291
1590
  get componentBody(): string;
@@ -1299,7 +1598,7 @@ export declare class ValidationErrorRenderContent extends ValidationError {
1299
1598
  classes: ClassTypes;
1300
1599
  private text;
1301
1600
  private textType;
1302
- constructor(text: RenderContentRef, classes?: ClassTypes);
1601
+ constructor(text: RenderContentRef, classes?: ClassTypes, code?: string);
1303
1602
  get resolvedText(): RenderContentNonCallable;
1304
1603
  get getTextType(): "string" | "md" | "component";
1305
1604
  get componentName(): string;
@@ -1314,21 +1613,27 @@ export declare class ValidationErrorRenderContent extends ValidationError {
1314
1613
  export declare class ValidationErrorText extends ValidationError {
1315
1614
  text: string;
1316
1615
  classes: ClassTypes;
1317
- constructor(text: string, classes?: ClassTypes);
1616
+ constructor(text: string, classes?: ClassTypes, code?: string);
1318
1617
  get componentName(): string;
1319
1618
  get componentBody(): string;
1320
1619
  get extraClasses(): ClassTypes;
1321
1620
  }
1322
1621
 
1323
- export declare type ValidationFunction<T = any> = (newValue: T, oldValue: T, field: FieldBase<T>) => ValidationFunctionResult | Promise<ValidationFunctionResult>;
1622
+ /**
1623
+ * What a validator does with a value. `signal` aborts when the verdict this call would reach stops counting - a
1624
+ * newer run over the same field, a field the validator was taken off once that removal stands, a transaction that
1625
+ * was unwound - so an asynchronous check hands it to the work it commissions and stops paying for an answer nobody
1626
+ * will read. A function that has nothing to cancel ignores it.
1627
+ */
1628
+ declare type ValidationFunction<T = any> = (newValue: T, oldValue: T, field: FieldBase<T>, signal: AbortSignal) => ValidationFunctionResult | Promise<ValidationFunctionResult>;
1324
1629
 
1325
- export declare type ValidationFunctionResult = ValidationError[] | null;
1630
+ declare type ValidationFunctionResult = ValidationError[] | null;
1326
1631
 
1327
1632
  /**
1328
1633
  * Validator is a specialized action that performs validation when a field's value changes.
1329
1634
  * It automatically adds/removes validation errors from the field's errors array.
1330
1635
  */
1331
- export declare class Validator<T = any> extends ValueChangedAction {
1636
+ declare class Validator<T = any> extends ValueChangedAction {
1332
1637
  private readonly source;
1333
1638
  /**
1334
1639
  * Creates a new validator
@@ -1349,8 +1654,19 @@ export declare class Validator<T = any> extends ValueChangedAction {
1349
1654
  * Takes this validator off `field`: the errors it put there are withdrawn and the verdict is re-formed over what
1350
1655
  * the validators the field still holds have to say. Withdrawing them is what keeps a field from staying invalid
1351
1656
  * on an error no validator is left to take back.
1657
+ *
1658
+ * A run in flight over the field is cancelled once the removal stands, and not before: a transaction that
1659
+ * unwinds puts the validator and its epoch back, and the run it never cancelled goes on to reach the verdict the
1660
+ * field is then owed. Cancelling it as the removal is made would leave the field reporting itself valid over a
1661
+ * value nothing checked, on the strength of a transaction that never happened.
1352
1662
  */
1353
1663
  unregisterFrom(field: FieldBase): void;
1664
+ /**
1665
+ * Cancels the asynchronous run in flight over `field`, where there is one. It is called once the field has
1666
+ * stopped holding this validator: the field's validation epoch has moved on by then, so the run's verdict is
1667
+ * already dropped and the signal it handed its check aborts with it.
1668
+ */
1669
+ protected abandonRun(field: FieldBase): void;
1354
1670
  /**
1355
1671
  * What this validator remembers about one of the fields it validates. One instance may be registered on several
1356
1672
  * fields - every row of a list carries the instances the item template carries - so each field has a record of
@@ -1368,8 +1684,10 @@ export declare class Validator<T = any> extends ValueChangedAction {
1368
1684
  }
1369
1685
 
1370
1686
  /** What a validator remembers about one field it is registered on; a subclass widens it. */
1371
- export declare interface ValidatorBindingState {
1687
+ declare interface ValidatorBindingState {
1372
1688
  run: number;
1689
+ /** cancels the asynchronous run in flight over the field, absent while no run is waiting for its verdict */
1690
+ abandon?: () => void;
1373
1691
  }
1374
1692
 
1375
1693
  export declare namespace Validators {
@@ -1377,7 +1695,9 @@ export declare namespace Validators {
1377
1695
  CompareTo,
1378
1696
  CompareToTarget,
1379
1697
  InAllowedValues,
1698
+ AllowedValues,
1380
1699
  Required_2 as Required,
1700
+ RequiredOptions,
1381
1701
  Pattern,
1382
1702
  ValidationFunctionResult,
1383
1703
  ValidationFunction,