@getodk/xforms-engine 0.2.0 → 0.3.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/body/BodyElementDefinition.d.ts +4 -3
- package/dist/body/RepeatElementDefinition.d.ts +2 -2
- package/dist/body/control/ControlDefinition.d.ts +2 -2
- package/dist/body/control/select/ItemDefinition.d.ts +2 -2
- package/dist/body/control/select/ItemsetDefinition.d.ts +5 -4
- package/dist/body/group/BaseGroupDefinition.d.ts +1 -1
- package/dist/body/group/PresentationGroupDefinition.d.ts +1 -1
- package/dist/client/BaseNode.d.ts +68 -2
- package/dist/client/GroupNode.d.ts +2 -0
- package/dist/client/ModelValueNode.d.ts +37 -0
- package/dist/client/NoteNode.d.ts +53 -0
- package/dist/client/RootNode.d.ts +2 -0
- package/dist/client/SelectNode.d.ts +5 -3
- package/dist/client/StringNode.d.ts +5 -3
- package/dist/client/SubtreeNode.d.ts +2 -0
- package/dist/client/TextRange.d.ts +85 -2
- package/dist/client/constants.d.ts +9 -0
- package/dist/client/hierarchy.d.ts +14 -9
- package/dist/client/node-types.d.ts +2 -1
- package/dist/client/{RepeatRangeNode.d.ts → repeat/BaseRepeatRangeNode.d.ts} +18 -17
- package/dist/client/{RepeatInstanceNode.d.ts → repeat/RepeatInstanceNode.d.ts} +9 -8
- package/dist/client/repeat/RepeatRangeControlledNode.d.ts +19 -0
- package/dist/client/repeat/RepeatRangeUncontrolledNode.d.ts +20 -0
- package/dist/client/validation.d.ts +163 -0
- package/dist/expression/DependentExpression.d.ts +12 -8
- package/dist/index.d.ts +9 -4
- package/dist/index.js +2635 -678
- package/dist/index.js.map +1 -1
- package/dist/instance/Group.d.ts +3 -1
- package/dist/instance/ModelValue.d.ts +40 -0
- package/dist/instance/Note.d.ts +42 -0
- package/dist/instance/Root.d.ts +2 -0
- package/dist/instance/SelectField.d.ts +10 -4
- package/dist/instance/StringField.d.ts +11 -5
- package/dist/instance/Subtree.d.ts +2 -0
- package/dist/instance/abstract/DescendantNode.d.ts +5 -6
- package/dist/instance/abstract/InstanceNode.d.ts +2 -0
- package/dist/instance/hierarchy.d.ts +10 -5
- package/dist/instance/internal-api/ValidationContext.d.ts +21 -0
- package/dist/instance/{RepeatRange.d.ts → repeat/BaseRepeatRange.d.ts} +46 -45
- package/dist/instance/{RepeatInstance.d.ts → repeat/RepeatInstance.d.ts} +13 -12
- package/dist/instance/repeat/RepeatRangeControlled.d.ts +16 -0
- package/dist/instance/repeat/RepeatRangeUncontrolled.d.ts +35 -0
- package/dist/instance/text/TextRange.d.ts +4 -4
- package/dist/lib/reactivity/createComputedExpression.d.ts +6 -1
- package/dist/lib/reactivity/createNoteReadonlyThunk.d.ts +5 -0
- package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +1 -1
- package/dist/lib/reactivity/node-state/createSpecifiedState.d.ts +1 -1
- package/dist/lib/reactivity/text/createFieldHint.d.ts +3 -3
- package/dist/lib/reactivity/text/createNodeLabel.d.ts +2 -2
- package/dist/lib/reactivity/text/createNoteText.d.ts +25 -0
- package/dist/lib/reactivity/text/createTextRange.d.ts +5 -7
- package/dist/lib/reactivity/validation/createAggregatedViolations.d.ts +9 -0
- package/dist/lib/reactivity/validation/createValidation.d.ts +18 -0
- package/dist/model/BindDefinition.d.ts +4 -2
- package/dist/model/BindElement.d.ts +1 -0
- package/dist/model/{ValueNodeDefinition.d.ts → LeafNodeDefinition.d.ts} +2 -2
- package/dist/model/NodeDefinition.d.ts +8 -8
- package/dist/model/RepeatInstanceDefinition.d.ts +2 -2
- package/dist/model/RepeatRangeDefinition.d.ts +14 -4
- package/dist/parse/NoteNodeDefinition.d.ts +31 -0
- package/dist/parse/expression/RepeatCountControlExpression.d.ts +19 -0
- package/dist/parse/text/HintDefinition.d.ts +9 -0
- package/dist/parse/text/ItemLabelDefinition.d.ts +9 -0
- package/dist/parse/text/ItemsetLabelDefinition.d.ts +13 -0
- package/dist/parse/text/LabelDefinition.d.ts +15 -0
- package/dist/parse/text/MessageDefinition.d.ts +15 -0
- package/dist/parse/text/OutputChunkDefinition.d.ts +8 -0
- package/dist/parse/text/ReferenceChunkDefinition.d.ts +8 -0
- package/dist/parse/text/StaticTextChunkDefinition.d.ts +10 -0
- package/dist/parse/text/TranslationChunkDefinition.d.ts +9 -0
- package/dist/parse/text/abstract/TextChunkDefinition.d.ts +18 -0
- package/dist/parse/text/abstract/TextElementDefinition.d.ts +23 -0
- package/dist/parse/text/abstract/TextRangeDefinition.d.ts +35 -0
- package/dist/parse/xpath/dependency-analysis.d.ts +40 -0
- package/dist/parse/xpath/path-resolution.d.ts +70 -0
- package/dist/parse/xpath/predicate-analysis.d.ts +30 -0
- package/dist/parse/xpath/reference-parsing.d.ts +18 -0
- package/dist/parse/xpath/semantic-analysis.d.ts +98 -0
- package/dist/parse/xpath/syntax-traversal.d.ts +69 -0
- package/dist/solid.js +2636 -679
- package/dist/solid.js.map +1 -1
- package/package.json +14 -15
- package/src/body/BodyElementDefinition.ts +4 -3
- package/src/body/RepeatElementDefinition.ts +5 -17
- package/src/body/control/ControlDefinition.ts +4 -3
- package/src/body/control/select/ItemDefinition.ts +3 -3
- package/src/body/control/select/ItemsetDefinition.ts +29 -12
- package/src/body/control/select/ItemsetNodesetExpression.ts +1 -1
- package/src/body/group/BaseGroupDefinition.ts +3 -2
- package/src/body/group/PresentationGroupDefinition.ts +1 -1
- package/src/client/BaseNode.ts +73 -7
- package/src/client/GroupNode.ts +2 -0
- package/src/client/ModelValueNode.ts +40 -0
- package/src/client/NoteNode.ts +74 -0
- package/src/client/README.md +1 -0
- package/src/client/RootNode.ts +2 -0
- package/src/client/SelectNode.ts +5 -3
- package/src/client/StringNode.ts +5 -3
- package/src/client/SubtreeNode.ts +2 -0
- package/src/client/TextRange.ts +99 -2
- package/src/client/constants.ts +10 -0
- package/src/client/hierarchy.ts +30 -14
- package/src/client/node-types.ts +8 -1
- package/src/client/{RepeatRangeNode.ts → repeat/BaseRepeatRangeNode.ts} +18 -19
- package/src/client/{RepeatInstanceNode.ts → repeat/RepeatInstanceNode.ts} +10 -8
- package/src/client/repeat/RepeatRangeControlledNode.ts +20 -0
- package/src/client/repeat/RepeatRangeUncontrolledNode.ts +21 -0
- package/src/client/validation.ts +199 -0
- package/src/expression/DependentExpression.ts +45 -27
- package/src/index.ts +15 -8
- package/src/instance/Group.ts +10 -4
- package/src/instance/ModelValue.ts +104 -0
- package/src/instance/Note.ts +142 -0
- package/src/instance/Root.ts +9 -3
- package/src/instance/SelectField.ts +28 -6
- package/src/instance/StringField.ts +35 -9
- package/src/instance/Subtree.ts +9 -3
- package/src/instance/abstract/DescendantNode.ts +6 -7
- package/src/instance/abstract/InstanceNode.ts +20 -6
- package/src/instance/children.ts +42 -15
- package/src/instance/hierarchy.ts +21 -2
- package/src/instance/internal-api/ValidationContext.ts +23 -0
- package/src/instance/{RepeatRange.ts → repeat/BaseRepeatRange.ts} +114 -99
- package/src/instance/{RepeatInstance.ts → repeat/RepeatInstance.ts} +27 -22
- package/src/instance/repeat/RepeatRangeControlled.ts +82 -0
- package/src/instance/repeat/RepeatRangeUncontrolled.ts +67 -0
- package/src/instance/text/TextRange.ts +10 -4
- package/src/lib/reactivity/createComputedExpression.ts +22 -24
- package/src/lib/reactivity/createNoteReadonlyThunk.ts +33 -0
- package/src/lib/reactivity/createSelectItems.ts +21 -14
- package/src/lib/reactivity/node-state/createSharedNodeState.ts +1 -1
- package/src/lib/reactivity/text/createFieldHint.ts +9 -7
- package/src/lib/reactivity/text/createNodeLabel.ts +7 -5
- package/src/lib/reactivity/text/createNoteText.ts +72 -0
- package/src/lib/reactivity/text/createTextRange.ts +17 -90
- package/src/lib/reactivity/validation/createAggregatedViolations.ts +70 -0
- package/src/lib/reactivity/validation/createValidation.ts +196 -0
- package/src/model/BindComputation.ts +0 -4
- package/src/model/BindDefinition.ts +8 -6
- package/src/model/BindElement.ts +1 -0
- package/src/model/{ValueNodeDefinition.ts → LeafNodeDefinition.ts} +4 -4
- package/src/model/ModelBindMap.ts +4 -0
- package/src/model/NodeDefinition.ts +12 -12
- package/src/model/RepeatInstanceDefinition.ts +2 -2
- package/src/model/RepeatRangeDefinition.ts +49 -8
- package/src/model/RootDefinition.ts +7 -3
- package/src/parse/NoteNodeDefinition.ts +70 -0
- package/src/parse/TODO.md +3 -0
- package/src/parse/expression/RepeatCountControlExpression.ts +44 -0
- package/src/parse/text/HintDefinition.ts +25 -0
- package/src/parse/text/ItemLabelDefinition.ts +25 -0
- package/src/parse/text/ItemsetLabelDefinition.ts +44 -0
- package/src/parse/text/LabelDefinition.ts +61 -0
- package/src/parse/text/MessageDefinition.ts +49 -0
- package/src/parse/text/OutputChunkDefinition.ts +25 -0
- package/src/parse/text/ReferenceChunkDefinition.ts +14 -0
- package/src/parse/text/StaticTextChunkDefinition.ts +19 -0
- package/src/parse/text/TranslationChunkDefinition.ts +38 -0
- package/src/parse/text/abstract/TextChunkDefinition.ts +38 -0
- package/src/parse/text/abstract/TextElementDefinition.ts +71 -0
- package/src/parse/text/abstract/TextRangeDefinition.ts +70 -0
- package/src/parse/xpath/dependency-analysis.ts +105 -0
- package/src/parse/xpath/path-resolution.ts +475 -0
- package/src/parse/xpath/predicate-analysis.ts +61 -0
- package/src/parse/xpath/reference-parsing.ts +90 -0
- package/src/parse/xpath/semantic-analysis.ts +466 -0
- package/src/parse/xpath/syntax-traversal.ts +129 -0
- package/dist/body/text/HintDefinition.d.ts +0 -11
- package/dist/body/text/LabelDefinition.d.ts +0 -22
- package/dist/body/text/TextElementDefinition.d.ts +0 -33
- package/dist/body/text/TextElementOutputPart.d.ts +0 -13
- package/dist/body/text/TextElementPart.d.ts +0 -13
- package/dist/body/text/TextElementReferencePart.d.ts +0 -7
- package/dist/body/text/TextElementStaticPart.d.ts +0 -7
- package/dist/lib/xpath/analysis.d.ts +0 -23
- package/src/body/text/HintDefinition.ts +0 -26
- package/src/body/text/LabelDefinition.ts +0 -68
- package/src/body/text/TextElementDefinition.ts +0 -97
- package/src/body/text/TextElementOutputPart.ts +0 -27
- package/src/body/text/TextElementPart.ts +0 -31
- package/src/body/text/TextElementReferencePart.ts +0 -21
- package/src/body/text/TextElementStaticPart.ts +0 -26
- package/src/lib/xpath/analysis.ts +0 -241
package/src/client/TextRange.ts
CHANGED
|
@@ -1,7 +1,70 @@
|
|
|
1
1
|
import type { ActiveLanguage } from './FormLanguage.ts';
|
|
2
2
|
import type { RootNodeState } from './RootNode.ts';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* **COMMENTARY**
|
|
6
|
+
*
|
|
7
|
+
* The spec makes naming and mapping these cases a bit more complex than would
|
|
8
|
+
* be ideal. The intent is to clearly identify distinct text definitions (and
|
|
9
|
+
* sub-structural parts) from a source form, in a way that semantically lines up
|
|
10
|
+
* with the ways they will need to be handled at runtime and conveyed to
|
|
11
|
+
* clients. This is the mapping:
|
|
12
|
+
*
|
|
13
|
+
* - 'output': All output values, i.e.:
|
|
14
|
+
* - `output/@value`
|
|
15
|
+
*
|
|
16
|
+
* - 'translation':
|
|
17
|
+
*
|
|
18
|
+
* - Valid XPath translation expressions, in a context accepting mixed
|
|
19
|
+
* translation/static syntax, i.e.:
|
|
20
|
+
*
|
|
21
|
+
* - `h:head//bind/@jr:constraintMsg[is-translation-expr()]`
|
|
22
|
+
* - `h:head//bind/@jr:requiredMsg[is-translation-expr()]`
|
|
23
|
+
*
|
|
24
|
+
* Here, `is-translation-expr()` is a fictional shorthand for checking
|
|
25
|
+
* that the attribute's value is a valid `jr:itext(...)` FunctionCall
|
|
26
|
+
* expression. Note that, per spec, these attributes **do not accept
|
|
27
|
+
* arbitrary XPath expressions**! The non-translation case is treated as
|
|
28
|
+
* static text, not parsed for e.g. an XPath [string] Literal expression.
|
|
29
|
+
* This is why we have introduced this 'translation' case, distinct from
|
|
30
|
+
* 'reference', which previously handled translated labels and hints.
|
|
31
|
+
*
|
|
32
|
+
* - Valid XPath translation expressions, in a context accepting arbitrary
|
|
33
|
+
* XPath expressions, i.e.:
|
|
34
|
+
*
|
|
35
|
+
* - `h:body//label/@ref[is-translation-expr()]`
|
|
36
|
+
*
|
|
37
|
+
* - 'static':
|
|
38
|
+
* - `h:head//bind/@jr:constraintMsg[not(is-translation-expr())]`
|
|
39
|
+
* - `h:head//bind/@jr:requiredMsg[not(is-translation-expr())]`
|
|
40
|
+
* - `h:body//label/text()`
|
|
41
|
+
* - `h:body//hint/text()`
|
|
42
|
+
*
|
|
43
|
+
* (See notes above for clarification of `is-translation-expr()`.)
|
|
44
|
+
*
|
|
45
|
+
* - 'reference': Any XPath **non-translation** expression defined as a label's
|
|
46
|
+
* (or hint's) `ref` attribute, i.e.
|
|
47
|
+
* - `h:body//label/@ref[not(is-translation-expr())]`
|
|
48
|
+
* - `h:body//hint/@ref[not(is-translation-expr())]`
|
|
49
|
+
*
|
|
50
|
+
* (See notes above for clarification of `is-translation-expr()`.)
|
|
51
|
+
*
|
|
52
|
+
* @todo It's unclear whether this will all become simpler or more compelex when
|
|
53
|
+
* we add support for outputs in translations. In theory, the actual translation
|
|
54
|
+
* `<text>` nodes map quite well to the `TextRange` concept (i.e. they are a
|
|
55
|
+
* range of static and output chunks, just like labels and hints). The potential
|
|
56
|
+
* for complications arise from XPath implementation details being largely
|
|
57
|
+
* opaque (as in, the `jr:itext` implementation is encapsulated in the `xpath`
|
|
58
|
+
* package, and the engine doesn't really deal with itext translations at the
|
|
59
|
+
* node level at all).
|
|
60
|
+
*/
|
|
61
|
+
// prettier-ignore
|
|
62
|
+
export type TextChunkSource =
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
64
|
+
| 'output'
|
|
65
|
+
| 'reference'
|
|
66
|
+
| 'translation'
|
|
67
|
+
| 'static';
|
|
5
68
|
|
|
6
69
|
/**
|
|
7
70
|
* @todo This (and everything else to do with {@link TextRange}s is for
|
|
@@ -23,6 +86,39 @@ export interface TextChunk {
|
|
|
23
86
|
get formatted(): unknown;
|
|
24
87
|
}
|
|
25
88
|
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
90
|
+
export type ElementTextRole = 'hint' | 'label' | 'item-label';
|
|
91
|
+
export type ValidationTextRole = 'constraintMsg' | 'requiredMsg';
|
|
92
|
+
export type TextRole = ElementTextRole | ValidationTextRole;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Specifies the origin of a {@link TextRange}.
|
|
96
|
+
*
|
|
97
|
+
* - 'form': text is computed from the form definition, as specified for the
|
|
98
|
+
* {@link TextRole}. User-facing clients should present text with this origin
|
|
99
|
+
* where appropriate.
|
|
100
|
+
*
|
|
101
|
+
* - 'form-derived': the form definition lacks a text definition for the
|
|
102
|
+
* {@link TextRole}, but an appropriate one has been derived from a related
|
|
103
|
+
* (and semantically appropriate) aspect of the form (example: a select item
|
|
104
|
+
* without a label may derive that label from the item's value). User-facing
|
|
105
|
+
* clients should generally present text with this origin where provided; this
|
|
106
|
+
* origin clarifies the source of such text.
|
|
107
|
+
*
|
|
108
|
+
* - 'engine': the form definition lacks a definition for the {@link TextRole},
|
|
109
|
+
* but provides a constant default in its absence. User facing clients may
|
|
110
|
+
* disregard these constant text values, or may use them where a sensible
|
|
111
|
+
* default is desired. Clients may also use these constants as keys for
|
|
112
|
+
* translation purposes, as appropriate. Non-user facing clients may reference
|
|
113
|
+
* these constants for e.g. testing purposes.
|
|
114
|
+
*/
|
|
115
|
+
// prettier-ignore
|
|
116
|
+
export type TextOrigin =
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
118
|
+
| 'form'
|
|
119
|
+
| 'form-derived'
|
|
120
|
+
| 'engine';
|
|
121
|
+
|
|
26
122
|
/**
|
|
27
123
|
* Represents aspects of a form which produce text, which _might_ be:
|
|
28
124
|
*
|
|
@@ -53,7 +149,8 @@ export interface TextChunk {
|
|
|
53
149
|
* a text range's role may correspond to the "short" or "guidance" `form` of a
|
|
54
150
|
* {@link https://getodk.github.io/xforms-spec/#languages | translation}).
|
|
55
151
|
*/
|
|
56
|
-
export interface TextRange<Role extends
|
|
152
|
+
export interface TextRange<Role extends TextRole, Origin extends TextOrigin = TextOrigin> {
|
|
153
|
+
readonly origin: Origin;
|
|
57
154
|
readonly role: Role;
|
|
58
155
|
|
|
59
156
|
[Symbol.iterator](): Iterable<TextChunk>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ValidationTextRole } from './TextRange.ts';
|
|
2
|
+
|
|
3
|
+
export const VALIDATION_TEXT = {
|
|
4
|
+
constraintMsg: 'Condition not satisfied: constraint',
|
|
5
|
+
requiredMsg: 'Condition not satisfied: required',
|
|
6
|
+
} as const satisfies Record<ValidationTextRole, string>;
|
|
7
|
+
|
|
8
|
+
type ValidationTextDefaults = typeof VALIDATION_TEXT;
|
|
9
|
+
|
|
10
|
+
export type ValidationTextDefault<Role extends ValidationTextRole> = ValidationTextDefaults[Role];
|
package/src/client/hierarchy.ts
CHANGED
|
@@ -1,17 +1,41 @@
|
|
|
1
1
|
import type { ExpandUnion } from '@getodk/common/types/helpers.d.ts';
|
|
2
2
|
import type { GroupNode } from './GroupNode.ts';
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
3
|
+
import type { ModelValueNode } from './ModelValueNode.ts';
|
|
4
|
+
import type { NoteNode } from './NoteNode.ts';
|
|
5
|
+
import type { RepeatInstanceNode } from './repeat/RepeatInstanceNode.ts';
|
|
6
|
+
import type { RepeatRangeControlledNode } from './repeat/RepeatRangeControlledNode.ts';
|
|
7
|
+
import type { RepeatRangeUncontrolledNode } from './repeat/RepeatRangeUncontrolledNode.ts';
|
|
5
8
|
import type { RootNode } from './RootNode.ts';
|
|
6
9
|
import type { SelectNode } from './SelectNode.ts';
|
|
7
10
|
import type { StringNode } from './StringNode.ts';
|
|
8
11
|
import type { SubtreeNode } from './SubtreeNode.ts';
|
|
9
12
|
|
|
10
13
|
// prettier-ignore
|
|
11
|
-
export type
|
|
14
|
+
export type AnyControlNode =
|
|
15
|
+
| NoteNode
|
|
12
16
|
| SelectNode
|
|
13
17
|
| StringNode;
|
|
14
18
|
|
|
19
|
+
// prettier-ignore
|
|
20
|
+
export type AnyLeafNode =
|
|
21
|
+
| AnyControlNode
|
|
22
|
+
| ModelValueNode;
|
|
23
|
+
|
|
24
|
+
// prettier-ignore
|
|
25
|
+
export type RepeatRangeNode =
|
|
26
|
+
| RepeatRangeControlledNode
|
|
27
|
+
| RepeatRangeUncontrolledNode;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Any of the concrete node types which may be a parent of non-repeat instance
|
|
31
|
+
* child nodes.
|
|
32
|
+
*/
|
|
33
|
+
export type GeneralParentNode =
|
|
34
|
+
| RootNode // eslint-disable-line @typescript-eslint/sort-type-constituents
|
|
35
|
+
| SubtreeNode
|
|
36
|
+
| GroupNode
|
|
37
|
+
| RepeatInstanceNode;
|
|
38
|
+
|
|
15
39
|
/**
|
|
16
40
|
* Any of the concrete node types which may be a parent of any other node.
|
|
17
41
|
*
|
|
@@ -21,12 +45,10 @@ export type AnyLeafNode =
|
|
|
21
45
|
* - Repeat instances should (continue to) specify {@link RepeatRangeNode}.
|
|
22
46
|
* - All other child nodes should specify {@link GeneralParentNode}.
|
|
23
47
|
*/
|
|
48
|
+
// prettier-ignore
|
|
24
49
|
export type AnyParentNode =
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
| GroupNode
|
|
28
|
-
| RepeatRangeNode
|
|
29
|
-
| RepeatInstanceNode;
|
|
50
|
+
| GeneralParentNode
|
|
51
|
+
| RepeatRangeNode;
|
|
30
52
|
|
|
31
53
|
/**
|
|
32
54
|
* Any of the concrete node types a client may get from the engine's form
|
|
@@ -38,12 +60,6 @@ export type AnyParentNode =
|
|
|
38
60
|
*/
|
|
39
61
|
export type AnyNode = ExpandUnion<AnyLeafNode | AnyParentNode>;
|
|
40
62
|
|
|
41
|
-
/**
|
|
42
|
-
* Any of the concrete node types which may be a parent of non-repeat instance
|
|
43
|
-
* child nodes.
|
|
44
|
-
*/
|
|
45
|
-
export type GeneralParentNode = Exclude<AnyParentNode, RepeatRangeNode>;
|
|
46
|
-
|
|
47
63
|
/**
|
|
48
64
|
* Any of the concrete node types which may be a child of any other node.
|
|
49
65
|
*
|
package/src/client/node-types.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
// prettier-ignore
|
|
2
|
+
export type RepeatRangeNodeType =
|
|
3
|
+
| 'repeat-range:controlled'
|
|
4
|
+
| 'repeat-range:uncontrolled'
|
|
5
|
+
|
|
1
6
|
// prettier-ignore
|
|
2
7
|
export type InstanceNodeType =
|
|
3
8
|
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
4
9
|
| 'root'
|
|
5
|
-
|
|
|
10
|
+
| RepeatRangeNodeType
|
|
6
11
|
| 'repeat-instance'
|
|
7
12
|
| 'group'
|
|
8
13
|
| 'subtree'
|
|
14
|
+
| 'model-value'
|
|
15
|
+
| 'note'
|
|
9
16
|
| 'select'
|
|
10
17
|
| 'string';
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { BaseNode, BaseNodeState } from '
|
|
3
|
-
import type { NodeAppearances } from '
|
|
1
|
+
import type { AnyRepeatRangeDefinition } from '../../model/RepeatRangeDefinition.ts';
|
|
2
|
+
import type { BaseNode, BaseNodeState } from '../BaseNode.ts';
|
|
3
|
+
import type { NodeAppearances } from '../NodeAppearances.ts';
|
|
4
|
+
import type { RootNode } from '../RootNode.ts';
|
|
5
|
+
import type { TextRange } from '../TextRange.ts';
|
|
6
|
+
import type { GeneralParentNode } from '../hierarchy.ts';
|
|
7
|
+
import type { RepeatRangeNodeType } from '../node-types.ts';
|
|
8
|
+
import type { AncestorNodeValidationState } from '../validation.ts';
|
|
4
9
|
import type { RepeatInstanceNode } from './RepeatInstanceNode.ts';
|
|
5
|
-
import type { RootNode } from './RootNode.ts';
|
|
6
|
-
import type { TextRange } from './TextRange.ts';
|
|
7
|
-
import type { GeneralParentNode } from './hierarchy.ts';
|
|
8
10
|
|
|
9
|
-
export interface
|
|
11
|
+
export interface BaseRepeatRangeNodeState extends BaseNodeState {
|
|
10
12
|
get hint(): null;
|
|
11
13
|
get label(): TextRange<'label'> | null;
|
|
12
14
|
|
|
@@ -16,7 +18,7 @@ export interface RepeatRangeNodeState extends BaseNodeState {
|
|
|
16
18
|
* Note: the web-forms engine's representation of this structure differs from
|
|
17
19
|
* the underlying XForms specification's primary instance structure.
|
|
18
20
|
*
|
|
19
|
-
* @see {@link
|
|
21
|
+
* @see {@link BaseRepeatRangeNode} for additional detail.
|
|
20
22
|
*/
|
|
21
23
|
get children(): readonly RepeatInstanceNode[];
|
|
22
24
|
|
|
@@ -24,12 +26,12 @@ export interface RepeatRangeNodeState extends BaseNodeState {
|
|
|
24
26
|
get value(): null;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
export type RepeatRangeNodeAppearances = NodeAppearances<
|
|
29
|
+
export type RepeatRangeNodeAppearances = NodeAppearances<AnyRepeatRangeDefinition>;
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* Represents a contiguous set of zero or more {@link RepeatInstanceNode}s
|
|
31
33
|
* (accessed by its
|
|
32
|
-
* {@link
|
|
34
|
+
* {@link BaseRepeatRangeNodeState.children | `currentState.children`}).
|
|
33
35
|
*
|
|
34
36
|
* This structure is modeled as a node, like any other, in the web-forms engine
|
|
35
37
|
* representation, which notably differs from the corresponding structure in the
|
|
@@ -88,17 +90,14 @@ export type RepeatRangeNodeAppearances = NodeAppearances<RepeatRangeDefinition>;
|
|
|
88
90
|
* Importantly, if the state of a given repeat range has no instances, no aspect
|
|
89
91
|
* of these repeats will be present in the underlying XForms primary instance
|
|
90
92
|
* state, but the web-forms engine's representations **retains a reference** to
|
|
91
|
-
* its {@link
|
|
93
|
+
* its {@link BaseRepeatRangeNode}.
|
|
92
94
|
*/
|
|
93
|
-
export interface
|
|
94
|
-
readonly nodeType:
|
|
95
|
+
export interface BaseRepeatRangeNode extends BaseNode {
|
|
96
|
+
readonly nodeType: RepeatRangeNodeType;
|
|
95
97
|
readonly appearances: RepeatRangeNodeAppearances;
|
|
96
|
-
readonly definition:
|
|
98
|
+
readonly definition: AnyRepeatRangeDefinition;
|
|
97
99
|
readonly root: RootNode;
|
|
98
100
|
readonly parent: GeneralParentNode;
|
|
99
|
-
readonly currentState:
|
|
100
|
-
|
|
101
|
-
addInstances(afterIndex?: number, count?: number): RootNode;
|
|
102
|
-
|
|
103
|
-
removeInstances(startIndex: number, count?: number): RootNode;
|
|
101
|
+
readonly currentState: BaseRepeatRangeNodeState;
|
|
102
|
+
readonly validationState: AncestorNodeValidationState;
|
|
104
103
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { RepeatInstanceDefinition } from '
|
|
2
|
-
import type { RepeatTemplateDefinition } from '
|
|
3
|
-
import type { BaseNode, BaseNodeState } from '
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
6
|
-
import type { RootNode } from '
|
|
7
|
-
import type {
|
|
1
|
+
import type { RepeatInstanceDefinition } from '../../model/RepeatInstanceDefinition.ts';
|
|
2
|
+
import type { RepeatTemplateDefinition } from '../../model/RepeatTemplateDefinition.ts';
|
|
3
|
+
import type { BaseNode, BaseNodeState } from '../BaseNode.ts';
|
|
4
|
+
import type { GeneralChildNode, RepeatRangeNode } from '../hierarchy.ts';
|
|
5
|
+
import type { NodeAppearances } from '../NodeAppearances.ts';
|
|
6
|
+
import type { RootNode } from '../RootNode.ts';
|
|
7
|
+
import type { AncestorNodeValidationState } from '../validation.ts';
|
|
8
|
+
import type { BaseRepeatRangeNode } from './BaseRepeatRangeNode.ts';
|
|
8
9
|
|
|
9
10
|
export interface RepeatInstanceNodeState extends BaseNodeState {
|
|
10
11
|
// TODO(?): Previous iteration included an `index` getter here. I don't see it
|
|
@@ -37,9 +38,10 @@ export interface RepeatInstanceNode extends BaseNode {
|
|
|
37
38
|
* Note: the web-forms engine's representation of this structure differs from
|
|
38
39
|
* the underlying XForms specification's primary instance structure.
|
|
39
40
|
*
|
|
40
|
-
* @see {@link
|
|
41
|
+
* @see {@link BaseRepeatRangeNode} for additional detail.
|
|
41
42
|
*/
|
|
42
43
|
readonly parent: RepeatRangeNode;
|
|
43
44
|
|
|
44
45
|
readonly currentState: RepeatInstanceNodeState;
|
|
46
|
+
readonly validationState: AncestorNodeValidationState;
|
|
45
47
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ControlledRepeatRangeDefinition } from '../../model/RepeatRangeDefinition.ts';
|
|
2
|
+
import type { BaseRepeatRangeNode, BaseRepeatRangeNodeState } from './BaseRepeatRangeNode.ts';
|
|
3
|
+
import type { RepeatRangeUncontrolledNode } from './RepeatRangeUncontrolledNode.ts';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* {@inheritDoc BaseRepeatRangeNodeState}
|
|
7
|
+
* @see {@link BaseRepeatRangeNodeState}
|
|
8
|
+
*/
|
|
9
|
+
export interface RepeatRangeControlledState extends BaseRepeatRangeNodeState {}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Represents a repeat range whose repeat instances are controlled by the
|
|
13
|
+
* engine, and cannot be added or removed by a client. Functionality and
|
|
14
|
+
* semantics are otherwise consistent with an [uncontrolled]
|
|
15
|
+
* {@link RepeatRangeUncontrolledNode}.
|
|
16
|
+
*/
|
|
17
|
+
export interface RepeatRangeControlledNode extends BaseRepeatRangeNode {
|
|
18
|
+
readonly nodeType: 'repeat-range:controlled';
|
|
19
|
+
readonly definition: ControlledRepeatRangeDefinition;
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { UncontrolledRepeatRangeDefinition } from '../../model/RepeatRangeDefinition.ts';
|
|
2
|
+
import type { RootNode } from '../RootNode.ts';
|
|
3
|
+
import type { BaseRepeatRangeNode, BaseRepeatRangeNodeState } from './BaseRepeatRangeNode.ts';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* {@inheritDoc BaseRepeatRangeNodeState}
|
|
7
|
+
* @see {@link BaseRepeatRangeNodeState}
|
|
8
|
+
*/
|
|
9
|
+
export interface RepeatRangeUncontrolledState extends BaseRepeatRangeNodeState {}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* {@inheritDoc BaseRepeatRangeNode}
|
|
13
|
+
* @see {@link BaseRepeatRangeNode}
|
|
14
|
+
*/
|
|
15
|
+
export interface RepeatRangeUncontrolledNode extends BaseRepeatRangeNode {
|
|
16
|
+
readonly nodeType: 'repeat-range:uncontrolled';
|
|
17
|
+
readonly definition: UncontrolledRepeatRangeDefinition;
|
|
18
|
+
|
|
19
|
+
addInstances(afterIndex?: number, count?: number): RootNode;
|
|
20
|
+
removeInstances(startIndex: number, count?: number): RootNode;
|
|
21
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { NodeID } from '../instance/identity.ts';
|
|
2
|
+
import type { BaseNode, BaseNodeState } from './BaseNode.ts';
|
|
3
|
+
import type { OpaqueReactiveObjectFactory } from './OpaqueReactiveObjectFactory.ts';
|
|
4
|
+
import type { RootNode } from './RootNode.ts';
|
|
5
|
+
import type { TextRange } from './TextRange.ts';
|
|
6
|
+
|
|
7
|
+
// This interface exists so that extensions can share JSDoc for `valid`.
|
|
8
|
+
interface BaseValidity {
|
|
9
|
+
/**
|
|
10
|
+
* Specifies the unambiguous validity state for each validity condition of a
|
|
11
|
+
* given node, or for the derived validity of any parent node whose descendants
|
|
12
|
+
* are validated.
|
|
13
|
+
*
|
|
14
|
+
* For {@link ValidationCondition | form-defined conditions}, validity is
|
|
15
|
+
* determined as follows:
|
|
16
|
+
*
|
|
17
|
+
*
|
|
18
|
+
* expression | state | blank | non-blank
|
|
19
|
+
* ------------:|:----------|:-------:|:---------:
|
|
20
|
+
* `constraint` | `true`\* | ✅ | ✅
|
|
21
|
+
* `constraint` | `false` | ✅ | ❌
|
|
22
|
+
* `required` | `false`\* | ✅ | ✅
|
|
23
|
+
* `required` | `true` | ❌ | ✅
|
|
24
|
+
*
|
|
25
|
+
* - \* = default (expression not defined)
|
|
26
|
+
* - ✅ = `valid: true`
|
|
27
|
+
* - ❌ = `valid: false`
|
|
28
|
+
*/
|
|
29
|
+
readonly valid: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Form-defined conditions which determine node validity.
|
|
34
|
+
*
|
|
35
|
+
* @see {@link https://getodk.github.io/xforms-spec/#bind-attributes | `constraint` and `required` bind attributes}
|
|
36
|
+
*/
|
|
37
|
+
export type ValidationCondition = 'constraint' | 'required';
|
|
38
|
+
|
|
39
|
+
interface ValidationConditionMessageRoles {
|
|
40
|
+
readonly constraint: 'constraintMsg';
|
|
41
|
+
readonly required: 'requiredMsg';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type ValidationConditionMessageRole<Condition extends ValidationCondition> =
|
|
45
|
+
ValidationConditionMessageRoles[Condition];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Source of a condition's violation message.
|
|
49
|
+
*
|
|
50
|
+
* - Form-defined messages (specified by the
|
|
51
|
+
* {@link https://getodk.github.io/xforms-spec/#bind-attributes | `jr:constraintMsg` and `jr:requiredMsg`}
|
|
52
|
+
* attributes) will be favored when provided by the form, and will be
|
|
53
|
+
* translated according to the form's active language (where applicable).
|
|
54
|
+
*
|
|
55
|
+
* - Otherwise, an engine-defined message will be provided as a fallback. This
|
|
56
|
+
* fallback is provided mainly for API consistency, and may be referenced for
|
|
57
|
+
* testing purposes; user-facing clients are expected to provide fallback
|
|
58
|
+
* messaging language most appropriate for their user neeeds. Engine-defined
|
|
59
|
+
* fallback messages **are not translated**. They are intended to be used, if
|
|
60
|
+
* at all, as sentinel values when a form-defined message is not available.
|
|
61
|
+
*/
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
63
|
+
export type ViolationMessageSource = 'form' | 'engine';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @see {@link ViolationMessage.asString}
|
|
67
|
+
*/
|
|
68
|
+
// prettier-ignore
|
|
69
|
+
type ViolationMessageAsString<
|
|
70
|
+
Source extends ViolationMessageSource,
|
|
71
|
+
Condition extends ValidationCondition,
|
|
72
|
+
> =
|
|
73
|
+
Source extends 'form'
|
|
74
|
+
? string
|
|
75
|
+
: `Condition not satisfied: ${Condition}`;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A violation message is provided for every violation of a form-defined
|
|
79
|
+
* {@link ValidationCondition}.
|
|
80
|
+
*/
|
|
81
|
+
export interface ViolationMessage<
|
|
82
|
+
Condition extends ValidationCondition,
|
|
83
|
+
Source extends ViolationMessageSource = ViolationMessageSource,
|
|
84
|
+
> extends TextRange<ValidationConditionMessageRole<Condition>> {
|
|
85
|
+
/**
|
|
86
|
+
* - Form-defined violation messages may produce arbitrary text. This text may
|
|
87
|
+
* be translated
|
|
88
|
+
* ({@link https://getodk.github.io/xforms-spec/#fn:jr:itext | `jr:itext`}),
|
|
89
|
+
* and it may be dynamic (translations may reference form state with
|
|
90
|
+
* {@link https://getodk.github.io/xforms-spec/#body-elements | `<output>`}).
|
|
91
|
+
*
|
|
92
|
+
* - When a form-defined violation message is not available, an engine-defined
|
|
93
|
+
* message will be provided in its place. Engine-defined violation messages
|
|
94
|
+
* are statically defined (and therefore not presently translated by the
|
|
95
|
+
* engine). Their static value can also be referenced as a static type, by
|
|
96
|
+
* checking {@link isFallbackMessage}.
|
|
97
|
+
*/
|
|
98
|
+
get asString(): ViolationMessageAsString<Source, Condition>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface ConditionSatisfied<Condition extends ValidationCondition> extends BaseValidity {
|
|
102
|
+
readonly condition: Condition;
|
|
103
|
+
readonly valid: true;
|
|
104
|
+
readonly message: null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface ConditionViolation<Condition extends ValidationCondition> extends BaseValidity {
|
|
108
|
+
readonly condition: Condition;
|
|
109
|
+
readonly valid: false;
|
|
110
|
+
readonly message: ViolationMessage<Condition>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export type ConditionValidation<Condition extends ValidationCondition> =
|
|
114
|
+
| ConditionSatisfied<Condition>
|
|
115
|
+
| ConditionViolation<Condition>;
|
|
116
|
+
|
|
117
|
+
export type AnyViolation = ConditionViolation<ValidationCondition>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Represents the validation state of a leaf (or value) node.
|
|
121
|
+
*
|
|
122
|
+
* Validity is computed for two conditions:
|
|
123
|
+
*
|
|
124
|
+
* - {@link constraint}: arbitrary form-defined condition which specifies
|
|
125
|
+
* whether a (non-blank) value is considered valid
|
|
126
|
+
*
|
|
127
|
+
* - {@link required}: when a node is required, the node must have a non-blank
|
|
128
|
+
* value to be considered valid
|
|
129
|
+
*
|
|
130
|
+
* Only one of these conditions can be violated (applicability is mutually
|
|
131
|
+
* exclusive). As such, {@link violation} provides a convenient way to determine
|
|
132
|
+
* whether a leaf/value node is valid with a single (null) check.
|
|
133
|
+
*
|
|
134
|
+
* @see {@link BaseValidity.valid} for additional details on how these
|
|
135
|
+
* conditions are evaluated (and how they interact with one another).
|
|
136
|
+
*/
|
|
137
|
+
export interface LeafNodeValidationState {
|
|
138
|
+
get constraint(): ConditionValidation<'constraint'>;
|
|
139
|
+
get required(): ConditionValidation<'required'>;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Violations are mutually exclusive:
|
|
143
|
+
*
|
|
144
|
+
* - {@link constraint} can only be violated by a non-blank value
|
|
145
|
+
* - {@link required} can only be violated by a blank value
|
|
146
|
+
*
|
|
147
|
+
* As such, at most one violation can be present. If none is present,
|
|
148
|
+
* the node is considered valid.
|
|
149
|
+
*/
|
|
150
|
+
get violation(): AnyViolation | null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Provides a reference to any leaf/value node which currently violates either
|
|
155
|
+
* of its validity conditions.
|
|
156
|
+
*
|
|
157
|
+
* Any client can safely assume:
|
|
158
|
+
*
|
|
159
|
+
* - {@link nodeId} will be a stable reference to a node with the same
|
|
160
|
+
* {@link BaseNode.nodeId | `nodeId`}.
|
|
161
|
+
*
|
|
162
|
+
* - {@link node} will have reference equality to the same node object, within
|
|
163
|
+
* the active form instance's {@link RootNode} tree
|
|
164
|
+
*
|
|
165
|
+
* - {@link reference} will be a **current** reference to the same node object's
|
|
166
|
+
* **computed** {@link BaseNodeState.reference | `currentState.reference`}
|
|
167
|
+
*
|
|
168
|
+
* Any client utilizing the engine's reactive APIs (having provided an
|
|
169
|
+
* {@link OpaqueReactiveObjectFactory}) can safely assume that {@link reference}
|
|
170
|
+
* will be recomputed and updated in tandem with the affected node's own
|
|
171
|
+
* computed `currentState.reference` as well.
|
|
172
|
+
*
|
|
173
|
+
* @todo this type currently exposes multiple ways to reference the affected
|
|
174
|
+
* node. This is intended to maximize flexibility: it's not yet clear how
|
|
175
|
+
* clients will be best served by which reference mechanism. It is expected that
|
|
176
|
+
* each property will be directly computed from the affected node.
|
|
177
|
+
*/
|
|
178
|
+
export interface DescendantNodeViolationReference {
|
|
179
|
+
readonly nodeId: NodeID;
|
|
180
|
+
|
|
181
|
+
get reference(): string;
|
|
182
|
+
get violation(): AnyViolation;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Provides access from any ancestor/parent node, to identify any validity
|
|
187
|
+
* violations present on any of its leaf/value node descendants.
|
|
188
|
+
*
|
|
189
|
+
* @see {@link DescendantNodeViolationReference} for details on how descendants
|
|
190
|
+
* may be referenced when such a violation is present.
|
|
191
|
+
*/
|
|
192
|
+
export interface AncestorNodeValidationState {
|
|
193
|
+
get violations(): readonly DescendantNodeViolationReference[];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// prettier-ignore
|
|
197
|
+
export type NodeValidationState =
|
|
198
|
+
| AncestorNodeValidationState
|
|
199
|
+
| LeafNodeValidationState;
|