@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
@@ -0,0 +1,72 @@
1
+ import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
2
+ import type { Accessor } from 'solid-js';
3
+ import type { TextRange } from '../../../client/TextRange.ts';
4
+ import type { EvaluationContext } from '../../../instance/internal-api/EvaluationContext.ts';
5
+ import type { NoteTextDefinition } from '../../../parse/NoteNodeDefinition.ts';
6
+ import { createTextRange } from './createTextRange.ts';
7
+
8
+ // eslint-disable-next-line @typescript-eslint/sort-type-constituents
9
+ export type NoteTextRole = 'label' | 'hint';
10
+
11
+ export type ComputedNoteText<Role extends NoteTextRole = NoteTextRole> = Accessor<
12
+ TextRange<Role, 'form'>
13
+ >;
14
+
15
+ interface BaseNoteText {
16
+ readonly role: NoteTextRole;
17
+ readonly label: ComputedNoteText<'label'> | null;
18
+ readonly hint: ComputedNoteText<'hint'> | null;
19
+ }
20
+
21
+ interface LabelNoteText extends BaseNoteText {
22
+ readonly role: 'label';
23
+ readonly label: ComputedNoteText<'label'>;
24
+ readonly hint: null;
25
+ }
26
+
27
+ interface HintNoteText extends BaseNoteText {
28
+ readonly role: 'hint';
29
+ readonly label: null;
30
+ readonly hint: ComputedNoteText<'hint'>;
31
+ }
32
+
33
+ // prettier-ignore
34
+ export type NoteTextComputation =
35
+ // eslint-disable-next-line @typescript-eslint/sort-type-constituents
36
+ | LabelNoteText
37
+ | HintNoteText;
38
+
39
+ export const createNoteText = (
40
+ context: EvaluationContext,
41
+ noteTextDefinition: NoteTextDefinition
42
+ ): NoteTextComputation => {
43
+ const { scope } = context;
44
+ const { role } = noteTextDefinition;
45
+
46
+ return scope.runTask(() => {
47
+ switch (role) {
48
+ case 'label': {
49
+ const label = createTextRange(context, role, noteTextDefinition);
50
+
51
+ return {
52
+ role,
53
+ label,
54
+ hint: null,
55
+ };
56
+ }
57
+
58
+ case 'hint': {
59
+ const hint = createTextRange(context, role, noteTextDefinition);
60
+
61
+ return {
62
+ role,
63
+ label: null,
64
+ hint,
65
+ };
66
+ }
67
+
68
+ default:
69
+ throw new UnreachableError(noteTextDefinition);
70
+ }
71
+ });
72
+ };
@@ -1,23 +1,13 @@
1
- import type { CollectionValues } from '@getodk/common/types/collections/CollectionValues.ts';
2
- import { createMemo, type Accessor } from 'solid-js';
3
- import type {
4
- TextElementChild,
5
- TextElementDefinition,
6
- } from '../../../body/text/TextElementDefinition.ts';
7
- import type { TextElementReferencePart } from '../../../body/text/TextElementReferencePart.ts';
8
- import type { TextChunkSource } from '../../../client/TextRange.ts';
1
+ import type { Accessor } from 'solid-js';
2
+ import { createMemo } from 'solid-js';
3
+ import type { TextChunkSource, TextRole } from '../../../client/TextRange.ts';
9
4
  import type { EvaluationContext } from '../../../instance/internal-api/EvaluationContext.ts';
10
5
  import { TextChunk } from '../../../instance/text/TextChunk.ts';
11
- import { TextRange, type TextRole } from '../../../instance/text/TextRange.ts';
6
+ import { TextRange } from '../../../instance/text/TextRange.ts';
7
+ import type { AnyTextChunkDefinition } from '../../../parse/text/abstract/TextChunkDefinition.ts';
8
+ import type { TextRangeDefinition } from '../../../parse/text/abstract/TextRangeDefinition.ts';
12
9
  import { createComputedExpression } from '../createComputedExpression.ts';
13
10
 
