@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.
Files changed (184) hide show
  1. package/dist/body/BodyElementDefinition.d.ts +4 -3
  2. package/dist/body/RepeatElementDefinition.d.ts +2 -2
  3. package/dist/body/control/ControlDefinition.d.ts +2 -2
  4. package/dist/body/control/select/ItemDefinition.d.ts +2 -2
  5. package/dist/body/control/select/ItemsetDefinition.d.ts +5 -4
  6. package/dist/body/group/BaseGroupDefinition.d.ts +1 -1
  7. package/dist/body/group/PresentationGroupDefinition.d.ts +1 -1
  8. package/dist/client/BaseNode.d.ts +68 -2
  9. package/dist/client/GroupNode.d.ts +2 -0
  10. package/dist/client/ModelValueNode.d.ts +37 -0
  11. package/dist/client/NoteNode.d.ts +53 -0
  12. package/dist/client/RootNode.d.ts +2 -0
  13. package/dist/client/SelectNode.d.ts +5 -3
  14. package/dist/client/StringNode.d.ts +5 -3
  15. package/dist/client/SubtreeNode.d.ts +2 -0
  16. package/dist/client/TextRange.d.ts +85 -2
  17. package/dist/client/constants.d.ts +9 -0
  18. package/dist/client/hierarchy.d.ts +14 -9
  19. package/dist/client/node-types.d.ts +2 -1
  20. package/dist/client/{RepeatRangeNode.d.ts → repeat/BaseRepeatRangeNode.d.ts} +18 -17
  21. package/dist/client/{RepeatInstanceNode.d.ts → repeat/RepeatInstanceNode.d.ts} +9 -8
  22. package/dist/client/repeat/RepeatRangeControlledNode.d.ts +19 -0
  23. package/dist/client/repeat/RepeatRangeUncontrolledNode.d.ts +20 -0
  24. package/dist/client/validation.d.ts +163 -0
  25. package/dist/expression/DependentExpression.d.ts +12 -8
  26. package/dist/index.d.ts +9 -4
  27. package/dist/index.js +2635 -678
  28. package/dist/index.js.map +1 -1
  29. package/dist/instance/Group.d.ts +3 -1
  30. package/dist/instance/ModelValue.d.ts +40 -0
  31. package/dist/instance/Note.d.ts +42 -0
  32. package/dist/instance/Root.d.ts +2 -0
  33. package/dist/instance/SelectField.d.ts +10 -4
  34. package/dist/instance/StringField.d.ts +11 -5
  35. package/dist/instance/Subtree.d.ts +2 -0
  36. package/dist/instance/abstract/DescendantNode.d.ts +5 -6
  37. package/dist/instance/abstract/InstanceNode.d.ts +2 -0
  38. package/dist/instance/hierarchy.d.ts +10 -5
  39. package/dist/instance/internal-api/ValidationContext.d.ts +21 -0
  40. package/dist/instance/{RepeatRange.d.ts → repeat/BaseRepeatRange.d.ts} +46 -45
  41. package/dist/instance/{RepeatInstance.d.ts → repeat/RepeatInstance.d.ts} +13 -12
  42. package/dist/instance/repeat/RepeatRangeControlled.d.ts +16 -0
  43. package/dist/instance/repeat/RepeatRangeUncontrolled.d.ts +35 -0
  44. package/dist/instance/text/TextRange.d.ts +4 -4
  45. package/dist/lib/reactivity/createComputedExpression.d.ts +6 -1
  46. package/dist/lib/reactivity/createNoteReadonlyThunk.d.ts +5 -0
  47. package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +1 -1
  48. package/dist/lib/reactivity/node-state/createSpecifiedState.d.ts +1 -1
  49. package/dist/lib/reactivity/text/createFieldHint.d.ts +3 -3
  50. package/dist/lib/reactivity/text/createNodeLabel.d.ts +2 -2
  51. package/dist/lib/reactivity/text/createNoteText.d.ts +25 -0
  52. package/dist/lib/reactivity/text/createTextRange.d.ts +5 -7
  53. package/dist/lib/reactivity/validation/createAggregatedViolations.d.ts +9 -0
  54. package/dist/lib/reactivity/validation/createValidation.d.ts +18 -0
  55. package/dist/model/BindDefinition.d.ts +4 -2
  56. package/dist/model/BindElement.d.ts +1 -0
  57. package/dist/model/{ValueNodeDefinition.d.ts → LeafNodeDefinition.d.ts} +2 -2
  58. package/dist/model/NodeDefinition.d.ts +8 -8
  59. package/dist/model/RepeatInstanceDefinition.d.ts +2 -2
  60. package/dist/model/RepeatRangeDefinition.d.ts +14 -4
  61. package/dist/parse/NoteNodeDefinition.d.ts +31 -0
  62. package/dist/parse/expression/RepeatCountControlExpression.d.ts +19 -0
  63. package/dist/parse/text/HintDefinition.d.ts +9 -0
  64. package/dist/parse/text/ItemLabelDefinition.d.ts +9 -0
  65. package/dist/parse/text/ItemsetLabelDefinition.d.ts +13 -0
  66. package/dist/parse/text/LabelDefinition.d.ts +15 -0
  67. package/dist/parse/text/MessageDefinition.d.ts +15 -0
  68. package/dist/parse/text/OutputChunkDefinition.d.ts +8 -0
  69. package/dist/parse/text/ReferenceChunkDefinition.d.ts +8 -0
  70. package/dist/parse/text/StaticTextChunkDefinition.d.ts +10 -0
  71. package/dist/parse/text/TranslationChunkDefinition.d.ts +9 -0
  72. package/dist/parse/text/abstract/TextChunkDefinition.d.ts +18 -0
  73. package/dist/parse/text/abstract/TextElementDefinition.d.ts +23 -0
  74. package/dist/parse/text/abstract/TextRangeDefinition.d.ts +35 -0
  75. package/dist/parse/xpath/dependency-analysis.d.ts +40 -0
  76. package/dist/parse/xpath/path-resolution.d.ts +70 -0
  77. package/dist/parse/xpath/predicate-analysis.d.ts +30 -0
  78. package/dist/parse/xpath/reference-parsing.d.ts +18 -0
  79. package/dist/parse/xpath/semantic-analysis.d.ts +98 -0
  80. package/dist/parse/xpath/syntax-traversal.d.ts +69 -0
  81. package/dist/solid.js +2636 -679
  82. package/dist/solid.js.map +1 -1
  83. package/package.json +14 -15
  84. package/src/body/BodyElementDefinition.ts +4 -3
  85. package/src/body/RepeatElementDefinition.ts +5 -17
  86. package/src/body/control/ControlDefinition.ts +4 -3
  87. package/src/body/control/select/ItemDefinition.ts +3 -3
  88. package/src/body/control/select/ItemsetDefinition.ts +29 -12
  89. package/src/body/control/select/ItemsetNodesetExpression.ts +1 -1
  90. package/src/body/group/BaseGroupDefinition.ts +3 -2
  91. package/src/body/group/PresentationGroupDefinition.ts +1 -1
  92. package/src/client/BaseNode.ts +73 -7
  93. package/src/client/GroupNode.ts +2 -0
  94. package/src/client/ModelValueNode.ts +40 -0
  95. package/src/client/NoteNode.ts +74 -0
  96. package/src/client/README.md +1 -0
  97. package/src/client/RootNode.ts +2 -0
  98. package/src/client/SelectNode.ts +5 -3
  99. package/src/client/StringNode.ts +5 -3
  100. package/src/client/SubtreeNode.ts +2 -0
  101. package/src/client/TextRange.ts +99 -2
  102. package/src/client/constants.ts +10 -0
  103. package/src/client/hierarchy.ts +30 -14
  104. package/src/client/node-types.ts +8 -1
  105. package/src/client/{RepeatRangeNode.ts → repeat/BaseRepeatRangeNode.ts} +18 -19
  106. package/src/client/{RepeatInstanceNode.ts → repeat/RepeatInstanceNode.ts} +10 -8
  107. package/src/client/repeat/RepeatRangeControlledNode.ts +20 -0
  108. package/src/client/repeat/RepeatRangeUncontrolledNode.ts +21 -0
  109. package/src/client/validation.ts +199 -0
  110. package/src/expression/DependentExpression.ts +45 -27
  111. package/src/index.ts +15 -8
  112. package/src/instance/Group.ts +10 -4
  113. package/src/instance/ModelValue.ts +104 -0
  114. package/src/instance/Note.ts +142 -0
  115. package/src/instance/Root.ts +9 -3
  116. package/src/instance/SelectField.ts +28 -6
  117. package/src/instance/StringField.ts +35 -9
  118. package/src/instance/Subtree.ts +9 -3
  119. package/src/instance/abstract/DescendantNode.ts +6 -7
  120. package/src/instance/abstract/InstanceNode.ts +20 -6
  121. package/src/instance/children.ts +42 -15
  122. package/src/instance/hierarchy.ts +21 -2
  123. package/src/instance/internal-api/ValidationContext.ts +23 -0
  124. package/src/instance/{RepeatRange.ts → repeat/BaseRepeatRange.ts} +114 -99
  125. package/src/instance/{RepeatInstance.ts → repeat/RepeatInstance.ts} +27 -22
  126. package/src/instance/repeat/RepeatRangeControlled.ts +82 -0
  127. package/src/instance/repeat/RepeatRangeUncontrolled.ts +67 -0
  128. package/src/instance/text/TextRange.ts +10 -4
  129. package/src/lib/reactivity/createComputedExpression.ts +22 -24
  130. package/src/lib/reactivity/createNoteReadonlyThunk.ts +33 -0
  131. package/src/lib/reactivity/createSelectItems.ts +21 -14
  132. package/src/lib/reactivity/node-state/createSharedNodeState.ts +1 -1
  133. package/src/lib/reactivity/text/createFieldHint.ts +9 -7
  134. package/src/lib/reactivity/text/createNodeLabel.ts +7 -5
  135. package/src/lib/reactivity/text/createNoteText.ts +72 -0
  136. package/src/lib/reactivity/text/createTextRange.ts +17 -90
  137. package/src/lib/reactivity/validation/createAggregatedViolations.ts +70 -0
  138. package/src/lib/reactivity/validation/createValidation.ts +196 -0
  139. package/src/model/BindComputation.ts +0 -4
  140. package/src/model/BindDefinition.ts +8 -6
  141. package/src/model/BindElement.ts +1 -0
  142. package/src/model/{ValueNodeDefinition.ts → LeafNodeDefinition.ts} +4 -4
  143. package/src/model/ModelBindMap.ts +4 -0
  144. package/src/model/NodeDefinition.ts +12 -12
  145. package/src/model/RepeatInstanceDefinition.ts +2 -2
  146. package/src/model/RepeatRangeDefinition.ts +49 -8
  147. package/src/model/RootDefinition.ts +7 -3
  148. package/src/parse/NoteNodeDefinition.ts +70 -0
  149. package/src/parse/TODO.md +3 -0
  150. package/src/parse/expression/RepeatCountControlExpression.ts +44 -0
  151. package/src/parse/text/HintDefinition.ts +25 -0
  152. package/src/parse/text/ItemLabelDefinition.ts +25 -0
  153. package/src/parse/text/ItemsetLabelDefinition.ts +44 -0
  154. package/src/parse/text/LabelDefinition.ts +61 -0
  155. package/src/parse/text/MessageDefinition.ts +49 -0
  156. package/src/parse/text/OutputChunkDefinition.ts +25 -0
  157. package/src/parse/text/ReferenceChunkDefinition.ts +14 -0
  158. package/src/parse/text/StaticTextChunkDefinition.ts +19 -0
  159. package/src/parse/text/TranslationChunkDefinition.ts +38 -0
  160. package/src/parse/text/abstract/TextChunkDefinition.ts +38 -0
  161. package/src/parse/text/abstract/TextElementDefinition.ts +71 -0
  162. package/src/parse/text/abstract/TextRangeDefinition.ts +70 -0
  163. package/src/parse/xpath/dependency-analysis.ts +105 -0
  164. package/src/parse/xpath/path-resolution.ts +475 -0
  165. package/src/parse/xpath/predicate-analysis.ts +61 -0
  166. package/src/parse/xpath/reference-parsing.ts +90 -0
  167. package/src/parse/xpath/semantic-analysis.ts +466 -0
  168. package/src/parse/xpath/syntax-traversal.ts +129 -0
  169. package/dist/body/text/HintDefinition.d.ts +0 -11
  170. package/dist/body/text/LabelDefinition.d.ts +0 -22
  171. package/dist/body/text/TextElementDefinition.d.ts +0 -33
  172. package/dist/body/text/TextElementOutputPart.d.ts +0 -13
  173. package/dist/body/text/TextElementPart.d.ts +0 -13
  174. package/dist/body/text/TextElementReferencePart.d.ts +0 -7
  175. package/dist/body/text/TextElementStaticPart.d.ts +0 -7
  176. package/dist/lib/xpath/analysis.d.ts +0 -23
  177. package/src/body/text/HintDefinition.ts +0 -26
  178. package/src/body/text/LabelDefinition.ts +0 -68
  179. package/src/body/text/TextElementDefinition.ts +0 -97
  180. package/src/body/text/TextElementOutputPart.ts +0 -27
  181. package/src/body/text/TextElementPart.ts +0 -31
  182. package/src/body/text/TextElementReferencePart.ts +0 -21
  183. package/src/body/text/TextElementStaticPart.ts +0 -26
  184. package/src/lib/xpath/analysis.ts +0 -241
