@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,61 @@
1
+ import type { LocalNamedElement } from '@getodk/common/types/dom.ts';
2
+ import type { XFormDefinition } from '../../XFormDefinition.ts';
3
+ import type { AnyGroupElementDefinition } from '../../body/BodyDefinition.ts';
4
+ import type { RepeatElementDefinition } from '../../body/RepeatElementDefinition.ts';
5
+ import type { AnyControlDefinition } from '../../body/control/ControlDefinition.ts';
6
+ import type { BaseGroupDefinition } from '../../body/group/BaseGroupDefinition.ts';
7
+ import { getLabelElement, getRepeatGroupLabelElement } from '../../lib/dom/query.ts';
8
+ import { TextElementDefinition } from './abstract/TextElementDefinition.ts';
9
+
10
+ // prettier-ignore
11
+ export type LabelOwner =
12
+ | AnyControlDefinition
13
+ | AnyGroupElementDefinition
14
+ | RepeatElementDefinition;
15
+
16
+ interface LabelElement extends LocalNamedElement<'label'> {}
17
+
18
+ export class LabelDefinition extends TextElementDefinition<'label'> {
19
+ static forControl(form: XFormDefinition, control: AnyControlDefinition): LabelDefinition | null {
20
+ const labelElement = getLabelElement(control.element);
21
+
22
+ if (labelElement == null) {
23
+ return null;
24
+ }
25
+
26
+ return new this(form, control, labelElement);
27
+ }
28
+
29
+ static forRepeatGroup(
30
+ form: XFormDefinition,
31
+ repeat: RepeatElementDefinition
32
+ ): LabelDefinition | null {
33
+ const repeatGroupLabel = getRepeatGroupLabelElement(repeat.element);
34
+
35
+ if (repeatGroupLabel == null) {
36
+ return null;
37
+ }
38
+
39
+ return new this(form, repeat, repeatGroupLabel);
40
+ }
41
+
42
+ static forGroup(
43
+ form: XFormDefinition,
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ group: BaseGroupDefinition<any>
46
+ ): LabelDefinition | null {
47
+ const labelElement = getLabelElement(group.element);
48
+
49
+ if (labelElement == null) {
50
+ return null;
51
+ }
52
+
53
+ return new this(form, group, labelElement);
54
+ }
55
+
56
+ readonly role = 'label';
57
+
58
+ private constructor(form: XFormDefinition, owner: LabelOwner, element: LabelElement) {
59
+ super(form, owner, element);
60
+ }
61
+ }
@@ -0,0 +1,49 @@
1
+ import { JAVAROSA_NAMESPACE_URI } from '@getodk/common/constants/xmlns.ts';
2
+ import type { BindDefinition } from '../../model/BindDefinition.ts';
3
+ import { StaticTextChunkDefinition } from './StaticTextChunkDefinition.ts';
4
+ import { TranslationChunkDefinition } from './TranslationChunkDefinition.ts';
5
+ import type { TextBindAttributeLocalName, TextSourceNode } from './abstract/TextRangeDefinition.ts';
6
+ import { TextRangeDefinition } from './abstract/TextRangeDefinition.ts';
7
+
8
+ export type MessageSourceNode = TextSourceNode<TextBindAttributeLocalName>;
9
+
10
+ // prettier-ignore
11
+ type MessageChunk =
12
+ | StaticTextChunkDefinition
13
+ | TranslationChunkDefinition;
14
+
15
+ export class MessageDefinition<
16
+ Type extends TextBindAttributeLocalName,
17
+ > extends TextRangeDefinition<Type> {
18
+ static from<Type extends TextBindAttributeLocalName>(
19
+ bind: BindDefinition,
20
+ type: Type
21
+ ): MessageDefinition<Type> | null {
22
+ const message = bind.bindElement.getAttributeNS(JAVAROSA_NAMESPACE_URI, type);
23
+
24
+ if (message == null) {
25
+ return null;
26
+ }
27
+
28
+ return new this(bind, type, message);
29
+ }
30
+
31
+ readonly chunks: readonly [MessageChunk];
32
+
33
+ private constructor(
34
+ bind: BindDefinition,
35
+ readonly role: Type,
36
+ message: string
37
+ ) {
38
+ super(bind.form, bind, null);
39
+
40
+ const chunk: MessageChunk =
41
+ TranslationChunkDefinition.fromMessage(this, message) ??
42
+ StaticTextChunkDefinition.from(this, message);
43
+
44
+ this.chunks = [chunk];
45
+ }
46
+ }
47
+
48
+ // prettier-ignore
49
+ export type AnyMessageDefinition = MessageDefinition<TextBindAttributeLocalName>;
@@ -0,0 +1,25 @@
1
+ import type { KnownAttributeLocalNamedElement } from '@getodk/common/types/dom.ts';
2
+ import { TextChunkDefinition } from './abstract/TextChunkDefinition.ts';
3
+ import type { AnyTextRangeDefinition } from './abstract/TextRangeDefinition.ts';
4
+
5
+ interface OutputElement extends KnownAttributeLocalNamedElement<'output', 'value'> {}
6
+
7
+ const isOutputElement = (element: Element): element is OutputElement => {
8
+ return element.localName === 'output' && element.hasAttribute('value');
9
+ };
10
+
11
+ export class OutputChunkDefinition extends TextChunkDefinition<'output'> {
12
+ static from(context: AnyTextRangeDefinition, element: Element): OutputChunkDefinition | null {
13
+ if (isOutputElement(element)) {
14
+ return new this(context, element);
15
+ }
16
+
17
+ return null;
18
+ }
19
+
20
+ readonly source = 'output';
21
+
22
+ private constructor(context: AnyTextRangeDefinition, element: OutputElement) {
23
+ super(context, element.getAttribute('value'));
24
+ }
25
+ }
@@ -0,0 +1,14 @@
1
+ import { TextChunkDefinition } from './abstract/TextChunkDefinition.ts';
2
+ import type { AnyTextRangeDefinition } from './abstract/TextRangeDefinition.ts';
3
+
4
+ export class ReferenceChunkDefinition extends TextChunkDefinition<'reference'> {
5
+ static from(context: AnyTextRangeDefinition, refExpression: string): ReferenceChunkDefinition {
6
+ return new this(context, refExpression);
7
+ }
8
+
9
+ readonly source = 'reference';
10
+
11
+ private constructor(context: AnyTextRangeDefinition, refExpression: string) {
12
+ super(context, refExpression);
13
+ }
14
+ }
@@ -0,0 +1,19 @@
1
+ import { TextChunkDefinition } from './abstract/TextChunkDefinition.ts';
2
+ import type { AnyTextRangeDefinition } from './abstract/TextRangeDefinition.ts';
3
+
4
+ export type StaticTextChunkSourceNode = Attr | Text;
5
+
6
+ export class StaticTextChunkDefinition extends TextChunkDefinition<'static'> {
7
+ static from(context: AnyTextRangeDefinition, stringValue: string): StaticTextChunkDefinition {
8
+ return new this(context, stringValue);
9
+ }
10
+
11
+ readonly source = 'static';
12
+
13
+ private constructor(
14
+ context: AnyTextRangeDefinition,
15
+ override readonly stringValue: string
16
+ ) {
17
+ super(context, 'null');
18
+ }
19
+ }
@@ -0,0 +1,38 @@
1
+ import { expressionParser } from '@getodk/xpath/expressionParser.js';
2
+ import type { TranslationExpression } from '../xpath/semantic-analysis.ts';
3
+ import { isTranslationExpression } from '../xpath/semantic-analysis.ts';
4
+ import { TextChunkDefinition } from './abstract/TextChunkDefinition.ts';
5
+ import type { AnyTextRangeDefinition } from './abstract/TextRangeDefinition.ts';
6
+
7
+ export class TranslationChunkDefinition extends TextChunkDefinition<'translation'> {
8
+ static fromMessage(
9
+ context: AnyTextRangeDefinition,
10
+ maybeExpression: string
11
+ ): TranslationChunkDefinition | null {
12
+ try {
13
+ expressionParser.parse(maybeExpression);
14
+ } catch {
15
+ return null;
16
+ }
17
+
18
+ if (isTranslationExpression(maybeExpression)) {
19
+ return new this(context, maybeExpression);
20
+ }
21
+
22
+ return null;
23
+ }
24
+
25
+ static from(context: AnyTextRangeDefinition, maybeExpression: string) {
26
+ if (isTranslationExpression(maybeExpression)) {
27
+ return new this(context, maybeExpression);
28
+ }
29
+
30
+ return null;
31
+ }
32
+
33
+ readonly source = 'translation';
34
+
35
+ private constructor(context: AnyTextRangeDefinition, expression: TranslationExpression) {
36
+ super(context, expression, { isTranslated: true });
37
+ }
38
+ }
@@ -0,0 +1,38 @@
1
+ import type { TextChunkSource } from '../../../client/TextRange.ts';
2
+ import { DependentExpression } from '../../../expression/DependentExpression.ts';
3
+ import type { OutputChunkDefinition } from '../OutputChunkDefinition.ts';
4
+ import type { ReferenceChunkDefinition } from '../ReferenceChunkDefinition.ts';
5
+ import type { StaticTextChunkDefinition } from '../StaticTextChunkDefinition.ts';
6
+ import type { TranslationChunkDefinition } from '../TranslationChunkDefinition.ts';
7
+ import type { AnyTextRangeDefinition } from './TextRangeDefinition.ts';
8
+
9
+ interface TextChunkDefinitionOptions {
10
+ readonly isTranslated?: true;
11
+ }
12
+
13
+ export abstract class TextChunkDefinition<
14
+ Source extends TextChunkSource,
15
+ > extends DependentExpression<'string'> {
16
+ abstract readonly source: Source;
17
+ readonly stringValue?: string;
18
+
19
+ constructor(
20
+ context: AnyTextRangeDefinition,
21
+ expression: string,
22
+ options: TextChunkDefinitionOptions = {}
23
+ ) {
24
+ super(context, 'string', expression, {
25
+ semanticDependencies: {
26
+ translations: options.isTranslated,
27
+ },
28
+ ignoreContextReference: true,
29
+ });
30
+ }
31
+ }
32
+
33
+ // prettier-ignore
34
+ export type AnyTextChunkDefinition =
35
+ | OutputChunkDefinition
36
+ | ReferenceChunkDefinition
37
+ | StaticTextChunkDefinition
38
+ | TranslationChunkDefinition;
@@ -0,0 +1,71 @@
1
+ import { isElementNode, isTextNode } from '@getodk/common/lib/dom/predicates.ts';
2
+ import type { XFormDefinition } from '../../../XFormDefinition.ts';
3
+ import type { ItemDefinition } from '../../../body/control/select/ItemDefinition.ts';
4
+ import type { ElementTextRole } from '../../../client/TextRange.ts';
5
+ import { parseNodesetReference } from '../../xpath/reference-parsing.ts';
6
+ import type { HintDefinition } from '../HintDefinition.ts';
7
+ import type { ItemLabelDefinition } from '../ItemLabelDefinition.ts';
8
+ import type { ItemsetLabelDefinition } from '../ItemsetLabelDefinition.ts';
9
+ import type { LabelDefinition, LabelOwner } from '../LabelDefinition.ts';
10
+ import { OutputChunkDefinition } from '../OutputChunkDefinition.ts';
11
+ import { ReferenceChunkDefinition } from '../ReferenceChunkDefinition.ts';
12
+ import { StaticTextChunkDefinition } from '../StaticTextChunkDefinition.ts';
13
+ import { TranslationChunkDefinition } from '../TranslationChunkDefinition.ts';
14
+ import type { TextSourceNode } from './TextRangeDefinition.ts';
15
+ import { TextRangeDefinition } from './TextRangeDefinition.ts';
16
+
17
+ // prettier-ignore
18
+ export type RefAttributeChunk =
19
+ | ReferenceChunkDefinition
20
+ | TranslationChunkDefinition;
21
+
22
+ // prettier-ignore
23
+ type TextElementChildChunk =
24
+ | OutputChunkDefinition
25
+ | StaticTextChunkDefinition;
26
+
27
+ // prettier-ignore
28
+ type TextElementChunks =
29
+ | readonly [RefAttributeChunk]
30
+ | readonly TextElementChildChunk[];
31
+
32
+ type TextElementOwner = ItemDefinition | LabelOwner;
33
+
34
+ export abstract class TextElementDefinition<
35
+ Role extends ElementTextRole,
36
+ > extends TextRangeDefinition<Role> {
37
+ readonly chunks: TextElementChunks;
38
+
39
+ constructor(form: XFormDefinition, owner: TextElementOwner, sourceNode: TextSourceNode<Role>) {
40
+ super(form, owner, sourceNode);
41
+
42
+ const context = this as AnyTextElementDefinition;
43
+ const refExpression = parseNodesetReference(owner, sourceNode, 'ref');
44
+
45
+ if (refExpression == null) {
46
+ this.chunks = Array.from(sourceNode.childNodes).flatMap((childNode) => {
47
+ if (isElementNode(childNode)) {
48
+ return OutputChunkDefinition.from(context, childNode) ?? [];
49
+ }
50
+
51
+ if (isTextNode(childNode)) {
52
+ return StaticTextChunkDefinition.from(context, childNode.data);
53
+ }
54
+
55
+ return [];
56
+ });
57
+ } else {
58
+ const refChunk =
59
+ TranslationChunkDefinition.from(context, refExpression) ??
60
+ ReferenceChunkDefinition.from(context, refExpression);
61
+ this.chunks = [refChunk];
62
+ }
63
+ }
64
+ }
65
+
66
+ // prettier-ignore
67
+ export type AnyTextElementDefinition =
68
+ | HintDefinition
69
+ | ItemLabelDefinition
70
+ | ItemsetLabelDefinition
71
+ | LabelDefinition;
@@ -0,0 +1,70 @@
1
+ import type { LocalNamedElement } from '@getodk/common/types/dom.ts';
2
+ import type { XFormDefinition } from '../../../XFormDefinition.ts';
3
+ import type { TextRole } from '../../../client/TextRange.ts';
4
+ import { DependencyContext } from '../../../expression/DependencyContext.ts';
5
+ import type { AnyDependentExpression } from '../../../expression/DependentExpression.ts';
6
+ import type { AnyMessageDefinition } from '../MessageDefinition.ts';
7
+ import type { AnyTextChunkDefinition } from './TextChunkDefinition.ts';
8
+ import type { AnyTextElementDefinition } from './TextElementDefinition.ts';
9
+
10
+ export type TextBindAttributeLocalName = 'constraintMsg' | 'requiredMsg';
11
+ export type TextBodyElementLocalName = 'hint' | 'label';
12
+
13
+ interface TextSourceNodes {
14
+ readonly constraintMsg: null;
15
+ readonly hint: LocalNamedElement<'hint'>;
16
+ readonly label: LocalNamedElement<'label'>;
17
+ readonly 'item-label': LocalNamedElement<'label'>;
18
+ readonly requiredMsg: null;
19
+ }
20
+
21
+ export type TextSourceNode<Type extends TextRole> = TextSourceNodes[Type];
22
+
23
+ export abstract class TextRangeDefinition<Role extends TextRole> extends DependencyContext {
24
+ abstract readonly role: Role;
25
+ readonly parentReference: string | null;
26
+ readonly reference: string | null;
27
+
28
+ abstract readonly chunks: readonly AnyTextChunkDefinition[];
29
+
30
+ override get isTranslated(): boolean {
31
+ return (
32
+ this.ownerContext.isTranslated || this.chunks.some((chunk) => chunk.source === 'translation')
33
+ );
34
+ }
35
+
36
+ override set isTranslated(value: true) {
37
+ if (this.ownerContext != null) {
38
+ this.ownerContext.isTranslated = value;
39
+ }
40
+
41
+ super.isTranslated = value;
42
+ }
43
+
44
+ protected constructor(
45
+ readonly form: XFormDefinition,
46
+ readonly ownerContext: DependencyContext,
47
+ readonly sourceNode: TextSourceNode<Role>
48
+ ) {
49
+ super();
50
+
51
+ this.reference = ownerContext.reference;
52
+ this.parentReference = ownerContext.parentReference;
53
+ }
54
+
55
+ override registerDependentExpression(expression: AnyDependentExpression): void {
56
+ this.ownerContext.registerDependentExpression(expression);
57
+ super.registerDependentExpression(expression);
58
+ }
59
+
60
+ toJSON(): object {
61
+ const { form, ownerContext, ...rest } = this;
62
+
63
+ return rest;
64
+ }
65
+ }
66
+
67
+ // prettier-ignore
68
+ export type AnyTextRangeDefinition =
69
+ | AnyMessageDefinition
70
+ | AnyTextElementDefinition;
@@ -0,0 +1,105 @@
1
+ import { expressionParser } from '@getodk/xpath/expressionParser.js';
2
+ import {
3
+ resolvePath,
4
+ resolvePredicateReference,
5
+ serializeNodesetReference,
6
+ } from './path-resolution.ts';
7
+ import { findPredicateReferences } from './predicate-analysis.ts';
8
+ import type { PathExpressionNode } from './semantic-analysis.ts';
9
+ import { findLocationPathSubExpressionNodes, getPathExpressionNode } from './semantic-analysis.ts';
10
+
11
+ export interface PathResolutionOptions {
12
+ /**
13
+ * Excludes direct references to the nodeset specified as a context. This flag
14
+ * has earlier precedent as `ignoreContextReference` in similar internal APIs.
15
+ * The intent, in part, is to allow for certain in-spec exceptions to cycle
16
+ * analysis (which we previously did explicitly, and foresee reintroducing in
17
+ * some form relatively soon). As such, the default for this flag should be
18
+ * overridden when a computation is (A) not among the core `<bind>`
19
+ * computations or (B) is a computation which is explicitly expected to have
20
+ * self-references (such as `constraint`).
21
+ *
22
+ * @default false
23
+ *
24
+ * @todo It is highly likely we will eliminate this in the future. We
25
+ * considered removing its precursor (and existing code now referencing this)
26
+ * in {@link https://github.com/getodk/web-forms/pull/67}, but ran into issues
27
+ * with some of the reactive logic introduced there. It's likely that
28
+ * subsequent improvements have addressed those issues, but it's also likely
29
+ * that we will want similar special casing when we reintroduce explicit graph
30
+ * cycle analysis.
31
+ */
32
+ readonly ignoreReferenceToContextPath?: boolean;
33
+ }
34
+
35
+ const defaultPathResolutionOptions: Required<PathResolutionOptions> = {
36
+ ignoreReferenceToContextPath: false,
37
+ };
38
+
39
+ /**
40
+ * Resolves XPath paths referenced by an arbitrary expression to XForms
41
+ * `nodeset` references:
42
+ *
43
+ * 1. The provided {@link expression} is analyzed to find path sub-expressions
44
+ * (LocationPaths, sub-expressions with a node-set producing FilterExpr).
45
+ *
46
+ * 2. When a {@link contextNodeset} is available, each sub-expression is
47
+ * resolved to that context. See {@link resolvePath} for additional
48
+ * detail.
49
+ *
50
+ * 3. Once resolved, each dependency path is further analyzed to identify
51
+ * sub-expressions in any of its predicates, resolving each to their
52
+ * respective predicates' Steps. See {@link resolvePredicateReference} for
53
+ * additional detail.
54
+ */
55
+ export const resolveDependencyNodesets = (
56
+ contextNodeset: string | null,
57
+ expression: string,
58
+ options: PathResolutionOptions = {}
59
+ ): readonly string[] => {
60
+ const { ignoreReferenceToContextPath } = {
61
+ ...defaultPathResolutionOptions,
62
+ ...options,
63
+ };
64
+
65
+ let contextNode: PathExpressionNode | null = null;
66
+
67
+ if (contextNodeset != null) {
68
+ contextNode = getPathExpressionNode(contextNodeset);
69
+ }
70
+
71
+ const expressionRootNode = expressionParser.parse(expression).rootNode;
72
+ const subExpressions = findLocationPathSubExpressionNodes(expressionRootNode);
73
+
74
+ const resolvedPathLists = subExpressions.flatMap((subExpression) => {
75
+ const pathNodes = resolvePath(contextNode, subExpression);
76
+ const predicateReferences = findPredicateReferences(pathNodes);
77
+ const resolvedPredicateReferences = predicateReferences.map((reference) => {
78
+ return resolvePredicateReference(
79
+ contextNode,
80
+ reference.stepContextNodes,
81
+ reference.predicatePathNode
82
+ );
83
+ });
84
+
85
+ return [pathNodes, ...resolvedPredicateReferences];
86
+ });
87
+
88
+ const serializedPaths = Array.from(
89
+ new Set(
90
+ resolvedPathLists.map((resolvedPathList) => {
91
+ return serializeNodesetReference(resolvedPathList, {
92
+ stripPredicates: true,
93
+ });
94
+ })
95
+ )
96
+ );
97
+
98
+ if (ignoreReferenceToContextPath) {
99
+ return serializedPaths.filter((result) => {
100
+ return result !== contextNodeset;
101
+ });
102
+ }
103
+
104
+ return serializedPaths;
105
+ };