14
- // prettier-ignore
15
- type TextSources =
16
- | readonly [TextElementReferencePart]
17
- | readonly TextElementChild[];
18
-
19
- type TextSource = CollectionValues<TextSources>;
20
-
21
11
  interface TextChunkComputation {
22
12
  readonly source: TextChunkSource;
23
13
  readonly getText: Accessor<string>;
@@ -25,21 +15,20 @@ interface TextChunkComputation {
25
15
 
26
16
  const createComputedTextChunk = (
27
17
  context: EvaluationContext,
28
- textSource: TextSource
18
+ textSource: AnyTextChunkDefinition
29
19
  ): TextChunkComputation => {
30
- const { type } = textSource;
20
+ const { source } = textSource;
31
21
 
32
- if (type === 'static') {
22
+ if (source === 'static') {
33
23
  const { stringValue } = textSource;
34
24
 
35
25
  return {
36
- source: type,
26
+ source,
37
27
  getText: () => stringValue,
38
28
  };
39
29
  }
40
30
 
41
31
  return context.scope.runTask(() => {
42
- const source: TextChunkSource = type === 'reference' ? 'itext' : type;
43
32
  const getText = createComputedExpression(context, textSource);
44
33
 
45
34
  return {
@@ -51,7 +40,7 @@ const createComputedTextChunk = (
51
40
 
52
41
  const createTextChunks = (
53
42
  context: EvaluationContext,
54
- textSources: TextSources
43
+ textSources: readonly AnyTextChunkDefinition[]
55
44
  ): Accessor<readonly TextChunk[]> => {
56
45
  return context.scope.runTask(() => {
57
46
  const { root } = context;
@@ -67,45 +56,7 @@ const createTextChunks = (
67
56
  });
68
57
  };
69
58
 
70
- interface CreateTextRangeOptions<FallbackValue extends string | null> {
71
- readonly fallbackValue?: FallbackValue;
72
- }
73
-
74
- // prettier-ignore
75
- type ComputedTextRange<
76
- Role extends TextRole,
77
- Definition extends TextElementDefinition<Role> | null,
78
- FallbackValue extends string | null
79
- > = Accessor<
80
- Definition extends null
81
- ? FallbackValue extends null
82
- ? TextRange<Role> | null
83
- : TextRange<Role>
84
- : TextRange<Role>
85
- >;
86
-
87
- // prettier-ignore
88
- type FallbackTextRange<
89
- Role extends TextRole,
90
- FallbackValue extends string | null
91
- > =
92
- FallbackValue extends null
93
- ? TextRange<Role> | null
94
- : TextRange<Role>;
95
-
96
- const createFallbackTextRange = <Role extends TextRole, FallbackValue extends string | null>(
97
- context: EvaluationContext,
98
- role: Role,
99
- fallbackValue: FallbackValue
100
- ): FallbackTextRange<Role, FallbackValue> => {
101
- if (fallbackValue == null) {
102
- return null as FallbackTextRange<Role, FallbackValue>;
103
- }
104
-
105
- const staticChunk = new TextChunk(context.root, 'static', fallbackValue);
106
-
107
- return new TextRange(role, [staticChunk]);
108
- };
59
+ type ComputedFormTextRange<Role extends TextRole> = Accessor<TextRange<Role, 'form'>>;
109
60
 
110
61
  /**
111
62
  * Creates a text range (e.g. label or hint) from the provided definition,
@@ -116,40 +67,16 @@ const createFallbackTextRange = <Role extends TextRole, FallbackValue extends st
116
67
  *
117
68
  * @todo This does not yet handle itext translations **with** outputs!
118
69
  */
119
- export const createTextRange = <
120
- Role extends TextRole,
121
- Definition extends TextElementDefinition<Role> | null,
122
- FallbackValue extends string | null = null,
123
- >(
70
+ export const createTextRange = <Role extends TextRole>(
124
71
  context: EvaluationContext,
125
72
  role: Role,
126
- definition: Definition,
127
- options?: CreateTextRangeOptions<FallbackValue>
128
- ): ComputedTextRange<Role, Definition, FallbackValue> => {
73
+ definition: TextRangeDefinition<Role>
74
+ ): ComputedFormTextRange<Role> => {
129
75
  return context.scope.runTask(() => {
130
- if (definition == null) {
131
- const textRange = createFallbackTextRange(
132
- context,
133
- role,
134
- options?.fallbackValue ?? (null as FallbackValue)
135
- );
136
- const getTextRange = () => textRange;
137
-
138
- return getTextRange as ComputedTextRange<Role, Definition, FallbackValue>;
139
- }
140
-
141
- const { children, referenceExpression } = definition;
142
-
143
- let getTextChunks: Accessor<readonly TextChunk[]>;
144
-
145
- if (referenceExpression == null) {
146
- getTextChunks = createTextChunks(context, children);
147
- } else {
148
- getTextChunks = createTextChunks(context, [referenceExpression]);
149
- }
76
+ const getTextChunks = createTextChunks(context, definition.chunks);
150
77
 
151
78
  return createMemo(() => {
152
- return new TextRange(role, getTextChunks());
79
+ return new TextRange('form', role, getTextChunks());
153
80
  });
154
81
  });
155
82
  };
@@ -0,0 +1,70 @@
1
+ import { createMemo } from 'solid-js';
2
+ import type { OpaqueReactiveObjectFactory } from '../../../client/OpaqueReactiveObjectFactory.ts';
3
+ import type {
4
+ AncestorNodeValidationState,
5
+ DescendantNodeViolationReference,
6
+ } from '../../../client/validation.ts';
7
+ import type { AnyParentNode, AnyValueNode } from '../../../instance/hierarchy.ts';
8
+ import { createSharedNodeState } from '../node-state/createSharedNodeState.ts';
9
+
10
+ const violationReference = (node: AnyValueNode): DescendantNodeViolationReference | null => {
11
+ const violation = node.getViolation();
12
+
13
+ if (violation == null) {
14
+ return null;
15
+ }
16
+
17
+ const { nodeId } = node;
18
+
19
+ return {
20
+ nodeId,
21
+ get reference() {
22
+ return node.currentState.reference;
23
+ },
24
+ violation,
25
+ };
26
+ };
27
+
28
+ const collectViolationReferences = (
29
+ context: AnyParentNode
30
+ ): readonly DescendantNodeViolationReference[] => {
31
+ return context.getChildren().flatMap((child) => {
32
+ switch (child.nodeType) {
33
+ case 'model-value':
34
+ case 'note':
35
+ case 'string':
36
+ case 'select': {
37
+ const reference = violationReference(child);
38
+
39
+ if (reference == null) {
40
+ return [];
41
+ }
42
+
43
+ return [reference];
44
+ }
45
+
46
+ default:
47
+ return collectViolationReferences(child);
48
+ }
49
+ });
50
+ };
51
+
52
+ interface AggregatedViolationsOptions {
53
+ readonly clientStateFactory: OpaqueReactiveObjectFactory<AncestorNodeValidationState>;
54
+ }
55
+
56
+ export const createAggregatedViolations = (
57
+ context: AnyParentNode,
58
+ options: AggregatedViolationsOptions
59
+ ): AncestorNodeValidationState => {
60
+ const { scope } = context;
61
+
62
+ return scope.runTask(() => {
63
+ const violations = createMemo(() => {
64
+ return collectViolationReferences(context);
65
+ });
66
+ const spec = { violations };
67
+
68
+ return createSharedNodeState(scope, spec, options).currentState;
69
+ });
70
+ };
@@ -0,0 +1,196 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import { createMemo } from 'solid-js';
3
+ import type { OpaqueReactiveObjectFactory } from '../../../client/OpaqueReactiveObjectFactory.ts';
4
+ import type {
5
+ TextRange as ClientTextRange,
6
+ ValidationTextRole,
7
+ } from '../../../client/TextRange.ts';
8
+ import { VALIDATION_TEXT } from '../../../client/constants.ts';
9
+ import type {
10
+ AnyViolation,
11
+ ConditionSatisfied,
12
+ ConditionValidation,
13
+ ConditionViolation,
14
+ ValidationCondition,
15
+ } from '../../../client/validation.ts';
16
+ import type { ValidationContext } from '../../../instance/internal-api/ValidationContext.ts';
17
+ import { TextChunk } from '../../../instance/text/TextChunk.ts';
18
+ import { TextRange } from '../../../instance/text/TextRange.ts';
19
+ import type { MessageDefinition } from '../../../parse/text/MessageDefinition.ts';
20
+ import { createComputedExpression } from '../createComputedExpression.ts';
21
+ import type {
22
+ SharedNodeState,
23
+ SharedNodeStateOptions,
24
+ } from '../node-state/createSharedNodeState.ts';
25
+ import { createSharedNodeState } from '../node-state/createSharedNodeState.ts';
26
+ import type { ReactiveScope } from '../scope.ts';
27
+ import { createTextRange } from '../text/createTextRange.ts';
28
+
29
+ type EngineViolationMessage<Role extends ValidationTextRole> = ClientTextRange<Role, 'engine'>;
30
+
31
+ const engineViolationMessage = <Role extends ValidationTextRole>(
32
+ context: ValidationContext,
33
+ role: Role
34
+ ): Accessor<EngineViolationMessage<Role>> => {
35
+ const messageText = VALIDATION_TEXT[role];
36
+ const chunk = new TextChunk(context.root, 'static', messageText);
37
+ const message = new TextRange('engine', role, [chunk]);
38
+
39
+ return () => message;
40
+ };
41
+
42
+ const createViolationMessage = <Role extends ValidationTextRole>(
43
+ context: ValidationContext,
44
+ role: Role,
45
+ definition: MessageDefinition<Role> | null
46
+ ) => {
47
+ if (definition == null) {
48
+ return engineViolationMessage(context, role);
49
+ }
50
+
51
+ return createTextRange(context, role, definition);
52
+ };
53
+
54
+ // prettier-ignore
55
+ type ComputedConditionValidation<
56
+ Condition extends ValidationCondition
57
+ > = Accessor<ConditionValidation<Condition>>;
58
+
59
+ const constraintValid = (): ConditionSatisfied<'constraint'> => {
60
+ return {
61
+ condition: 'constraint',
62
+ valid: true,
63
+ message: null,
64
+ };
65
+ };
66
+
67
+ const createConstraintValidation = (
68
+ context: ValidationContext
69
+ ): ComputedConditionValidation<'constraint'> => {
70
+ return context.scope.runTask(() => {
71
+ const { constraint, constraintMsg } = context.definition.bind;
72
+
73
+ if (constraint == null) {
74
+ return constraintValid;
75
+ }
76
+
77
+ const isValid = createComputedExpression(context, constraint, {
78
+ arbitraryDependencies: [context],
79
+ });
80
+
81
+ const message = createViolationMessage(context, 'constraintMsg', constraintMsg);
82
+
83
+ return createMemo(() => {
84
+ if (!context.isRelevant() || context.isBlank() || isValid()) {
85
+ return constraintValid();
86
+ }
87
+
88
+ return {
89
+ condition: 'constraint',
90
+ valid: false,
91
+ message: message(),
92
+ } as const;
93
+ });
94
+ });
95
+ };
96
+
97
+ const requiredValid = (): ConditionSatisfied<'required'> => {
98
+ return {
99
+ condition: 'required',
100
+ valid: true,
101
+ message: null,
102
+ };
103
+ };
104
+
105
+ const createRequiredValidation = (
106
+ context: ValidationContext
107
+ ): ComputedConditionValidation<'required'> => {
108
+ return context.scope.runTask(() => {
109
+ const { required, requiredMsg } = context.definition.bind;
110
+
111
+ if (required.isDefaultExpression) {
112
+ return requiredValid;
113
+ }
114
+
115
+ const isValid = () => {
116
+ if (context.isRequired()) {
117
+ return !context.isBlank();
118
+ }
119
+
120
+ return true;
121
+ };
122
+
123
+ const message = createViolationMessage(context, 'requiredMsg', requiredMsg);
124
+
125
+ return createMemo(() => {
126
+ if (!context.isRelevant() || isValid()) {
127
+ return requiredValid();
128
+ }
129
+
130
+ return {
131
+ condition: 'required',
132
+ valid: false,
133
+ message: message(),
134
+ } as const;
135
+ });
136
+ });
137
+ };
138
+
139
+ type OptionalViolation<Condition extends ValidationCondition> =
140
+ Accessor<ConditionViolation<Condition> | null>;
141
+
142
+ const createComputedViolation = <Condition extends ValidationCondition>(
143
+ scope: ReactiveScope,
144
+ validateCondition: ComputedConditionValidation<Condition>
145
+ ): OptionalViolation<Condition> => {
146
+ return scope.runTask(() => {
147
+ return createMemo(() => {
148
+ const validation = validateCondition();
149
+
150
+ if (validation.valid) {
151
+ return null;
152
+ }
153
+
154
+ return validation;
155
+ });
156
+ });
157
+ };
158
+
159
+ type ComputedViolation = Accessor<AnyViolation | null>;
160
+
161
+ interface ValidationStateSpec {
162
+ readonly constraint: ComputedConditionValidation<'constraint'>;
163
+ readonly required: ComputedConditionValidation<'required'>;
164
+ readonly violation: ComputedViolation;
165
+ }
166
+
167
+ export type SharedValidationState = SharedNodeState<ValidationStateSpec>;
168
+
169
+ interface ValidationStateOptions<Factory extends OpaqueReactiveObjectFactory>
170
+ extends SharedNodeStateOptions<Factory, ValidationStateSpec> {}
171
+
172
+ export const createValidationState = <Factory extends OpaqueReactiveObjectFactory>(
173
+ context: ValidationContext,
174
+ options: ValidationStateOptions<Factory>
175
+ ): SharedValidationState => {
176
+ const { scope } = context;
177
+
178
+ return scope.runTask(() => {
179
+ const constraint = createConstraintValidation(context);
180
+ const constraintViolation = createComputedViolation(scope, constraint);
181
+ const required = createRequiredValidation(context);
182
+ const requiredViolation = createComputedViolation(scope, required);
183
+
184
+ const violation = createMemo(() => {
185
+ return constraintViolation() ?? requiredViolation();
186
+ });
187
+
188
+ const spec: ValidationStateSpec = {
189
+ constraint,
190
+ required,
191
+ violation,
192
+ };
193
+
194
+ return createSharedNodeState(scope, spec, options);
195
+ });
196
+ };
@@ -57,7 +57,6 @@ export class BindComputation<Computation extends BindComputationType> extends De
57
57
  readonly computation: Computation,
58
58
  expression: string | null
59
59
  ) {
60
- const isInherited = computation === 'readonly' || computation === 'relevant';
61
60
  const ignoreContextReference = computation === 'constraint';
62
61
 
63
62
  let isDefaultExpression: boolean;
@@ -78,9 +77,6 @@ export class BindComputation<Computation extends BindComputationType> extends De
78
77
 
79
78
  super(bind, bindComputationResultTypes[computation], resolvedExpression, {
80
79
  ignoreContextReference,
81
- semanticDependencies: {
82
- parentContext: isInherited,
83
- },
84
80
  });
85
81
 
86
82
  this.isDefaultExpression = isDefaultExpression;
@@ -3,6 +3,7 @@ import { bindDataType } from '../XFormDataType.ts';
3
3
  import type { XFormDefinition } from '../XFormDefinition.ts';
4
4
  import { DependencyContext } from '../expression/DependencyContext.ts';
5
5
  import type { DependentExpression } from '../expression/DependentExpression.ts';
6
+ import { MessageDefinition } from '../parse/text/MessageDefinition.ts';
6
7
  import { BindComputation } from './BindComputation.ts';
7
8
  import type { BindElement } from './BindElement.ts';
8
9
  import type { ModelDefinition } from './ModelDefinition.ts';
@@ -25,15 +26,16 @@ export class BindDefinition extends DependencyContext {
25
26
  /**
26
27
  * Diverges from {@link https://github.com/getodk/javarosa/blob/059321160e6f8dbb3e81d9add61d68dd35b13cc8/dag.md | JavaRosa's}, which excludes `constraint` expressions. We compute `constraint` dependencies like the other <bind> computation expressions, but explicitly ignore self-references (this is currently handled by {@link BindComputation}, via its {@link DependentExpression} parent class).
27
28
  */
28
- readonly constraint: BindComputation<'constraint'> & DependentExpression<'boolean'>;
29
+ readonly constraint: BindComputation<'constraint'>;
30
+
31
+ readonly constraintMsg: MessageDefinition<'constraintMsg'> | null;
32
+ readonly requiredMsg: MessageDefinition<'requiredMsg'> | null;
29
33
 
30
34
  // TODO: it is unclear whether this will need to be supported.
31
35
  // https://github.com/getodk/collect/issues/3758 mentions deprecation.
32
36
  readonly saveIncomplete: BindComputation<'saveIncomplete'>;
33
37
 
34
- // TODO: these are deferred just to put off sharing namespace stuff
35
- // readonly requiredMsg: string | null;
36
- // readonly constraintMsg: string | null;
38
+ // TODO: these are deferred until prioritized
37
39
  // readonly preload: string | null;
38
40
  // readonly preloadParams: string | null;
39
41
  // readonly 'max-pixels': string | null;
@@ -88,9 +90,9 @@ export class BindDefinition extends DependencyContext {
88
90
  this.required = BindComputation.forExpression(this, 'required');
89
91
  this.constraint = BindComputation.forExpression(this, 'constraint');
90
92
  this.saveIncomplete = BindComputation.forExpression(this, 'saveIncomplete');
93
+ this.constraintMsg = MessageDefinition.from(this, 'constraintMsg');
94
+ this.requiredMsg = MessageDefinition.from(this, 'requiredMsg');
91
95
 
92
- // this.requiredMsg = BindComputation.forExpression(this, 'requiredMsg');
93
- // this.constraintMsg = BindComputation.forExpression(this, 'constraintMsg');
94
96
  // this.preload = BindComputation.forExpression(this, 'preload');
95
97
  // this.preloadParams = BindComputation.forExpression(this, 'preloadParams');
96
98
  // this['max-pixels'] = BindComputation.forExpression(this, 'max-pixels');
@@ -5,4 +5,5 @@ export interface BindElement {
5
5
 
6
6
  getAttribute(name: 'nodeset'): BindNodeset;
7
7
  getAttribute(name: string): string | null;
8
+ getAttributeNS(namespaceURI: string | null, localName: string): string | null;
8
9
  }
@@ -1,5 +1,4 @@
1
1
  import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
2
- import type { RepeatDefinition } from '../body/RepeatDefinition.ts';
3
2
  import type { BindDefinition } from './BindDefinition.ts';
4
3
  import type {
5
4
  ModelNode,
@@ -14,7 +13,7 @@ import type { RootDefinition } from './RootDefinition.ts';
14
13
 
15
14
  export type DescendentNodeType = Exclude<NodeDefinitionType, 'root'>;
16
15
 
17
- type DescendentNodeBodyElement = AnyBodyElementDefinition | RepeatDefinition;
16
+ type DescendentNodeBodyElement = AnyBodyElementDefinition;
18
17
 
19
18
  export abstract class DescendentNodeDefinition<
20
19
  Type extends DescendentNodeType,
@@ -1,14 +1,13 @@
1
- import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
2
- import type { AnyControlDefinition } from '../body/control/ControlDefinition.ts';
1
+ import type { AnyBodyElementDefinition, ControlElementDefinition } from '../body/BodyDefinition.ts';
3
2
  import type { BindDefinition } from './BindDefinition.ts';
4
3
  import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
5
4
  import type { NodeDefinition, ParentNodeDefinition } from './NodeDefinition.ts';
6
5
 
7
- export class ValueNodeDefinition
8
- extends DescendentNodeDefinition<'value-node', AnyControlDefinition | null>
9
- implements NodeDefinition<'value-node'>
6
+ export class LeafNodeDefinition
7
+ extends DescendentNodeDefinition<'leaf-node', ControlElementDefinition | null>
8
+ implements NodeDefinition<'leaf-node'>
10
9
  {
11
- readonly type = 'value-node';
10
+ readonly type = 'leaf-node';
12
11
 
13
12
  readonly nodeName: string;
14
13
  readonly children = null;
@@ -17,6 +17,10 @@ class ArtificialBindElement implements BindElement {
17
17
 
18
18
  return null;
19
19
  }
20
+
21
+ getAttributeNS() {
22
+ return null;
23
+ }
20
24
  }
21
25
 
22
26
  type TopologicalSortIndex = number;
@@ -8,7 +8,7 @@ export class ModelDefinition {
8
8
 
9
9
  constructor(readonly form: XFormDefinition) {
10
10
  this.binds = ModelBindMap.fromModel(this);
11
- this.root = new RootDefinition(form, this);
11
+ this.root = new RootDefinition(form, this, form.body.classes);
12
12
  }
13
13
 
14
14
  toJSON() {