@@ -2,7 +2,8 @@ import { identity } from '@getodk/common/lib/identity.ts';
2
2
  import type { Accessor } from 'solid-js';
3
3
  import type { InputDefinition } from '../body/control/InputDefinition.ts';
4
4
  import type { StringNode, StringNodeAppearances } from '../client/StringNode.ts';
5
- import type { TextRange } from '../index.ts';
5
+ import type { TextRange } from '../client/TextRange.ts';
6
+ import type { AnyViolation, LeafNodeValidationState } from '../client/validation.ts';
6
7
  import { createValueState } from '../lib/reactivity/createValueState.ts';
7
8
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
8
9
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
@@ -11,17 +12,20 @@ import { createSharedNodeState } from '../lib/reactivity/node-state/createShared
11
12
  import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
12
13
  import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
13
14
  import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
14
- import type { ValueNodeDefinition } from '../model/ValueNodeDefinition.ts';
15
+ import type { SharedValidationState } from '../lib/reactivity/validation/createValidation.ts';
16
+ import { createValidationState } from '../lib/reactivity/validation/createValidation.ts';
17
+ import type { LeafNodeDefinition } from '../model/LeafNodeDefinition.ts';
15
18
  import type { Root } from './Root.ts';
16
19
  import type { DescendantNodeStateSpec } from './abstract/DescendantNode.ts';
