@getodk/xforms-engine 0.1.1 → 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 (228) hide show
  1. package/dist/body/BodyDefinition.d.ts +24 -7
  2. package/dist/body/BodyElementDefinition.d.ts +4 -3
  3. package/dist/body/RepeatElementDefinition.d.ts +19 -0
  4. package/dist/body/appearance/inputAppearanceParser.d.ts +4 -0
  5. package/dist/body/appearance/selectAppearanceParser.d.ts +4 -0
  6. package/dist/body/appearance/structureElementAppearanceParser.d.ts +4 -0
  7. package/dist/body/control/ControlDefinition.d.ts +4 -2
  8. package/dist/body/control/InputDefinition.d.ts +5 -0
  9. package/dist/body/control/select/ItemDefinition.d.ts +2 -2
  10. package/dist/body/control/select/ItemsetDefinition.d.ts +5 -4
  11. package/dist/body/control/select/SelectDefinition.d.ts +11 -1
  12. package/dist/body/group/BaseGroupDefinition.d.ts +4 -9
  13. package/dist/body/group/PresentationGroupDefinition.d.ts +1 -1
  14. package/dist/client/BaseNode.d.ts +74 -3
  15. package/dist/client/GroupNode.d.ts +7 -2
  16. package/dist/client/ModelValueNode.d.ts +37 -0
  17. package/dist/client/NodeAppearances.d.ts +15 -0
  18. package/dist/client/NoteNode.d.ts +53 -0
  19. package/dist/client/RootNode.d.ts +21 -0
  20. package/dist/client/SelectNode.d.ts +8 -3
  21. package/dist/client/StringNode.d.ts +8 -3
  22. package/dist/client/SubtreeNode.d.ts +3 -0
  23. package/dist/client/TextRange.d.ts +85 -2
  24. package/dist/client/constants.d.ts +9 -0
  25. package/dist/client/hierarchy.d.ts +14 -9
  26. package/dist/client/node-types.d.ts +2 -1
  27. package/dist/client/{RepeatRangeNode.d.ts → repeat/BaseRepeatRangeNode.d.ts} +19 -15
  28. package/dist/client/{RepeatInstanceNode.d.ts → repeat/RepeatInstanceNode.d.ts} +11 -7
  29. package/dist/client/repeat/RepeatRangeControlledNode.d.ts +19 -0
  30. package/dist/client/repeat/RepeatRangeUncontrolledNode.d.ts +20 -0
  31. package/dist/client/validation.d.ts +163 -0
  32. package/dist/expression/DependentExpression.d.ts +12 -8
  33. package/dist/index.d.ts +9 -4
  34. package/dist/index.js +3173 -960
  35. package/dist/index.js.map +1 -1
  36. package/dist/instance/Group.d.ts +6 -4
  37. package/dist/instance/ModelValue.d.ts +40 -0
  38. package/dist/instance/Note.d.ts +42 -0
  39. package/dist/instance/Root.d.ts +10 -23
  40. package/dist/instance/SelectField.d.ts +12 -6
  41. package/dist/instance/StringField.d.ts +13 -7
  42. package/dist/instance/Subtree.d.ts +3 -1
  43. package/dist/instance/abstract/DescendantNode.d.ts +16 -9
  44. package/dist/instance/abstract/InstanceNode.d.ts +28 -29
  45. package/dist/instance/hierarchy.d.ts +10 -5
  46. package/dist/instance/internal-api/EvaluationContext.d.ts +5 -4
  47. package/dist/instance/internal-api/ValidationContext.d.ts +21 -0
  48. package/dist/instance/internal-api/ValueContext.d.ts +2 -2
  49. package/dist/instance/repeat/BaseRepeatRange.d.ts +160 -0
  50. package/dist/instance/{RepeatInstance.d.ts → repeat/RepeatInstance.d.ts} +38 -13
  51. package/dist/instance/repeat/RepeatRangeControlled.d.ts +16 -0
  52. package/dist/instance/repeat/RepeatRangeUncontrolled.d.ts +35 -0
  53. package/dist/instance/text/TextRange.d.ts +4 -4
  54. package/dist/lib/TokenListParser.d.ts +84 -0
  55. package/dist/lib/dom/query.d.ts +5 -0
  56. package/dist/lib/reactivity/createComputedExpression.d.ts +6 -1
  57. package/dist/lib/reactivity/createNoteReadonlyThunk.d.ts +5 -0
  58. package/dist/lib/reactivity/materializeCurrentStateChildren.d.ts +2 -1
  59. package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +1 -1
  60. package/dist/lib/reactivity/node-state/createSpecifiedState.d.ts +1 -1
  61. package/dist/lib/reactivity/text/createFieldHint.d.ts +3 -3
  62. package/dist/lib/reactivity/text/createNodeLabel.d.ts +2 -2
  63. package/dist/lib/reactivity/text/createNoteText.d.ts +25 -0
  64. package/dist/lib/reactivity/text/createTextRange.d.ts +5 -7
  65. package/dist/lib/reactivity/validation/createAggregatedViolations.d.ts +9 -0
  66. package/dist/lib/reactivity/validation/createValidation.d.ts +18 -0
  67. package/dist/model/BindDefinition.d.ts +4 -2
  68. package/dist/model/BindElement.d.ts +1 -0
  69. package/dist/model/DescendentNodeDefinition.d.ts +1 -2
  70. package/dist/model/{ValueNodeDefinition.d.ts → LeafNodeDefinition.d.ts} +3 -4
  71. package/dist/model/NodeDefinition.d.ts +16 -16
  72. package/dist/model/RepeatInstanceDefinition.d.ts +5 -6
  73. package/dist/model/RepeatRangeDefinition.d.ts +30 -0
  74. package/dist/model/RepeatTemplateDefinition.d.ts +6 -7
  75. package/dist/model/RootDefinition.d.ts +3 -1
  76. package/dist/model/SubtreeDefinition.d.ts +2 -2
  77. package/dist/parse/NoteNodeDefinition.d.ts +31 -0
  78. package/dist/parse/expression/RepeatCountControlExpression.d.ts +19 -0
  79. package/dist/parse/text/HintDefinition.d.ts +9 -0
  80. package/dist/parse/text/ItemLabelDefinition.d.ts +9 -0
  81. package/dist/parse/text/ItemsetLabelDefinition.d.ts +13 -0
  82. package/dist/parse/text/LabelDefinition.d.ts +15 -0
  83. package/dist/parse/text/MessageDefinition.d.ts +15 -0
  84. package/dist/parse/text/OutputChunkDefinition.d.ts +8 -0
  85. package/dist/parse/text/ReferenceChunkDefinition.d.ts +8 -0
  86. package/dist/parse/text/StaticTextChunkDefinition.d.ts +10 -0
  87. package/dist/parse/text/TranslationChunkDefinition.d.ts +9 -0
  88. package/dist/parse/text/abstract/TextChunkDefinition.d.ts +18 -0
  89. package/dist/parse/text/abstract/TextElementDefinition.d.ts +23 -0
  90. package/dist/parse/text/abstract/TextRangeDefinition.d.ts +35 -0
  91. package/dist/parse/xpath/dependency-analysis.d.ts +40 -0
  92. package/dist/parse/xpath/path-resolution.d.ts +70 -0
  93. package/dist/parse/xpath/predicate-analysis.d.ts +30 -0
  94. package/dist/parse/xpath/reference-parsing.d.ts +18 -0
  95. package/dist/parse/xpath/semantic-analysis.d.ts +98 -0
  96. package/dist/parse/xpath/syntax-traversal.d.ts +69 -0
  97. package/dist/solid.js +3174 -961
  98. package/dist/solid.js.map +1 -1
  99. package/package.json +14 -15
  100. package/src/XFormDOM.ts +81 -8
  101. package/src/body/BodyDefinition.ts +38 -23
  102. package/src/body/BodyElementDefinition.ts +4 -3
  103. package/src/body/RepeatElementDefinition.ts +58 -0
  104. package/src/body/appearance/inputAppearanceParser.ts +39 -0
  105. package/src/body/appearance/selectAppearanceParser.ts +38 -0
  106. package/src/body/appearance/structureElementAppearanceParser.ts +7 -0
  107. package/src/body/control/ControlDefinition.ts +8 -3
  108. package/src/body/control/InputDefinition.ts +13 -0
  109. package/src/body/control/select/ItemDefinition.ts +3 -3
  110. package/src/body/control/select/ItemsetDefinition.ts +29 -12
  111. package/src/body/control/select/ItemsetNodesetExpression.ts +1 -1
  112. package/src/body/control/select/SelectDefinition.ts +14 -5
  113. package/src/body/group/BaseGroupDefinition.ts +14 -51
  114. package/src/body/group/PresentationGroupDefinition.ts +1 -1
  115. package/src/client/BaseNode.ts +82 -8
  116. package/src/client/GroupNode.ts +8 -2
  117. package/src/client/ModelValueNode.ts +40 -0
  118. package/src/client/NodeAppearances.ts +22 -0
  119. package/src/client/NoteNode.ts +74 -0
  120. package/src/client/README.md +1 -0
  121. package/src/client/RootNode.ts +24 -0
  122. package/src/client/SelectNode.ts +9 -3
  123. package/src/client/StringNode.ts +9 -3
  124. package/src/client/SubtreeNode.ts +3 -0
  125. package/src/client/TextRange.ts +99 -2
  126. package/src/client/constants.ts +10 -0
  127. package/src/client/hierarchy.ts +30 -14
  128. package/src/client/node-types.ts +8 -1
  129. package/src/client/{RepeatRangeNode.ts → repeat/BaseRepeatRangeNode.ts} +20 -17
  130. package/src/client/{RepeatInstanceNode.ts → repeat/RepeatInstanceNode.ts} +13 -7
  131. package/src/client/repeat/RepeatRangeControlledNode.ts +20 -0
  132. package/src/client/repeat/RepeatRangeUncontrolledNode.ts +21 -0
  133. package/src/client/validation.ts +199 -0
  134. package/src/expression/DependentExpression.ts +45 -27
  135. package/src/index.ts +15 -8
  136. package/src/instance/Group.ts +24 -13
  137. package/src/instance/ModelValue.ts +104 -0
  138. package/src/instance/Note.ts +142 -0
  139. package/src/instance/Root.ts +29 -67
  140. package/src/instance/SelectField.ts +35 -13
  141. package/src/instance/StringField.ts +40 -13
  142. package/src/instance/Subtree.ts +19 -10
  143. package/src/instance/abstract/DescendantNode.ts +50 -49
  144. package/src/instance/abstract/InstanceNode.ts +89 -92
  145. package/src/instance/children.ts +47 -10
  146. package/src/instance/hierarchy.ts +21 -2
  147. package/src/instance/index.ts +1 -1
  148. package/src/instance/internal-api/EvaluationContext.ts +5 -6
  149. package/src/instance/internal-api/ValidationContext.ts +23 -0
  150. package/src/instance/internal-api/ValueContext.ts +2 -2
  151. package/src/instance/repeat/BaseRepeatRange.ts +347 -0
  152. package/src/instance/{RepeatInstance.ts → repeat/RepeatInstance.ts} +85 -36
  153. package/src/instance/repeat/RepeatRangeControlled.ts +82 -0
  154. package/src/instance/repeat/RepeatRangeUncontrolled.ts +67 -0
  155. package/src/instance/text/TextRange.ts +10 -4
  156. package/src/lib/TokenListParser.ts +156 -0
  157. package/src/lib/dom/query.ts +13 -0
  158. package/src/lib/reactivity/createChildrenState.ts +51 -6
  159. package/src/lib/reactivity/createComputedExpression.ts +23 -25
  160. package/src/lib/reactivity/createNoteReadonlyThunk.ts +33 -0
  161. package/src/lib/reactivity/createSelectItems.ts +25 -20
  162. package/src/lib/reactivity/createValueState.ts +6 -6
  163. package/src/lib/reactivity/materializeCurrentStateChildren.ts +3 -1
  164. package/src/lib/reactivity/node-state/createSharedNodeState.ts +1 -1
  165. package/src/lib/reactivity/text/createFieldHint.ts +9 -7
  166. package/src/lib/reactivity/text/createNodeLabel.ts +7 -5
  167. package/src/lib/reactivity/text/createNoteText.ts +72 -0
  168. package/src/lib/reactivity/text/createTextRange.ts +17 -90
  169. package/src/lib/reactivity/validation/createAggregatedViolations.ts +70 -0
  170. package/src/lib/reactivity/validation/createValidation.ts +196 -0
  171. package/src/model/BindComputation.ts +0 -4
  172. package/src/model/BindDefinition.ts +8 -6
  173. package/src/model/BindElement.ts +1 -0
  174. package/src/model/DescendentNodeDefinition.ts +1 -2
  175. package/src/model/{ValueNodeDefinition.ts → LeafNodeDefinition.ts} +5 -6
  176. package/src/model/ModelBindMap.ts +4 -0
  177. package/src/model/ModelDefinition.ts +1 -1
  178. package/src/model/NodeDefinition.ts +21 -21
  179. package/src/model/RepeatInstanceDefinition.ts +8 -13
  180. package/src/model/RepeatRangeDefinition.ts +94 -0
  181. package/src/model/RepeatTemplateDefinition.ts +10 -15
  182. package/src/model/RootDefinition.ts +12 -14
  183. package/src/model/SubtreeDefinition.ts +3 -3
  184. package/src/parse/NoteNodeDefinition.ts +70 -0
  185. package/src/parse/TODO.md +3 -0
  186. package/src/parse/expression/RepeatCountControlExpression.ts +44 -0
  187. package/src/parse/text/HintDefinition.ts +25 -0
  188. package/src/parse/text/ItemLabelDefinition.ts +25 -0
  189. package/src/parse/text/ItemsetLabelDefinition.ts +44 -0
  190. package/src/parse/text/LabelDefinition.ts +61 -0
  191. package/src/parse/text/MessageDefinition.ts +49 -0
  192. package/src/parse/text/OutputChunkDefinition.ts +25 -0
  193. package/src/parse/text/ReferenceChunkDefinition.ts +14 -0
  194. package/src/parse/text/StaticTextChunkDefinition.ts +19 -0
  195. package/src/parse/text/TranslationChunkDefinition.ts +38 -0
  196. package/src/parse/text/abstract/TextChunkDefinition.ts +38 -0
  197. package/src/parse/text/abstract/TextElementDefinition.ts +71 -0
  198. package/src/parse/text/abstract/TextRangeDefinition.ts +70 -0
  199. package/src/parse/xpath/dependency-analysis.ts +105 -0
  200. package/src/parse/xpath/path-resolution.ts +475 -0
  201. package/src/parse/xpath/predicate-analysis.ts +61 -0
  202. package/src/parse/xpath/reference-parsing.ts +90 -0
  203. package/src/parse/xpath/semantic-analysis.ts +466 -0
  204. package/src/parse/xpath/syntax-traversal.ts +129 -0
  205. package/dist/body/RepeatDefinition.d.ts +0 -16
  206. package/dist/body/group/RepeatGroupDefinition.d.ts +0 -13
  207. package/dist/body/text/HintDefinition.d.ts +0 -11
  208. package/dist/body/text/LabelDefinition.d.ts +0 -20
  209. package/dist/body/text/TextElementDefinition.d.ts +0 -33
  210. package/dist/body/text/TextElementOutputPart.d.ts +0 -13
  211. package/dist/body/text/TextElementPart.d.ts +0 -13
  212. package/dist/body/text/TextElementReferencePart.d.ts +0 -7
  213. package/dist/body/text/TextElementStaticPart.d.ts +0 -7
  214. package/dist/instance/RepeatRange.d.ts +0 -80
  215. package/dist/lib/xpath/analysis.d.ts +0 -23
  216. package/dist/model/RepeatSequenceDefinition.d.ts +0 -20
  217. package/src/body/RepeatDefinition.ts +0 -54
  218. package/src/body/group/RepeatGroupDefinition.ts +0 -91
  219. package/src/body/text/HintDefinition.ts +0 -26
  220. package/src/body/text/LabelDefinition.ts +0 -54
  221. package/src/body/text/TextElementDefinition.ts +0 -97
  222. package/src/body/text/TextElementOutputPart.ts +0 -27
  223. package/src/body/text/TextElementPart.ts +0 -31
  224. package/src/body/text/TextElementReferencePart.ts +0 -21
  225. package/src/body/text/TextElementStaticPart.ts +0 -26
  226. package/src/instance/RepeatRange.ts +0 -214
  227. package/src/lib/xpath/analysis.ts +0 -241
  228. package/src/model/RepeatSequenceDefinition.ts +0 -53