17
20
  import { DescendantNode } from './abstract/DescendantNode.ts';
18
21
  import type { GeneralParentNode } from './hierarchy.ts';
19
22
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
20
23
  import type { SubscribableDependency } from './internal-api/SubscribableDependency.ts';
24
+ import type { ValidationContext } from './internal-api/ValidationContext.ts';
21
25
  import type { ValueContext } from './internal-api/ValueContext.ts';
22
26
 
23
- export interface StringFieldDefinition extends ValueNodeDefinition {
24
- readonly bodyElement: InputDefinition | null;
27
+ export interface StringFieldDefinition extends LeafNodeDefinition {
28
+ readonly bodyElement: InputDefinition;
25
29
  }
26
30
 
27
31
  interface StringFieldStateSpec extends DescendantNodeStateSpec<string> {
@@ -34,8 +38,14 @@ interface StringFieldStateSpec extends DescendantNodeStateSpec<string> {
34
38
 
35
39
  export class StringField
36
40
  extends DescendantNode<StringFieldDefinition, StringFieldStateSpec, null>
37
- implements StringNode, EvaluationContext, SubscribableDependency, ValueContext<string>
41
+ implements
42
+ StringNode,
43
+ EvaluationContext,
44
+ SubscribableDependency,
45
+ ValidationContext,
46
+ ValueContext<string>
38
47
  {
48
+ private readonly validation: SharedValidationState;
39
49
  protected readonly state: SharedNodeState<StringFieldStateSpec>;
40
50
 
41
51
  // InstanceNode
@@ -46,6 +56,10 @@ export class StringField
46
56
  readonly appearances: StringNodeAppearances;
47
57
  readonly currentState: CurrentState<StringFieldStateSpec>;
48
58
 
59
+ get validationState(): LeafNodeValidationState {
60
+ return this.validation.currentState;
61
+ }
62
+
49
63
  // ValueContext
50
64
  readonly encodeValue = identity<string>;
51
65
 
@@ -54,7 +68,11 @@ export class StringField
54
68
  constructor(parent: GeneralParentNode, definition: StringFieldDefinition) {
55
69
  super(parent, definition);
56
70
 
57
- this.appearances = (definition.bodyElement?.appearances ?? null) as StringNodeAppearances;
71
+ this.appearances = definition.bodyElement.appearances;
72
+
73
+ const sharedStateOptions = {
74
+ clientStateFactory: this.engineConfig.stateFactory,
75
+ };
58
76
 
59
77
  const state = createSharedNodeState(
60
78
  this.scope,
@@ -70,14 +88,22 @@ export class StringField
70
88
  valueOptions: null,
71
89
  value: createValueState(this),
72
90
  },
73
- {
74
- clientStateFactory: this.engineConfig.stateFactory,
75
- }
91
+ sharedStateOptions
76
92
  );
77
93
 
78
94
  this.state = state;
79
95
  this.engineState = state.engineState;
80
96
  this.currentState = state.currentState;
97
+ this.validation = createValidationState(this, sharedStateOptions);
98
+ }
99
+
100
+ getViolation(): AnyViolation | null {
101
+ return this.validation.engineState.violation;
102
+ }
103
+
104
+ // ValidationContext
105
+ isBlank(): boolean {
106
+ return this.engineState.value === '';
81
107
  }
82
108
 
83
109
  // InstanceNode
@@ -1,5 +1,6 @@
1
1
  import { type Accessor } from 'solid-js';
2
2
  import type { SubtreeDefinition, SubtreeNode } from '../client/SubtreeNode.ts';
3
+ import type { AncestorNodeValidationState } from '../client/validation.ts';
3
4
  import type { ChildrenState } from '../lib/reactivity/createChildrenState.ts';
4
5
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
5
6
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -8,6 +9,7 @@ import type { CurrentState } from '../lib/reactivity/node-state/createCurrentSta
8
9
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
9
10
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
10
11
  import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
12
+ import { createAggregatedViolations } from '../lib/reactivity/validation/createAggregatedViolations.ts';
11
13
  import type { DescendantNodeSharedStateSpec } from './abstract/DescendantNode.ts';
12
14
  import { DescendantNode } from './abstract/DescendantNode.ts';
13
15
  import { buildChildren } from './children.ts';
@@ -38,6 +40,7 @@ export class Subtree
38
40
  readonly nodeType = 'subtree';
39
41
  readonly appearances = null;
40
42
  readonly currentState: MaterializedChildren<CurrentState<SubtreeStateSpec>, GeneralChildNode>;
43
+ readonly validationState: AncestorNodeValidationState;
41
44
 
42
45
  constructor(parent: GeneralParentNode, definition: SubtreeDefinition) {
43
46
  super(parent, definition);
@@ -46,6 +49,10 @@ export class Subtree
46
49
 
47
50
  this.childrenState = childrenState;
48
51
 
52
+ const sharedStateOptions = {
53
+ clientStateFactory: this.engineConfig.stateFactory,
54
+ };
55
+
49
56
  const state = createSharedNodeState(
50
57
  this.scope,
51
58
  {
@@ -60,9 +67,7 @@ export class Subtree
60
67
  valueOptions: null,
61
68
  value: null,
62
69
  },
63
- {
64
- clientStateFactory: this.engineConfig.stateFactory,
65
- }
70
+ sharedStateOptions
66
71
  );
67
72
 
68
73
  this.state = state;
@@ -74,6 +79,7 @@ export class Subtree
74
79
  );
75
80
 
76
81
  childrenState.setChildren(buildChildren(this));
82
+ this.validationState = createAggregatedViolations(this, sharedStateOptions);
77
83
  }
78
84
 
79
85
  getChildren(): readonly GeneralChildNode[] {
@@ -4,15 +4,14 @@ import type { BaseNode } from '../../client/BaseNode.ts';
4
4
  import { createComputedExpression } from '../../lib/reactivity/createComputedExpression.ts';
5
5
  import type { ReactiveScope } from '../../lib/reactivity/scope.ts';
6
6
  import type { AnyDescendantNodeDefinition } from '../../model/DescendentNodeDefinition.ts';
7
+ import type { LeafNodeDefinition } from '../../model/LeafNodeDefinition.ts';
7
8
  import type { AnyNodeDefinition } from '../../model/NodeDefinition.ts';
8
9
  import type { RepeatInstanceDefinition } from '../../model/RepeatInstanceDefinition.ts';
9
- import type { ValueNodeDefinition } from '../../model/ValueNodeDefinition.ts';
10
- import type { RepeatInstance } from '../RepeatInstance.ts';
11
- import type { RepeatRange } from '../RepeatRange.ts';
12
- import type { Root } from '../Root.ts';
13
- import type { AnyChildNode, GeneralParentNode } from '../hierarchy.ts';
10
+ import type { AnyChildNode, GeneralParentNode, RepeatRange } from '../hierarchy.ts';
14
11
  import type { EvaluationContext } from '../internal-api/EvaluationContext.ts';
15
12
  import type { SubscribableDependency } from '../internal-api/SubscribableDependency.ts';
13
+ import type { RepeatInstance } from '../repeat/RepeatInstance.ts';
14
+ import type { Root } from '../Root.ts';
16
15
  import type { InstanceNodeStateSpec } from './InstanceNode.ts';
17
16
  import { InstanceNode } from './InstanceNode.ts';
18
17
 
@@ -37,7 +36,7 @@ export type DescendantNodeDefinition = Extract<
37
36
 
38
37
  // prettier-ignore
39
38
  export type DescendantNodeParent<Definition extends DescendantNodeDefinition> =
40
- Definition extends ValueNodeDefinition
39
+ Definition extends LeafNodeDefinition
41
40
  ? GeneralParentNode
42
41
  : Definition extends RepeatInstanceDefinition
43
42
  ? RepeatRange
@@ -96,7 +95,7 @@ export abstract class DescendantNode<
96
95
  return this.isSelfRelevant();
97
96
  };
98
97
 
99
- protected readonly isRequired: Accessor<boolean>;
98
+ readonly isRequired: Accessor<boolean>;
100
99
 
101
100
  readonly root: Root;
102
101
  readonly evaluator: XFormsXPathEvaluator;
@@ -3,6 +3,7 @@ import type { Accessor, Signal } from 'solid-js';
3
3
  import type { BaseNode } from '../../client/BaseNode.ts';
4
4
  import type { NodeAppearances } from '../../client/NodeAppearances.ts';
5
5
  import type { InstanceNodeType } from '../../client/node-types.ts';
6
+ import type { NodeValidationState } from '../../client/validation.ts';
6
7
  import type { TextRange } from '../../index.ts';
7
8
  import type { MaterializedChildren } from '../../lib/reactivity/materializeCurrentStateChildren.ts';
8
9
  import type { CurrentState } from '../../lib/reactivity/node-state/createCurrentState.ts';
@@ -117,6 +118,8 @@ export abstract class InstanceNode<
117
118
 
118
119
  abstract readonly currentState: InstanceNodeCurrentState<Spec, Child>;
119
120
 
121
+ abstract readonly validationState: NodeValidationState;
122
+
120
123
  // BaseNode: structural
121
124
  abstract readonly root: Root;
122
125
 
@@ -230,11 +233,22 @@ export abstract class InstanceNode<
230
233
  subscribe(): void {
231
234
  const { engineState } = this;
232
235
 
233
- if (engineState.relevant) {
234
- engineState.reference;
235
- engineState.relevant;
236
- engineState.children;
237
- engineState.value;
238
- }
236
+ // Note: a previous iteration of this default implementation guarded these
237
+ // reactive reads behind a relevance check. This caused timing issues for
238
+ // downstream computations referencing a node whose relevance changes.
239
+ //
240
+ // That original guard was intended to reduce excessive redundant
241
+ // computations, and so removing it is intended as a naive compromise of
242
+ // performance for obvious correctness improvements.
243
+ //
244
+ // This compromise, like many others, will be moot if/when we decide to
245
+ // decouple XPath evaluation from the browser/XML DOM: reactive
246
+ // subscriptions would be established by evaluation of the expressions
247
+ // themselves (as they traverse instance state and access values), rather
248
+ // than this safer/less focused approach.
249
+ engineState.reference;
250
+ engineState.relevant;
251
+ engineState.children;
252
+ engineState.value;
239
253
  }
240
254
  }
@@ -1,15 +1,20 @@
1
1
  import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
2
2
  import type { GroupDefinition } from '../client/GroupNode.ts';
3
3
  import type { SubtreeDefinition } from '../client/SubtreeNode.ts';
4
+ import type { LeafNodeDefinition } from '../model/LeafNodeDefinition.ts';
4
5
  import type { SubtreeDefinition as ModelSubtreeDefinition } from '../model/SubtreeDefinition.ts';
6
+ import { NoteNodeDefinition } from '../parse/NoteNodeDefinition.ts';
5
7
  import { Group } from './Group.ts';
6
- import { RepeatRange } from './RepeatRange.ts';
8
+ import type { GeneralChildNode, GeneralParentNode } from './hierarchy.ts';
9
+ import { ModelValue, type ModelValueDefinition } from './ModelValue.ts';
10
+ import { Note } from './Note.ts';
11
+ import { RepeatRangeControlled } from './repeat/RepeatRangeControlled.ts';
12
+ import { RepeatRangeUncontrolled } from './repeat/RepeatRangeUncontrolled.ts';
7
13
  import type { SelectFieldDefinition } from './SelectField.ts';
8
14
  import { SelectField } from './SelectField.ts';
9
15
  import type { StringFieldDefinition } from './StringField.ts';
10
16
  import { StringField } from './StringField.ts';
11
17
  import { Subtree } from './Subtree.ts';
12
- import type { GeneralChildNode, GeneralParentNode } from './hierarchy.ts';
13
18
 
14
19
  const isSubtreeDefinition = (
15
20
  definition: ModelSubtreeDefinition
@@ -17,6 +22,22 @@ const isSubtreeDefinition = (
17
22
  return definition.bodyElement == null;
18
23
  };
19
24
 
25
+ type ControlNodeDefinition = SelectFieldDefinition | StringFieldDefinition;
26
+
27
+ type AnyLeafNodeDefinition = ControlNodeDefinition | ModelValueDefinition;
28
+
29
+ const isModelValueDefinition = (
30
+ definition: LeafNodeDefinition
31
+ ): definition is ModelValueDefinition => {
32
+ return definition.bodyElement == null;
33
+ };
34
+
35
+ const isStringFieldDefinition = (
36
+ definition: ControlNodeDefinition
37
+ ): definition is StringFieldDefinition => {
38
+ return definition.bodyElement.type === 'input';
39
+ };
40
+
20
41
  export const buildChildren = (parent: GeneralParentNode): GeneralChildNode[] => {
21
42
  const { children } = parent.definition;
22
43
 
@@ -34,24 +55,30 @@ export const buildChildren = (parent: GeneralParentNode): GeneralChildNode[] =>
34
55
  }
35
56
 
36
57
  case 'repeat-range': {
37
- return new RepeatRange(parent, child);
58
+ if (child.isControlled()) {
59
+ return new RepeatRangeControlled(parent, child);
60
+ }
61
+
62
+ return new RepeatRangeUncontrolled(parent, child);
38
63
  }
39
64
 
40
- case 'value-node': {
41
- // TODO: this sort of awkwardness might go away if we embrace a
42
- // proliferation of node types throughout.
43
- switch (child.bodyElement?.type) {
44
- case 'select':
45
- case 'select1':
46
- return new SelectField(parent, child as SelectFieldDefinition);
65
+ case 'leaf-node': {
66
+ if (child instanceof NoteNodeDefinition) {
67
+ return new Note(parent, child);
68
+ }
47
69
 
48
- case 'input':
49
- case undefined:
50
- return new StringField(parent, child as StringFieldDefinition);
70
+ // More specific type helps with narrowing below
71
+ const leafChild: AnyLeafNodeDefinition = child;
51
72
 
52
- default:
53
- throw new UnreachableError(child.bodyElement);
73
+ if (isModelValueDefinition(leafChild)) {
74
+ return new ModelValue(parent, leafChild);
54
75
  }
76
+
77
+ if (isStringFieldDefinition(leafChild)) {
78
+ return new StringField(parent, leafChild);
79
+ }
80
+
81
+ return new SelectField(parent, leafChild);
55
82
  }
56
83
 
57
84
  default: {
@@ -1,11 +1,16 @@
1
1
  import type { Group } from './Group.ts';
2
- import type { RepeatInstance } from './RepeatInstance.ts';
3
- import type { RepeatRange } from './RepeatRange.ts';
2
+ import type { ModelValue } from './ModelValue.ts';
3
+ import type { Note } from './Note.ts';
4
+ import type { RepeatInstance } from './repeat/RepeatInstance.ts';
5
+ import type { RepeatRangeControlled } from './repeat/RepeatRangeControlled.ts';
6
+ import type { RepeatRangeUncontrolled } from './repeat/RepeatRangeUncontrolled.ts';
4
7
  import type { Root } from './Root.ts';
5
8
  import type { SelectField } from './SelectField.ts';
6
9
  import type { StringField } from './StringField.ts';
7
10
  import type { Subtree } from './Subtree.ts';
8
11
 
12
+ export type RepeatRange = RepeatRangeControlled | RepeatRangeUncontrolled;
13
+
9
14
  // prettier-ignore
10
15
  export type AnyNode =
11
16
  // eslint-disable-next-line @typescript-eslint/sort-type-constituents
@@ -14,6 +19,8 @@ export type AnyNode =
14
19
  | Subtree
15
20
  | RepeatRange
16
21
  | RepeatInstance
22
+ | Note
23
+ | ModelValue
17
24
  | StringField
18
25
  | SelectField;
19
26
 
@@ -41,6 +48,8 @@ export type AnyChildNode =
41
48
  | Subtree
42
49
  | RepeatRange
43
50
  | RepeatInstance
51
+ | ModelValue
52
+ | Note
44
53
  | StringField
45
54
  | SelectField;
46
55
 
@@ -50,5 +59,15 @@ export type GeneralChildNode =
50
59
  | Group
51
60
  | Subtree
52
61
  | RepeatRange
62
+ | ModelValue
63
+ | Note
64
+ | StringField
65
+ | SelectField;
66
+
67
+ // prettier-ignore
68
+ export type AnyValueNode =
69
+ // eslint-disable-next-line @typescript-eslint/sort-type-constituents
70
+ | ModelValue
71
+ | Note
53
72
  | StringField
54
73
  | SelectField;
@@ -0,0 +1,23 @@
1
+ import type { BindComputation } from '../../model/BindComputation.ts';
2
+ import type { MessageDefinition } from '../../parse/text/MessageDefinition.ts';
3
+ import type { EvaluationContext } from './EvaluationContext.ts';
4
+ import type { SubscribableDependency } from './SubscribableDependency.ts';
5
+
6
+ interface ValidationContextDefinitionBind {
7
+ readonly constraint: BindComputation<'constraint'>;
8
+ readonly constraintMsg: MessageDefinition<'constraintMsg'> | null;
9
+ readonly required: BindComputation<'required'>;
10
+ readonly requiredMsg: MessageDefinition<'requiredMsg'> | null;
11
+ }
12
+
13
+ interface ValidationContextDefinition {
14
+ readonly bind: ValidationContextDefinitionBind;
15
+ }
16
+
17
+ export interface ValidationContext extends EvaluationContext, SubscribableDependency {
18
+ readonly definition: ValidationContextDefinition;
19
+
20
+ isRelevant(): boolean;
21
+ isRequired(): boolean;
22
+ isBlank(): boolean;
23
+ }