@@ -1,10 +1,20 @@
1
1
  import type { XFormsXPathEvaluator } from '@getodk/xpath';
2
- import { getNodesetDependencies, isItextFunctionCalled } from '../lib/xpath/analysis.ts';
2
+ import { resolveDependencyNodesets } from '../parse/xpath/dependency-analysis.ts';
3
+ import type {
4
+ ConstantExpression,
5
+ ConstantTruthyExpression,
6
+ } from '../parse/xpath/semantic-analysis.ts';
7
+ import {
8
+ isConstantExpression,
9
+ isConstantTruthyExpression,
10
+ isTranslationExpression,
11
+ } from '../parse/xpath/semantic-analysis.ts';
3
12
  import type { DependencyContext } from './DependencyContext.ts';
4
13
 
5
14
  const evaluatorMethodsByResultType = {
6
15
  boolean: 'evaluateBoolean',
7
16
  nodes: 'evaluateNodes',
17
+ number: 'evaluateNumber',
8
18
  string: 'evaluateString',
9
19
  } as const;
10
20
 
@@ -20,11 +30,6 @@ export type DependentExpressionResult<Type extends DependentExpressionResultType
20
30
  >;
21
31
 
22
32
  interface SemanticDependencyOptions {
23
- /**
24
- * @default false
25
- */
26
- readonly parentContext?: boolean | undefined;
27
-
28
33
  /**
29
34
  * @default false
30
35
  */
@@ -37,18 +42,24 @@ interface DependentExpressionOptions {
37
42
  */
38
43
  readonly ignoreContextReference?: boolean;
39
44
 
40
- /**
41
- * @default true
42
- */
43
- readonly ignoreNullExpressions?: boolean;
44
-
45
45
  readonly semanticDependencies?: SemanticDependencyOptions;
46
46
  }
47
47
 
48
+ export interface ConstantDependentExpression<Type extends DependentExpressionResultType>
49
+ extends DependentExpression<Type> {
50
+ readonly expression: ConstantExpression;
51
+ }
52
+
53
+ export interface ConstantTruthyDependentExpression extends ConstantDependentExpression<'boolean'> {
54
+ readonly expression: ConstantTruthyExpression;
55
+ }
56
+
48
57
  export class DependentExpression<Type extends DependentExpressionResultType> {
49
58
  readonly dependencyReferences: ReadonlySet<string> = new Set();
50
59
  readonly isTranslated: boolean = false;
51
60
  readonly evaluatorMethod: DependentExpressionEvaluatorMethod<Type>;
61
+ readonly constantExpression: ConstantExpression | null;
62
+ readonly constantTruthyExpression: ConstantTruthyExpression | null;
52
63
 
53
64
  constructor(
54
65
  context: DependencyContext,
@@ -56,34 +67,33 @@ export class DependentExpression<Type extends DependentExpressionResultType> {
56
67
  readonly expression: string,
57
68
  options: DependentExpressionOptions = {}
58
69
  ) {
70
+ if (resultType === 'boolean' && isConstantTruthyExpression(expression)) {
71
+ this.constantTruthyExpression = expression;
72
+ this.constantExpression = expression;
73
+ } else if (isConstantExpression(expression)) {
74
+ this.constantTruthyExpression = null;
75
+ this.constantExpression = expression;
76
+ } else {
77
+ this.constantTruthyExpression = null;
78
+ this.constantExpression = null;
79
+ }
80
+
59
81
  this.evaluatorMethod = evaluatorMethodsByResultType[resultType];
60
82
 
61
83
  const {
62
84
  ignoreContextReference = false,
63
- ignoreNullExpressions = true,
64
85
  semanticDependencies = {
65
- parentContext: false,
66
86
  translations: false,
67
87
  },
68
88
  } = options;
69
89
 
70
- const dependencyReferences = new Set<string>(
71
- getNodesetDependencies(expression, {
72
- contextReference: context.reference,
73
- ignoreContextReference,
74
- ignoreNullExpressions,
90
+ this.dependencyReferences = new Set(
91
+ resolveDependencyNodesets(context.reference, expression, {
92
+ ignoreReferenceToContextPath: ignoreContextReference,
75
93
  })
76
94
  );
77
95
 
78
- const parentDependency = semanticDependencies.parentContext ? context.parentReference : null;
79
-
80
- if (parentDependency != null) {
81
- dependencyReferences.add(parentDependency);
82
- }
83
-
84
- this.dependencyReferences = dependencyReferences;
85
-
86
- const isTranslated = semanticDependencies.translations && isItextFunctionCalled(expression);
96
+ const isTranslated = semanticDependencies.translations && isTranslationExpression(expression);
87
97
 
88
98
  if (isTranslated) {
89
99
  this.isTranslated = true;
@@ -93,6 +103,14 @@ export class DependentExpression<Type extends DependentExpressionResultType> {
93
103
  context.registerDependentExpression(this);
94
104
  }
95
105
 
106
+ isConstantExpression(): this is ConstantDependentExpression<Type> {
107
+ return this.constantExpression != null;
108
+ }
109
+
110
+ isConstantTruthyExpression(): this is ConstantTruthyDependentExpression {
111
+ return this.resultType === 'boolean' && this.constantTruthyExpression != null;
112
+ }
113
+
96
114
  toString(): string | null {
97
115
  return this.expression;
98
116
  }
package/src/index.ts CHANGED
@@ -3,26 +3,33 @@ import { initializeForm as engine__initializeForm } from './instance/index.ts';
3
3
 
4
4
  export const initializeForm: InitializeForm = engine__initializeForm;
5
5
 
6
+ export * as constants from './client/constants.ts';
6
7
  export type * from './client/EngineConfig.ts';
7
8
  export type * from './client/FormLanguage.ts';
8
9
  export type * from './client/GroupNode.ts';
9
- export type * from './client/OpaqueReactiveObjectFactory.ts';
10
- export type * from './client/RepeatInstanceNode.ts';
11
- export type * from './client/RepeatRangeNode.ts';
12
- export type * from './client/RootNode.ts';
13
- export type * from './client/SelectNode.ts';
14
- export type * from './client/StringNode.ts';
15
- export type * from './client/SubtreeNode.ts';
16
- export type * from './client/TextRange.ts';
17
10
  export type {
18
11
  AnyChildNode,
12
+ AnyControlNode,
19
13
  AnyLeafNode,
20
14
  AnyNode,
21
15
  AnyParentNode,
22
16
  GeneralChildNode,
23
17
  GeneralParentNode,
18
+ RepeatRangeNode,
24
19
  } from './client/hierarchy.ts';
25
20
  export type * from './client/index.ts';
21
+ export type * from './client/ModelValueNode.ts';
22
+ export type * from './client/NoteNode.ts';
23
+ export type * from './client/OpaqueReactiveObjectFactory.ts';
24
+ export type * from './client/repeat/RepeatInstanceNode.ts';
25
+ export type * from './client/repeat/RepeatRangeControlledNode.ts';
26
+ export type * from './client/repeat/RepeatRangeUncontrolledNode.ts';
27
+ export type * from './client/RootNode.ts';
28
+ export type * from './client/SelectNode.ts';
29
+ export type * from './client/StringNode.ts';
30
+ export type * from './client/SubtreeNode.ts';
31
+ export type * from './client/TextRange.ts';
32
+ export type * from './client/validation.ts';
26
33
 
27
34
  // TODO: notwithstanding potential conflicts with parallel work on `web-forms`
28
35
  // (former `ui-vue`), these are the last remaining references **outside of
@@ -1,6 +1,7 @@
1
1
  import type { Accessor } from 'solid-js';
2
- import type { GroupDefinition, GroupNode } from '../client/GroupNode.ts';
3
- import type { TextRange } from '../index.ts';
2
+ import type { GroupDefinition, GroupNode, GroupNodeAppearances } from '../client/GroupNode.ts';
3
+ import type { TextRange } from '../client/TextRange.ts';
4
+ import type { AncestorNodeValidationState } from '../client/validation.ts';
4
5
  import type { ChildrenState } from '../lib/reactivity/createChildrenState.ts';
5
6
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
6
7
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -10,6 +11,7 @@ import type { EngineState } from '../lib/reactivity/node-state/createEngineState
10
11
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
11
12
  import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
12
13
  import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
14
+ import { createAggregatedViolations } from '../lib/reactivity/validation/createAggregatedViolations.ts';
13
15
  import type { DescendantNodeSharedStateSpec } from './abstract/DescendantNode.ts';
14
16
  import { DescendantNode } from './abstract/DescendantNode.ts';
15
17
  import { buildChildren } from './children.ts';
@@ -38,21 +40,31 @@ export class Group
38
40
  protected override engineState: EngineState<GroupStateSpec>;
39
41
 
40
42
  // GroupNode
41
- readonly currentState: MaterializedChildren<CurrentState<GroupStateSpec>, GeneralChildNode>;
42
-
43
43
  readonly nodeType = 'group';
44
+ readonly appearances: GroupNodeAppearances;
45
+ readonly currentState: MaterializedChildren<CurrentState<GroupStateSpec>, GeneralChildNode>;
46
+ readonly validationState: AncestorNodeValidationState;
44
47
 
45
48
  constructor(parent: GeneralParentNode, definition: GroupDefinition) {
46
49
  super(parent, definition);
47
50
 
51
+ this.appearances = definition.bodyElement.appearances;
52
+
48
53
  const childrenState = createChildrenState<Group, GeneralChildNode>(this);
49
54
 
50
55
  this.childrenState = childrenState;
51
56
 
57
+ const sharedStateOptions = {
58
+ clientStateFactory: this.engineConfig.stateFactory,
59
+ };
60
+
52
61
  const state = createSharedNodeState(
53
62
  this.scope,
54
63
  {
55
- ...this.buildSharedStateSpec(parent, definition),
64
+ reference: this.contextReference,
65
+ readonly: this.isReadonly,
66
+ relevant: this.isRelevant,
67
+ required: this.isRequired,
56
68
 
57
69
  label: createNodeLabel(this, definition),
58
70
  hint: null,
@@ -60,20 +72,19 @@ export class Group
60
72
  valueOptions: null,
61
73
  value: null,
62
74
  },
63
- {
64
- clientStateFactory: this.engineConfig.stateFactory,
65
- }
75
+ sharedStateOptions
66
76
  );
67
77
 
68
78
  this.state = state;
69
79
  this.engineState = state.engineState;
70
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
80
+ this.currentState = materializeCurrentStateChildren(
81
+ this.scope,
82
+ state.currentState,
83
+ childrenState
84
+ );
71
85
 
72
86
  childrenState.setChildren(buildChildren(this));
73
- }
74
-
75
- protected computeReference(parent: GeneralParentNode): string {
76
- return this.computeChildStepReference(parent);
87
+ this.validationState = createAggregatedViolations(this, sharedStateOptions);
77
88
  }
78
89
 
79
90
  getChildren(): readonly GeneralChildNode[] {
@@ -0,0 +1,104 @@
1
+ import { identity } from '@getodk/common/lib/identity.ts';
2
+ import type { ModelValueNode } from '../client/ModelValueNode.ts';
3
+ import type { AnyViolation, LeafNodeValidationState } from '../client/validation.ts';
4
+ import { createValueState } from '../lib/reactivity/createValueState.ts';
5
+ import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
6
+ import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
7
+ import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
8
+ import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
9
+ import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
10
+ import type { SharedValidationState } from '../lib/reactivity/validation/createValidation.ts';
11
+ import { createValidationState } from '../lib/reactivity/validation/createValidation.ts';
12
+ import type { LeafNodeDefinition } from '../model/LeafNodeDefinition.ts';
13
+ import type { DescendantNodeStateSpec } from './abstract/DescendantNode.ts';
14
+ import { DescendantNode } from './abstract/DescendantNode.ts';
15
+ import type { GeneralParentNode } from './hierarchy.ts';
16
+ import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
17
+ import type { SubscribableDependency } from './internal-api/SubscribableDependency.ts';
18
+ import type { ValidationContext } from './internal-api/ValidationContext.ts';
19
+ import type { ValueContext } from './internal-api/ValueContext.ts';
20
+
21
+ export interface ModelValueDefinition extends LeafNodeDefinition {
22
+ readonly bodyElement: null;
23
+ }
24
+
25
+ interface ModelValueStateSpec extends DescendantNodeStateSpec<string> {
26
+ readonly label: null;
27
+ readonly hint: null;
28
+ readonly children: null;
29
+ readonly value: SimpleAtomicState<string>;
30
+ readonly valueOptions: null;
31
+ }
32
+
33
+ export class ModelValue
34
+ extends DescendantNode<ModelValueDefinition, ModelValueStateSpec, null>
35
+ implements
36
+ ModelValueNode,
37
+ EvaluationContext,
38
+ SubscribableDependency,
39
+ ValidationContext,
40
+ ValueContext<string>
41
+ {
42
+ private readonly validation: SharedValidationState;
43
+ protected readonly state: SharedNodeState<ModelValueStateSpec>;
44
+
45
+ // InstanceNode
46
+ protected engineState: EngineState<ModelValueStateSpec>;
47
+
48
+ // ModelValueNode
49
+ readonly nodeType = 'model-value';
50
+ readonly appearances = null;
51
+ readonly currentState: CurrentState<ModelValueStateSpec>;
52
+
53
+ get validationState(): LeafNodeValidationState {
54
+ return this.validation.currentState;
55
+ }
56
+
57
+ // ValueContext
58
+ readonly encodeValue = identity<string>;
59
+ readonly decodeValue = identity<string>;
60
+
61
+ constructor(parent: GeneralParentNode, definition: ModelValueDefinition) {
62
+ super(parent, definition);
63
+
64
+ const sharedStateOptions = {
65
+ clientStateFactory: this.engineConfig.stateFactory,
66
+ };
67
+
68
+ const state = createSharedNodeState(
69
+ this.scope,
70
+ {
71
+ reference: this.contextReference,
72
+ readonly: this.isReadonly,
73
+ relevant: this.isRelevant,
74
+ required: this.isRequired,
75
+
76
+ label: null,
77
+ hint: null,
78
+ children: null,
79
+ valueOptions: null,
80
+ value: createValueState(this),
81
+ },
82
+ sharedStateOptions
83
+ );
84
+
85
+ this.state = state;
86
+ this.engineState = state.engineState;
87
+ this.currentState = state.currentState;
88
+ this.validation = createValidationState(this, sharedStateOptions);
89
+ }
90
+
91
+ getViolation(): AnyViolation | null {
92
+ return this.validation.engineState.violation;
93
+ }
94
+
95
+ // ValidationContext
96
+ isBlank(): boolean {
97
+ return this.engineState.value === '';
98
+ }
99
+
100
+ // InstanceNode
101
+ getChildren(): readonly [] {
102
+ return [];
103
+ }
104
+ }
@@ -0,0 +1,142 @@
1
+ import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
2
+ import { identity } from '@getodk/common/lib/identity.ts';
3
+ import type { Accessor } from 'solid-js';
4
+ import type { NoteNode, NoteNodeAppearances } from '../client/NoteNode.ts';
5
+ import type { TextRange } from '../client/TextRange.ts';
6
+ import type { AnyViolation, LeafNodeValidationState } from '../client/validation.ts';
7
+ import { createNoteReadonlyThunk } from '../lib/reactivity/createNoteReadonlyThunk.ts';
8
+ import { createValueState } from '../lib/reactivity/createValueState.ts';
9
+ import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
10
+ import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
11
+ import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
12
+ import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
13
+ import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
14
+ import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
15
+ import { createNoteText, type ComputedNoteText } from '../lib/reactivity/text/createNoteText.ts';
16
+ import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
17
+ import type { SharedValidationState } from '../lib/reactivity/validation/createValidation.ts';
18
+ import { createValidationState } from '../lib/reactivity/validation/createValidation.ts';
19
+ import type { NoteNodeDefinition } from '../parse/NoteNodeDefinition.ts';
20
+ import type { DescendantNodeStateSpec } from './abstract/DescendantNode.ts';
21
+ import { DescendantNode } from './abstract/DescendantNode.ts';
22
+ import type { GeneralParentNode } from './hierarchy.ts';
23
+ import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
24
+ import type { SubscribableDependency } from './internal-api/SubscribableDependency.ts';
25
+ import type { ValidationContext } from './internal-api/ValidationContext.ts';
26
+ import type { ValueContext } from './internal-api/ValueContext.ts';
27
+
28
+ interface NoteStateSpec extends DescendantNodeStateSpec<string> {
29
+ readonly readonly: Accessor<true>;
30
+ readonly noteText: ComputedNoteText;
31
+ readonly label: Accessor<TextRange<'label', 'form'> | null>;
32
+ readonly hint: Accessor<TextRange<'hint', 'form'> | null>;
33
+ readonly children: null;
34
+ readonly value: SimpleAtomicState<string>;
35
+ readonly valueOptions: null;
36
+ }
37
+
38
+ export class Note
39
+ extends DescendantNode<NoteNodeDefinition, NoteStateSpec, null>
40
+ implements
41
+ NoteNode,
42
+ EvaluationContext,
43
+ SubscribableDependency,
44
+ ValidationContext,
45
+ ValueContext<string>
46
+ {
47
+ private readonly validation: SharedValidationState;
48
+ protected readonly state: SharedNodeState<NoteStateSpec>;
49
+
50
+ // InstanceNode
51
+ protected engineState: EngineState<NoteStateSpec>;
52
+
53
+ // NoteNode
54
+ readonly nodeType = 'note';
55
+ readonly appearances: NoteNodeAppearances;
56
+ readonly currentState: CurrentState<NoteStateSpec>;
57
+
58
+ get validationState(): LeafNodeValidationState {
59
+ return this.validation.currentState;
60
+ }
61
+
62
+ // ValueContext
63
+ readonly encodeValue = identity<string>;
64
+
65
+ readonly decodeValue = identity<string>;
66
+
67
+ constructor(parent: GeneralParentNode, definition: NoteNodeDefinition) {
68
+ super(parent, definition);
69
+
70
+ this.appearances = definition.bodyElement.appearances;
71
+
72
+ const sharedStateOptions = {
73
+ clientStateFactory: this.engineConfig.stateFactory,
74
+ };
75
+
76
+ const isReadonly = createNoteReadonlyThunk(this, definition.bind.readonly);
77
+ const noteTextComputation = createNoteText(this, definition.noteTextDefinition);
78
+
79
+ let noteText: ComputedNoteText;
80
+ let label: Accessor<TextRange<'label', 'form'> | null>;
81
+ let hint: Accessor<TextRange<'hint', 'form'> | null>;
82
+
83
+ switch (noteTextComputation.role) {
84
+ case 'label': {
85
+ noteText = noteTextComputation.label;
86
+ label = noteTextComputation.label;
87
+ hint = createFieldHint(this, definition);
88
+
89
+ break;
90
+ }
91
+
92
+ case 'hint': {
93
+ noteText = noteTextComputation.hint;
94
+ label = createNodeLabel(this, definition);
95
+ hint = noteTextComputation.hint;
96
+
97
+ break;
98
+ }
99
+
100
+ default:
101
+ throw new UnreachableError(noteTextComputation);
102
+ }
103
+
104
+ const state = createSharedNodeState(
105
+ this.scope,
106
+ {
107
+ reference: this.contextReference,
108
+ readonly: isReadonly,
109
+ relevant: this.isRelevant,
110
+ required: this.isRequired,
111
+
112
+ label,
113
+ hint,
114
+ noteText,
115
+
116
+ children: null,
117
+ valueOptions: null,
118
+ value: createValueState(this),
119
+ },
120
+ sharedStateOptions
121
+ );
122
+
123
+ this.state = state;
124
+ this.engineState = state.engineState;
125
+ this.currentState = state.currentState;
126
+ this.validation = createValidationState(this, sharedStateOptions);
127
+ }
128
+
129
+ getViolation(): AnyViolation | null {
130
+ return this.validation.engineState.violation;
131
+ }
132
+
133
+ // ValidationContext
134
+ isBlank(): boolean {
135
+ return this.engineState.value === '';
136
+ }
137
+
138
+ // InstanceNode
139
+ getChildren(): readonly [] {
140
+ return [];
141
+ }
142
+ }
@@ -2,8 +2,10 @@ import type { XFormsXPathEvaluator } from '@getodk/xpath';
2
2
  import type { Accessor, Signal } from 'solid-js';
3
3
  import { createSignal } from 'solid-js';
4
4
  import type { XFormDOM } from '../XFormDOM.ts';
5
+ import type { BodyClassList } from '../body/BodyDefinition.ts';
5
6
  import type { ActiveLanguage, FormLanguage, FormLanguages } from '../client/FormLanguage.ts';
6
7
  import type { RootNode } from '../client/RootNode.ts';
8
+ import type { AncestorNodeValidationState } from '../client/validation.ts';
7
9
  import type { ChildrenState } from '../lib/reactivity/createChildrenState.ts';
8
10
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
9
11
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -12,6 +14,7 @@ import type { CurrentState } from '../lib/reactivity/node-state/createCurrentSta
12
14
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
13
15
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
14
16
  import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
17
+ import { createAggregatedViolations } from '../lib/reactivity/validation/createAggregatedViolations.ts';
15
18
  import type { RootDefinition } from '../model/RootDefinition.ts';
16
19
  import { InstanceNode } from './abstract/InstanceNode.ts';
17
20
  import { buildChildren } from './children.ts';
@@ -98,28 +101,22 @@ export class Root
98
101
  SubscribableDependency,
99
102
  TranslationContext
100
103
  {
101
- static async initialize(
102
- xformDOM: XFormDOM,
103
- definition: RootDefinition,
104
- engineConfig: InstanceConfig
105
- ): Promise<Root> {
106
- const instance = new Root(xformDOM, definition, engineConfig);
107
-
108
- await instance.formStateInitialized();
109
-
110
- return instance;
111
- }
112
-
113
104
  private readonly childrenState: ChildrenState<GeneralChildNode>;
114
105
 
115
106
  // InstanceNode
107
+ readonly hasReadonlyAncestor = () => false;
108
+ readonly isReadonly = () => false;
109
+ readonly hasNonRelevantAncestor = () => false;
110
+ readonly isRelevant = () => true;
116
111
  protected readonly state: SharedNodeState<RootStateSpec>;
117
112
  protected readonly engineState: EngineState<RootStateSpec>;
118
113
 
119
114
  // RootNode
120
115
  readonly nodeType = 'root';
121
-
116
+ readonly appearances = null;
117
+ readonly classes: BodyClassList;
122
118
  readonly currentState: MaterializedChildren<CurrentState<RootStateSpec>, GeneralChildNode>;
119
+ readonly validationState: AncestorNodeValidationState;
123
120
 
124
121
  protected readonly instanceDOM: XFormDOM;
125
122
 
@@ -129,12 +126,6 @@ export class Root
129
126
  // EvaluationContext
130
127
  readonly evaluator: XFormsXPathEvaluator;
131
128
 
132
- private readonly rootReference: string;
133
-
134
- override get contextReference(): string {
135
- return this.rootReference;
136
- }
137
-
138
129
  readonly contextNode: Element;
139
130
 
140
131
  // RootNode
@@ -147,25 +138,27 @@ export class Root
147
138
  return this.engineState.activeLanguage;
148
139
  }
149
140
 
150
- protected constructor(
151
- xformDOM: XFormDOM,
152
- definition: RootDefinition,
153
- engineConfig: InstanceConfig
154
- ) {
155
- super(engineConfig, null, definition);
141
+ constructor(xformDOM: XFormDOM, definition: RootDefinition, engineConfig: InstanceConfig) {
142
+ const reference = definition.nodeset;
156
143
 
157
- const childrenState = createChildrenState<Root, GeneralChildNode>(this);
144
+ super(engineConfig, null, definition, {
145
+ computeReference: () => reference,
146
+ });
158
147
 
159
- this.childrenState = childrenState;
148
+ this.classes = definition.classes;
160
149
 
161
- const reference = definition.nodeset;
150
+ const childrenState = createChildrenState<Root, GeneralChildNode>(this);
162
151
 
163
- this.rootReference = reference;
152
+ this.childrenState = childrenState;
164
153
 
165
154
  const instanceDOM = xformDOM.createInstance();
166
155
  const evaluator = instanceDOM.primaryInstanceEvaluator;
167
156
  const { translations } = evaluator;
168
157
  const { defaultLanguage, languages } = getInitialLanguageState(translations);
158
+ const sharedStateOptions = {
159
+ clientStateFactory: this.engineConfig.stateFactory,
160
+ };
161
+
169
162
  const state = createSharedNodeState(
170
163
  this.scope,
171
164
  {
@@ -180,14 +173,16 @@ export class Root
180
173
  value: null,
181
174
  children: childrenState.childIds,
182
175
  },
183
- {
184
- clientStateFactory: engineConfig.stateFactory,
185
- }
176
+ sharedStateOptions
186
177
  );
187
178
 
188
179
  this.state = state;
189
180
  this.engineState = state.engineState;
190
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
181
+ this.currentState = materializeCurrentStateChildren(
182
+ this.scope,
183
+ state.currentState,
184
+ childrenState
185
+ );
191
186
 
192
187
  const contextNode = instanceDOM.xformDocument.createElement(definition.nodeName);
193
188
 
@@ -199,40 +194,7 @@ export class Root
199
194
  this.languages = languages;
200
195
 
201
196
  childrenState.setChildren(buildChildren(this));
202
- }
203
-
204
- /**
205
- * Waits until form state is fully initialized.
206
- *
207
- * As much as possible, all instance state computations are implemented so
208
- * that they complete synchronously.
209
- *
210
- * There is currently one exception: because instance nodes may form
211
- * computation dependencies into their descendants as well as their ancestors,
212
- * there is an allowance **during form initialization only** to account for
213
- * this chicken/egg scenario. Note that this allowance is intentionally,
214
- * strictly limited: if form state initialization is not resolved within a
215
- * single microtask tick we throw/reject.
216
- *
217
- * All subsequent computations are always performed synchronously (and we will
218
- * use tests to validate this, by utilizing the synchronously returned `Root`
219
- * state from client-facing write interfaces).
220
- */
221
- async formStateInitialized(): Promise<void> {
222
- await new Promise<void>((resolve) => {
223
- queueMicrotask(resolve);
224
- });
225
-
226
- if (!this.isStateInitialized()) {
227
- throw new Error(
228
- 'Form state initialization failed to complete in a single frame. Has some aspect of reactive computation been made asynchronous by mistake?'
229
- );
230
- }
231
- }
232
-
233
- // InstanceNode
234
- protected computeReference(_parent: null, definition: RootDefinition): string {
235
- return definition.nodeset;
197
+ this.validationState = createAggregatedViolations(this, sharedStateOptions);
236
198
  }
237
199
 
238
200
  getChildren(): readonly GeneralChildNode[] {