@getodk/xforms-engine 0.5.0 → 0.6.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 (232) hide show
  1. package/README.md +1 -1
  2. package/dist/client/BaseNode.d.ts +4 -0
  3. package/dist/client/BaseValueNode.d.ts +1 -1
  4. package/dist/client/GroupNode.d.ts +1 -0
  5. package/dist/client/InputNode.d.ts +32 -3
  6. package/dist/client/ModelValueNode.d.ts +1 -0
  7. package/dist/client/NoteNode.d.ts +24 -7
  8. package/dist/client/RangeNode.d.ts +36 -0
  9. package/dist/client/RankNode.d.ts +46 -0
  10. package/dist/client/RootNode.d.ts +6 -3
  11. package/dist/client/SelectNode.d.ts +47 -25
  12. package/dist/client/SubtreeNode.d.ts +1 -0
  13. package/dist/client/TriggerNode.d.ts +10 -6
  14. package/dist/client/hierarchy.d.ts +5 -5
  15. package/dist/client/node-types.d.ts +2 -2
  16. package/dist/client/unsupported/UnsupportedControlNode.d.ts +1 -3
  17. package/dist/error/RankMissingValueError.d.ts +3 -0
  18. package/dist/error/RankValueTypeError.d.ts +6 -0
  19. package/dist/error/SelectValueTypeError.d.ts +15 -0
  20. package/dist/error/XFormsSpecViolationError.d.ts +2 -0
  21. package/dist/index.d.ts +2 -3
  22. package/dist/index.js +5604 -4666
  23. package/dist/index.js.map +1 -1
  24. package/dist/instance/Group.d.ts +1 -0
  25. package/dist/instance/InputControl.d.ts +5 -3
  26. package/dist/instance/ModelValue.d.ts +2 -0
  27. package/dist/instance/Note.d.ts +13 -25
  28. package/dist/instance/PrimaryInstance.d.ts +1 -0
  29. package/dist/instance/RangeControl.d.ts +34 -0
  30. package/dist/instance/RankControl.d.ts +40 -0
  31. package/dist/instance/Root.d.ts +1 -0
  32. package/dist/instance/SelectControl.d.ts +66 -0
  33. package/dist/instance/Subtree.d.ts +1 -0
  34. package/dist/instance/TriggerControl.d.ts +9 -22
  35. package/dist/instance/abstract/DescendantNode.d.ts +1 -2
  36. package/dist/instance/abstract/InstanceNode.d.ts +3 -1
  37. package/dist/instance/abstract/UnsupportedControl.d.ts +1 -0
  38. package/dist/instance/abstract/ValueNode.d.ts +0 -1
  39. package/dist/instance/hierarchy.d.ts +9 -9
  40. package/dist/instance/internal-api/InstanceValueContext.d.ts +2 -0
  41. package/dist/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.d.ts +2 -1
  42. package/dist/instance/internal-api/submission/ClientReactiveSubmittableParentNode.d.ts +2 -1
  43. package/dist/instance/internal-api/submission/ClientReactiveSubmittableValueNode.d.ts +2 -1
  44. package/dist/instance/repeat/BaseRepeatRange.d.ts +1 -0
  45. package/dist/instance/repeat/RepeatInstance.d.ts +1 -0
  46. package/dist/integration/xpath/adapter/names.d.ts +1 -11
  47. package/dist/integration/xpath/adapter/traversal.d.ts +10 -9
  48. package/dist/integration/xpath/static-dom/StaticAttribute.d.ts +10 -3
  49. package/dist/integration/xpath/static-dom/StaticDocument.d.ts +0 -1
  50. package/dist/integration/xpath/static-dom/StaticElement.d.ts +12 -4
  51. package/dist/lib/client-reactivity/submission/createRootSubmissionState.d.ts +3 -0
  52. package/dist/lib/codecs/Geopoint/Geopoint.d.ts +48 -0
  53. package/dist/lib/codecs/Geopoint/GeopointValueCodec.d.ts +5 -0
  54. package/dist/lib/codecs/NoteCodec.d.ts +8 -0
  55. package/dist/lib/codecs/RangeCodec.d.ts +8 -0
  56. package/dist/lib/codecs/TriggerCodec.d.ts +7 -0
  57. package/dist/lib/codecs/ValueArrayCodec.d.ts +11 -0
  58. package/dist/lib/codecs/ValueCodec.d.ts +2 -2
  59. package/dist/lib/codecs/getNoteCodec.d.ts +3 -0
  60. package/dist/lib/codecs/getSelectCodec.d.ts +5 -0
  61. package/dist/lib/codecs/getSharedValueCodec.d.ts +3 -2
  62. package/dist/lib/codecs/items/BaseItemCodec.d.ts +9 -0
  63. package/dist/lib/codecs/items/MultipleValueItemCodec.d.ts +14 -0
  64. package/dist/lib/codecs/items/SingleValueItemCodec.d.ts +24 -0
  65. package/dist/lib/dom/query.d.ts +1 -2
  66. package/dist/lib/names/NamespaceDeclaration.d.ts +45 -0
  67. package/dist/lib/names/NamespaceDeclarationMap.d.ts +137 -0
  68. package/dist/lib/names/NamespaceURL.d.ts +30 -0
  69. package/dist/lib/names/QualifiedName.d.ts +113 -0
  70. package/dist/lib/number-parsers.d.ts +2 -0
  71. package/dist/lib/reactivity/createItemCollection.d.ts +21 -0
  72. package/dist/lib/xml-serialization.d.ts +11 -2
  73. package/dist/parse/XFormDOM.d.ts +1 -1
  74. package/dist/parse/body/BodyDefinition.d.ts +2 -2
  75. package/dist/parse/body/appearance/rangeAppearanceParser.d.ts +3 -0
  76. package/dist/parse/body/control/InputControlDefinition.d.ts +3 -0
  77. package/dist/parse/body/control/ItemDefinition.d.ts +14 -0
  78. package/dist/parse/body/control/ItemsetDefinition.d.ts +18 -0
  79. package/dist/parse/body/control/RangeControlDefinition.d.ts +31 -2
  80. package/dist/parse/body/control/RankControlDefinition.d.ts +7 -3
  81. package/dist/parse/body/control/{select/SelectDefinition.d.ts → SelectControlDefinition.d.ts} +9 -9
  82. package/dist/parse/expression/ItemsetNodesetExpression.d.ts +1 -1
  83. package/dist/parse/expression/ItemsetValueExpression.d.ts +1 -1
  84. package/dist/parse/model/BindDefinition.d.ts +3 -1
  85. package/dist/parse/model/BindPreloadDefinition.d.ts +42 -0
  86. package/dist/parse/model/DescendentNodeDefinition.d.ts +4 -13
  87. package/dist/parse/model/ItextTranslation/ItextTranslationRootDefinition.d.ts +2 -1
  88. package/dist/parse/model/LeafNodeDefinition.d.ts +7 -4
  89. package/dist/parse/model/ModelBindMap.d.ts +1 -1
  90. package/dist/parse/model/NodeDefinition.d.ts +16 -19
  91. package/dist/parse/model/NoteNodeDefinition.d.ts +6 -5
  92. package/dist/parse/model/RangeNodeDefinition.d.ts +41 -0
  93. package/dist/parse/model/RepeatInstanceDefinition.d.ts +7 -4
  94. package/dist/parse/model/RepeatRangeDefinition.d.ts +7 -4
  95. package/dist/parse/model/RepeatTemplateDefinition.d.ts +7 -4
  96. package/dist/parse/model/RootAttributeDefinition.d.ts +24 -0
  97. package/dist/parse/model/RootAttributeMap.d.ts +23 -0
  98. package/dist/parse/model/RootDefinition.d.ts +9 -7
  99. package/dist/parse/model/SubtreeDefinition.d.ts +7 -4
  100. package/dist/parse/shared/parseStaticDocumentFromDOMSubtree.d.ts +2 -3
  101. package/dist/parse/text/ItemLabelDefinition.d.ts +1 -1
  102. package/dist/parse/text/ItemsetLabelDefinition.d.ts +2 -2
  103. package/dist/parse/text/abstract/TextElementDefinition.d.ts +1 -1
  104. package/dist/parse/xpath/semantic-analysis.d.ts +1 -3
  105. package/dist/solid.js +5603 -4665
  106. package/dist/solid.js.map +1 -1
  107. package/package.json +15 -12
  108. package/src/client/BaseNode.ts +5 -0
  109. package/src/client/BaseValueNode.ts +1 -1
  110. package/src/client/GroupNode.ts +1 -0
  111. package/src/client/InputNode.ts +38 -2
  112. package/src/client/ModelValueNode.ts +1 -0
  113. package/src/client/NoteNode.ts +43 -7
  114. package/src/client/RangeNode.ts +51 -0
  115. package/src/client/RankNode.ts +54 -0
  116. package/src/client/RootNode.ts +11 -5
  117. package/src/client/SelectNode.ts +53 -26
  118. package/src/client/SubtreeNode.ts +1 -0
  119. package/src/client/TriggerNode.ts +12 -6
  120. package/src/client/hierarchy.ts +7 -8
  121. package/src/client/node-types.ts +1 -1
  122. package/src/client/unsupported/UnsupportedControlNode.ts +2 -6
  123. package/src/error/RankMissingValueError.ts +5 -0
  124. package/src/error/RankValueTypeError.ts +13 -0
  125. package/src/error/SelectValueTypeError.ts +22 -0
  126. package/src/error/XFormsSpecViolationError.ts +1 -0
  127. package/src/index.ts +2 -12
  128. package/src/instance/Group.ts +1 -0
  129. package/src/instance/InputControl.ts +42 -2
  130. package/src/instance/ModelValue.ts +2 -0
  131. package/src/instance/Note.ts +34 -59
  132. package/src/instance/PrimaryInstance.ts +1 -0
  133. package/src/instance/RangeControl.ts +113 -0
  134. package/src/instance/RankControl.ts +199 -0
  135. package/src/instance/Root.ts +3 -2
  136. package/src/instance/SelectControl.ts +219 -0
  137. package/src/instance/Subtree.ts +1 -0
  138. package/src/instance/TriggerControl.ts +36 -75
  139. package/src/instance/abstract/DescendantNode.ts +1 -6
  140. package/src/instance/abstract/InstanceNode.ts +10 -2
  141. package/src/instance/abstract/UnsupportedControl.ts +1 -0
  142. package/src/instance/abstract/ValueNode.ts +3 -2
  143. package/src/instance/children.ts +71 -30
  144. package/src/instance/hierarchy.ts +21 -16
  145. package/src/instance/internal-api/InstanceValueContext.ts +2 -0
  146. package/src/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.ts +2 -1
  147. package/src/instance/internal-api/submission/ClientReactiveSubmittableParentNode.ts +2 -1
  148. package/src/instance/internal-api/submission/ClientReactiveSubmittableValueNode.ts +2 -1
  149. package/src/instance/repeat/BaseRepeatRange.ts +2 -0
  150. package/src/instance/repeat/RepeatInstance.ts +1 -0
  151. package/src/instance/resource.ts +4 -1
  152. package/src/integration/xpath/adapter/names.ts +66 -17
  153. package/src/integration/xpath/adapter/traversal.ts +10 -9
  154. package/src/integration/xpath/static-dom/StaticAttribute.ts +15 -7
  155. package/src/integration/xpath/static-dom/StaticDocument.ts +0 -2
  156. package/src/integration/xpath/static-dom/StaticElement.ts +21 -8
  157. package/src/lib/client-reactivity/submission/createLeafNodeSubmissionState.ts +1 -1
  158. package/src/lib/client-reactivity/submission/createParentNodeSubmissionState.ts +1 -1
  159. package/src/lib/client-reactivity/submission/createRootSubmissionState.ts +19 -0
  160. package/src/lib/client-reactivity/submission/createValueNodeSubmissionState.ts +2 -2
  161. package/src/lib/codecs/Geopoint/Geopoint.ts +150 -0
  162. package/src/lib/codecs/Geopoint/GeopointValueCodec.ts +20 -0
  163. package/src/lib/codecs/NoteCodec.ts +32 -0
  164. package/src/lib/codecs/RangeCodec.ts +65 -0
  165. package/src/lib/codecs/TriggerCodec.ts +64 -0
  166. package/src/lib/codecs/ValueArrayCodec.ts +42 -0
  167. package/src/lib/codecs/ValueCodec.ts +2 -2
  168. package/src/lib/codecs/getNoteCodec.ts +27 -0
  169. package/src/lib/codecs/getSelectCodec.ts +27 -0
  170. package/src/lib/codecs/getSharedValueCodec.ts +5 -3
  171. package/src/lib/codecs/items/BaseItemCodec.ts +20 -0
  172. package/src/lib/codecs/items/MultipleValueItemCodec.ts +28 -0
  173. package/src/lib/codecs/items/SingleValueItemCodec.ts +67 -0
  174. package/src/lib/dom/query.ts +1 -2
  175. package/src/lib/names/NamespaceDeclaration.ts +106 -0
  176. package/src/lib/names/NamespaceDeclarationMap.ts +228 -0
  177. package/src/lib/names/NamespaceURL.ts +44 -0
  178. package/src/lib/names/QualifiedName.ts +170 -0
  179. package/src/lib/number-parsers.ts +25 -0
  180. package/src/lib/reactivity/createInstanceValueState.ts +50 -0
  181. package/src/lib/reactivity/{createSelectItems.ts → createItemCollection.ts} +41 -36
  182. package/src/lib/xml-serialization.ts +76 -9
  183. package/src/parse/XFormDOM.ts +141 -21
  184. package/src/parse/XFormDefinition.ts +1 -4
  185. package/src/parse/body/BodyDefinition.ts +4 -4
  186. package/src/parse/body/appearance/rangeAppearanceParser.ts +11 -0
  187. package/src/parse/body/control/InputControlDefinition.ts +9 -0
  188. package/src/parse/body/control/{select/ItemDefinition.ts → ItemDefinition.ts} +8 -6
  189. package/src/parse/body/control/{select/ItemsetDefinition.ts → ItemsetDefinition.ts} +11 -9
  190. package/src/parse/body/control/RangeControlDefinition.ts +91 -6
  191. package/src/parse/body/control/RankControlDefinition.ts +25 -7
  192. package/src/parse/body/control/{select/SelectDefinition.ts → SelectControlDefinition.ts} +9 -9
  193. package/src/parse/expression/ItemsetNodesetExpression.ts +1 -1
  194. package/src/parse/expression/ItemsetValueExpression.ts +1 -1
  195. package/src/parse/model/BindDefinition.ts +4 -0
  196. package/src/parse/model/BindPreloadDefinition.ts +100 -0
  197. package/src/parse/model/DescendentNodeDefinition.ts +7 -25
  198. package/src/parse/model/ItextTranslation/ItextTranslationRootDefinition.ts +2 -1
  199. package/src/parse/model/LeafNodeDefinition.ts +11 -4
  200. package/src/parse/model/NodeDefinition.ts +24 -45
  201. package/src/parse/model/NoteNodeDefinition.ts +8 -7
  202. package/src/parse/model/RangeNodeDefinition.ts +118 -0
  203. package/src/parse/model/RepeatInstanceDefinition.ts +11 -7
  204. package/src/parse/model/RepeatRangeDefinition.ts +11 -7
  205. package/src/parse/model/RepeatTemplateDefinition.ts +11 -7
  206. package/src/parse/model/RootAttributeDefinition.ts +45 -0
  207. package/src/parse/model/RootAttributeMap.ts +44 -0
  208. package/src/parse/model/RootDefinition.ts +29 -28
  209. package/src/parse/model/SecondaryInstance/sources/GeoJSONExternalSecondaryInstance.ts +1 -0
  210. package/src/parse/model/SubtreeDefinition.ts +12 -12
  211. package/src/parse/shared/parseStaticDocumentFromDOMSubtree.ts +3 -3
  212. package/src/parse/text/ItemLabelDefinition.ts +1 -1
  213. package/src/parse/text/ItemsetLabelDefinition.ts +2 -2
  214. package/src/parse/text/abstract/TextElementDefinition.ts +1 -1
  215. package/src/parse/xpath/semantic-analysis.ts +4 -3
  216. package/dist/client/unsupported/RangeNode.d.ts +0 -9
  217. package/dist/client/unsupported/RankNode.d.ts +0 -9
  218. package/dist/instance/SelectField.d.ts +0 -58
  219. package/dist/instance/unsupported/RangeControl.d.ts +0 -6
  220. package/dist/instance/unsupported/RankControl.d.ts +0 -6
  221. package/dist/integration/xpath/static-dom/StaticNamedNode.d.ts +0 -17
  222. package/dist/lib/reactivity/createSelectItems.d.ts +0 -16
  223. package/dist/parse/body/control/select/ItemDefinition.d.ts +0 -13
  224. package/dist/parse/body/control/select/ItemsetDefinition.d.ts +0 -17
  225. package/dist/parse/body/control/select/ItemsetNodesetContext.d.ts +0 -9
  226. package/src/client/unsupported/RangeNode.ts +0 -14
  227. package/src/client/unsupported/RankNode.ts +0 -14
  228. package/src/instance/SelectField.ts +0 -263
  229. package/src/instance/unsupported/RangeControl.ts +0 -9
  230. package/src/instance/unsupported/RankControl.ts +0 -9
  231. package/src/integration/xpath/static-dom/StaticNamedNode.ts +0 -45
  232. package/src/parse/body/control/select/ItemsetNodesetContext.ts +0 -21
@@ -0,0 +1,150 @@
1
+ /**
2
+ * This abstract class defines the minimal behavior for a default geopoint.
3
+ * It can be expanded later to support units (e.g., degrees or meters),
4
+ * which would also serve as documentation to clarify what each value represents.
5
+ */
6
+ abstract class SemanticValue<Semantic extends string, Value extends number | null> {
7
+ abstract readonly semantic: Semantic;
8
+
9
+ constructor(readonly value: Value) {}
10
+ }
11
+
12
+ class Latitude extends SemanticValue<'latitude', number> {
13
+ readonly semantic = 'latitude';
14
+ }
15
+
16
+ class Longitude extends SemanticValue<'longitude', number> {
17
+ readonly semantic = 'longitude';
18
+ }
19
+
20
+ class Altitude<Value extends number | null = number> extends SemanticValue<'altitude', Value> {
21
+ readonly semantic = 'altitude';
22
+ }
23
+
24
+ class Accuracy<Value extends number | null = number> extends SemanticValue<'accuracy', Value> {
25
+ readonly semantic = 'accuracy';
26
+ }
27
+
28
+ export interface GeopointValue {
29
+ readonly latitude: number;
30
+ readonly longitude: number;
31
+ readonly altitude: number | null;
32
+ readonly accuracy: number | null;
33
+ }
34
+
35
+ export type GeopointRuntimeValue = GeopointValue | null;
36
+
37
+ // TODO: Add support for GeoJSONValue
38
+ export type GeopointInputValue = GeopointRuntimeValue | string;
39
+
40
+ interface GeopointInternalValue {
41
+ readonly latitude: Latitude;
42
+ readonly longitude: Longitude;
43
+ readonly altitude: Altitude<null> | Altitude<number>;
44
+ readonly accuracy: Accuracy<null> | Accuracy<number>;
45
+ }
46
+
47
+ type GeopointTuple =
48
+ | readonly [
49
+ latitude: Latitude,
50
+ longitude: Longitude,
51
+ altitude: Altitude<null> | Altitude<number>,
52
+ accuracy: Accuracy,
53
+ ]
54
+ | readonly [latitude: Latitude, longitude: Longitude, altitude: Altitude]
55
+ | readonly [latitude: Latitude, longitude: Longitude];
56
+
57
+ const DEGREES_MAX = {
58
+ latitude: 90,
59
+ longitude: 180,
60
+ } as const;
61
+
62
+ type CoordinateType = keyof typeof DEGREES_MAX;
63
+
64
+ export class Geopoint {
65
+ private readonly internalValue: GeopointInternalValue;
66
+
67
+ constructor(coordinates: GeopointValue) {
68
+ const { latitude, longitude, altitude, accuracy } = coordinates;
69
+
70
+ this.internalValue = {
71
+ latitude: new Latitude(latitude),
72
+ longitude: new Longitude(longitude),
73
+ altitude: this.isValidNumber(altitude) ? new Altitude(altitude) : new Altitude(null),
74
+ accuracy: this.isValidNumber(accuracy) ? new Accuracy(accuracy) : new Accuracy(null),
75
+ };
76
+ }
77
+
78
+ getTuple(): GeopointTuple {
79
+ const { latitude, longitude, altitude, accuracy } = this.internalValue;
80
+
81
+ if (accuracy.value != null) {
82
+ return [latitude, longitude, altitude, accuracy];
83
+ }
84
+
85
+ if (altitude.value != null) {
86
+ return [latitude, longitude, altitude];
87
+ }
88
+
89
+ return [latitude, longitude];
90
+ }
91
+
92
+ getRuntimeValue(): GeopointRuntimeValue {
93
+ const { latitude, longitude, altitude, accuracy } = this.internalValue;
94
+ const isLatitude = this.isValidDegrees('latitude', latitude.value);
95
+ const isLongitude = this.isValidDegrees('longitude', longitude.value);
96
+
97
+ if (!isLatitude || !isLongitude || Geopoint.isNullLocation(latitude.value, longitude.value)) {
98
+ return null;
99
+ }
100
+
101
+ return {
102
+ latitude: latitude.value,
103
+ longitude: longitude.value,
104
+ altitude: altitude.value,
105
+ accuracy: accuracy.value,
106
+ };
107
+ }
108
+
109
+ private isValidDegrees(coordinate: CoordinateType, degrees: number): degrees is number {
110
+ return this.isValidNumber(degrees) && Math.abs(degrees) <= DEGREES_MAX[coordinate];
111
+ }
112
+
113
+ private isValidNumber(value: number | null | undefined) {
114
+ return value != null && !Number.isNaN(value);
115
+ }
116
+
117
+ private static isNullLocation(latitude: number, longitude: number) {
118
+ return latitude === 0 && longitude === 0;
119
+ }
120
+
121
+ static parseString(value: string): GeopointRuntimeValue {
122
+ if (value.trim() === '') {
123
+ return null;
124
+ }
125
+
126
+ const [latitude, longitude, altitude = null, accuracy = null] = value.split(/\s+/).map(Number);
127
+
128
+ if (latitude == null || longitude == null || Number.isNaN(altitude) || Number.isNaN(accuracy)) {
129
+ return null;
130
+ }
131
+
132
+ return new this({ latitude, longitude, altitude, accuracy }).getRuntimeValue();
133
+ }
134
+
135
+ static toCoordinatesString(value: GeopointInputValue): string {
136
+ const decodedValue = typeof value === 'string' ? Geopoint.parseString(value) : value;
137
+
138
+ if (
139
+ decodedValue == null ||
140
+ Geopoint.isNullLocation(decodedValue.latitude, decodedValue.longitude)
141
+ ) {
142
+ return '';
143
+ }
144
+
145
+ return new this(decodedValue)
146
+ .getTuple()
147
+ .map((item) => item.value ?? 0)
148
+ .join(' ');
149
+ }
150
+ }
@@ -0,0 +1,20 @@
1
+ import { type CodecDecoder, type CodecEncoder, ValueCodec } from '../ValueCodec.ts';
2
+ import { Geopoint, type GeopointInputValue, type GeopointRuntimeValue } from './Geopoint.ts';
3
+
4
+ export class GeopointValueCodec extends ValueCodec<
5
+ 'geopoint',
6
+ GeopointRuntimeValue,
7
+ GeopointInputValue
8
+ > {
9
+ constructor() {
10
+ const encodeValue: CodecEncoder<GeopointInputValue> = (value) => {
11
+ return Geopoint.toCoordinatesString(value);
12
+ };
13
+
14
+ const decodeValue: CodecDecoder<GeopointRuntimeValue> = (value: string) => {
15
+ return Geopoint.parseString(value);
16
+ };
17
+
18
+ super('geopoint', encodeValue, decodeValue);
19
+ }
20
+ }
@@ -0,0 +1,32 @@
1
+ import type { ValueType } from '../../client/ValueType.ts';
2
+ import type { RuntimeInputValue, RuntimeValue, SharedValueCodec } from './getSharedValueCodec.ts';
3
+ import { ValueCodec } from './ValueCodec.ts';
4
+
5
+ // prettier-ignore
6
+ export type NoteRuntimeValue<V extends ValueType> =
7
+ | RuntimeValue<V>
8
+ | null;
9
+
10
+ // prettier-ignore
11
+ export type NoteInputValue<V extends ValueType> =
12
+ | RuntimeInputValue<V>
13
+ | RuntimeValue<V>
14
+ | null;
15
+
16
+ export class NoteCodec<V extends ValueType> extends ValueCodec<
17
+ V,
18
+ NoteRuntimeValue<V>,
19
+ NoteInputValue<V>
20
+ > {
21
+ constructor(baseCodec: SharedValueCodec<V>) {
22
+ const encodeValue = (value: NoteInputValue<V>): string => {
23
+ return baseCodec.encodeValue(value ?? '');
24
+ };
25
+
26
+ const decodeValue = (value: string): NoteRuntimeValue<V> => {
27
+ return value === '' ? null : baseCodec.decodeValue(value);
28
+ };
29
+
30
+ super(baseCodec.valueType, encodeValue, decodeValue);
31
+ }
32
+ }
@@ -0,0 +1,65 @@
1
+ import type { RangeValue } from '../../client/RangeNode.ts';
2
+ import { ValueTypeInvariantError } from '../../error/ValueTypeInvariantError.ts';
3
+ import type { RangeNodeDefinition, RangeValueType } from '../../parse/model/RangeNodeDefinition.ts';
4
+ import type { RuntimeInputValue, RuntimeValue, SharedValueCodec } from './getSharedValueCodec.ts';
5
+ import { ValueCodec } from './ValueCodec.ts';
6
+
7
+ export type RangeRuntimeValue<V extends RangeValueType> = RuntimeValue<V>;
8
+
9
+ export type RangeInputValue<V extends RangeValueType> = RuntimeInputValue<V>;
10
+
11
+ export class RangeCodec<V extends RangeValueType> extends ValueCodec<
12
+ V,
13
+ RangeRuntimeValue<V>,
14
+ RangeInputValue<V>
15
+ > {
16
+ constructor(baseCodec: SharedValueCodec<V>, definition: RangeNodeDefinition<V>) {
17
+ const { valueType, bounds } = definition;
18
+ const { min, max } = bounds;
19
+
20
+ type ComparableRangeValue = bigint | number | null;
21
+
22
+ const toComparableValue = (value: RangeInputValue<V>): ComparableRangeValue => {
23
+ if (value == null) {
24
+ return value;
25
+ }
26
+
27
+ if (typeof value === 'string') {
28
+ return baseCodec.decodeValue(value);
29
+ }
30
+
31
+ return value;
32
+ };
33
+
34
+ const assertBounds = (value: RuntimeInputValue<V>) => {
35
+ const comparableValue = toComparableValue(value);
36
+
37
+ if (comparableValue == null) {
38
+ return;
39
+ }
40
+
41
+ if (comparableValue < min || comparableValue > max) {
42
+ throw new ValueTypeInvariantError(
43
+ valueType,
44
+ `Expected value to be within bounds [${min}, ${max}]. Got: ${value}`
45
+ );
46
+ }
47
+ };
48
+
49
+ const encodeValue = (value: RangeInputValue<V>): string => {
50
+ assertBounds(value);
51
+
52
+ return baseCodec.encodeValue(value);
53
+ };
54
+
55
+ const decodeValue = (value: string): RangeValue<V> => {
56
+ const decoded = baseCodec.decodeValue(value);
57
+
58
+ assertBounds(value);
59
+
60
+ return decoded;
61
+ };
62
+
63
+ super(valueType, encodeValue, decodeValue);
64
+ }
65
+ }
@@ -0,0 +1,64 @@
1
+ import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
2
+ import { ErrorProductionDesignPendingError } from '../../error/ErrorProductionDesignPendingError.ts';
3
+ import { ValueCodec } from './ValueCodec.ts';
4
+
5
+ export type TriggerValueType = 'string';
6
+
7
+ export type TriggerRuntimeValue = boolean;
8
+
9
+ export type TriggerInputValue = boolean | '' | null;
10
+
11
+ type BaseTriggerInstanceValueMapping = Readonly<Record<string, string>>;
12
+
13
+ const TRIGGER_INSTANCE_VALUES = {
14
+ true: 'OK',
15
+ false: '',
16
+ } as const satisfies BaseTriggerInstanceValueMapping;
17
+
18
+ type TriggerInstanceValueMapping = typeof TRIGGER_INSTANCE_VALUES;
19
+
20
+ type TriggerInstanceValue = TriggerInstanceValueMapping[keyof TriggerInstanceValueMapping];
21
+
22
+ const encodeTriggerValue = (value: TriggerInputValue): TriggerInstanceValue => {
23
+ switch (value) {
24
+ case true:
25
+ return TRIGGER_INSTANCE_VALUES.true;
26
+
27
+ case false:
28
+ case '':
29
+ case null:
30
+ return TRIGGER_INSTANCE_VALUES.false;
31
+
32
+ default:
33
+ throw new UnreachableError(value);
34
+ }
35
+ };
36
+
37
+ const decodeTriggerValue = (value: string): TriggerRuntimeValue => {
38
+ if (value === 'OK') {
39
+ return true;
40
+ }
41
+
42
+ if (value === '') {
43
+ return false;
44
+ }
45
+
46
+ if (value.toLowerCase() === 'ok') {
47
+ // eslint-disable-next-line no-console
48
+ console.warn('Treating trigger value as case-insensitive:', value);
49
+
50
+ return true;
51
+ }
52
+
53
+ throw new ErrorProductionDesignPendingError(`Unexpected trigger value: ${value}`);
54
+ };
55
+
56
+ export class TriggerCodec extends ValueCodec<
57
+ TriggerValueType,
58
+ TriggerRuntimeValue,
59
+ TriggerInputValue
60
+ > {
61
+ constructor() {
62
+ super('string', encodeTriggerValue, decodeTriggerValue);
63
+ }
64
+ }
@@ -0,0 +1,42 @@
1
+ import type { ValueType } from '../../client/ValueType.ts';
2
+ import { ErrorProductionDesignPendingError } from '../../error/ErrorProductionDesignPendingError.ts';
3
+ import type { RuntimeValue, SharedValueCodec } from './getSharedValueCodec.ts';
4
+ import type { CodecDecoder, CodecEncoder } from './ValueCodec.ts';
5
+ import { ValueCodec } from './ValueCodec.ts';
6
+
7
+ export type SplitInstanceValues = (value: string) => readonly string[];
8
+
9
+ export type JoinInstnaceValues = (values: readonly string[]) => string;
10
+
11
+ export type RuntimeItemValue<V extends ValueType> = NonNullable<RuntimeValue<V>>;
12
+
13
+ export type RuntimeValues<V extends ValueType> = ReadonlyArray<RuntimeItemValue<V>>;
14
+
15
+ export abstract class ValueArrayCodec<
16
+ V extends ValueType,
17
+ Values extends RuntimeValues<V> = RuntimeValues<V>,
18
+ > extends ValueCodec<V, Values, Values> {
19
+ readonly decodeItemValue: CodecDecoder<RuntimeItemValue<V>>;
20
+
21
+ constructor(
22
+ baseCodec: SharedValueCodec<V>,
23
+ encodeValue: CodecEncoder<Values>,
24
+ decodeValue: CodecDecoder<Values>
25
+ ) {
26
+ const decodeItemValue: CodecDecoder<RuntimeItemValue<V>> = (value) => {
27
+ const decoded = baseCodec.decodeValue(value);
28
+
29
+ if (decoded == null) {
30
+ throw new ErrorProductionDesignPendingError(
31
+ `Failed to decode item value: ${JSON.stringify(value)}`
32
+ );
33
+ }
34
+
35
+ return decoded;
36
+ };
37
+
38
+ super(baseCodec.valueType, encodeValue, decodeValue);
39
+
40
+ this.decodeItemValue = decodeItemValue;
41
+ }
42
+ }
@@ -3,9 +3,9 @@ import type { ValueType } from '../../client/ValueType.ts';
3
3
  import type { DecodeInstanceValue } from '../../instance/internal-api/InstanceValueContext.ts';
4
4
  import type { SimpleAtomicState } from '../reactivity/types.ts';
5
5
 
6
- type CodecEncoder<RuntimeInputValue> = (input: RuntimeInputValue) => string;
6
+ export type CodecEncoder<RuntimeInputValue> = (input: RuntimeInputValue) => string;
7
7
 
8
- type CodecDecoder<RuntimeValue> = (value: string) => RuntimeValue;
8
+ export type CodecDecoder<RuntimeValue> = (value: string) => RuntimeValue;
9
9
 
10
10
  type RuntimeValueAccessor<RuntimeValue> = Accessor<RuntimeValue>;
11
11
 
@@ -0,0 +1,27 @@
1
+ import type { ValueType } from '../../client/ValueType.ts';
2
+ import { sharedValueCodecs } from './getSharedValueCodec.ts';
3
+ import { NoteCodec } from './NoteCodec.ts';
4
+
5
+ type NoteCodecs = {
6
+ readonly [V in ValueType]: NoteCodec<V>;
7
+ };
8
+
9
+ const noteCodecs: NoteCodecs = {
10
+ string: new NoteCodec(sharedValueCodecs.string),
11
+ int: new NoteCodec(sharedValueCodecs.int),
12
+ decimal: new NoteCodec(sharedValueCodecs.decimal),
13
+ boolean: new NoteCodec(sharedValueCodecs.boolean),
14
+ date: new NoteCodec(sharedValueCodecs.date),
15
+ time: new NoteCodec(sharedValueCodecs.time),
16
+ dateTime: new NoteCodec(sharedValueCodecs.dateTime),
17
+ geopoint: new NoteCodec(sharedValueCodecs.geopoint),
18
+ geotrace: new NoteCodec(sharedValueCodecs.geotrace),
19
+ geoshape: new NoteCodec(sharedValueCodecs.geoshape),
20
+ binary: new NoteCodec(sharedValueCodecs.binary),
21
+ barcode: new NoteCodec(sharedValueCodecs.barcode),
22
+ intent: new NoteCodec(sharedValueCodecs.intent),
23
+ };
24
+
25
+ export const getNoteCodec = <V extends ValueType>(valueType: V): NoteCodec<V> => {
26
+ return noteCodecs[valueType];
27
+ };
@@ -0,0 +1,27 @@
1
+ import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
2
+ import type { SelectDefinition } from '../../client/SelectNode.ts';
3
+ import { sharedValueCodecs } from './getSharedValueCodec.ts';
4
+ import { MultipleValueItemCodec } from './items/MultipleValueItemCodec.ts';
5
+ import { SingleValueItemCodec } from './items/SingleValueItemCodec.ts';
6
+
7
+ const multipleValueSelectCodec = new MultipleValueItemCodec(sharedValueCodecs.string);
8
+
9
+ const singleValueSelectCodec = new SingleValueItemCodec(sharedValueCodecs.string);
10
+
11
+ // prettier-ignore
12
+ export type SelectCodec =
13
+ | MultipleValueItemCodec
14
+ | SingleValueItemCodec;
15
+
16
+ export const getSelectCodec = (definition: SelectDefinition<'string'>): SelectCodec => {
17
+ switch (definition.bodyElement.type) {
18
+ case 'select':
19
+ return multipleValueSelectCodec;
20
+
21
+ case 'select1':
22
+ return singleValueSelectCodec;
23
+
24
+ default:
25
+ throw new UnreachableError(definition.bodyElement.type);
26
+ }
27
+ };
@@ -4,6 +4,8 @@ import {
4
4
  type DecimalInputValue,
5
5
  type DecimalRuntimeValue,
6
6
  } from './DecimalValueCodec.ts';
7
+ import type { GeopointInputValue, GeopointRuntimeValue } from './Geopoint/Geopoint.ts';
8
+ import { GeopointValueCodec } from './Geopoint/GeopointValueCodec.ts';
7
9
  import { IntValueCodec, type IntInputValue, type IntRuntimeValue } from './IntValueCodec.ts';
8
10
  import { StringValueCodec } from './StringValueCodec.ts';
9
11
  import type { ValueCodec } from './ValueCodec.ts';
@@ -17,7 +19,7 @@ interface RuntimeValuesByType {
17
19
  readonly date: string;
18
20
  readonly time: string;
19
21
  readonly dateTime: string;
20
- readonly geopoint: string;
22
+ readonly geopoint: GeopointRuntimeValue;
21
23
  readonly geotrace: string;
22
24
  readonly geoshape: string;
23
25
  readonly binary: string;
@@ -35,7 +37,7 @@ interface RuntimeInputValuesByType {
35
37
  readonly date: string;
36
38
  readonly time: string;
37
39
  readonly dateTime: string;
38
- readonly geopoint: string;
40
+ readonly geopoint: GeopointInputValue;
39
41
  readonly geotrace: string;
40
42
  readonly geoshape: string;
41
43
  readonly binary: string;
@@ -64,7 +66,7 @@ export const sharedValueCodecs: SharedValueCodecs = {
64
66
  date: new ValueTypePlaceholderCodec('date'),
65
67
  time: new ValueTypePlaceholderCodec('time'),
66
68
  dateTime: new ValueTypePlaceholderCodec('dateTime'),
67
- geopoint: new ValueTypePlaceholderCodec('geopoint'),
69
+ geopoint: new GeopointValueCodec(),
68
70
  geotrace: new ValueTypePlaceholderCodec('geotrace'),
69
71
  geoshape: new ValueTypePlaceholderCodec('geoshape'),
70
72
  binary: new ValueTypePlaceholderCodec('binary'),
@@ -0,0 +1,20 @@
1
+ import type { ValueType } from '../../../client/ValueType.ts';
2
+ import type { SharedValueCodec } from '../getSharedValueCodec.ts';
3
+ import { ValueArrayCodec } from '../ValueArrayCodec.ts';
4
+ import type { CodecDecoder, CodecEncoder } from '../ValueCodec.ts';
5
+
6
+ export type BaseItemValueType = 'string';
7
+
8
+ export type UnsupportedBaseItemValueType = Exclude<ValueType, BaseItemValueType>;
9
+
10
+ export abstract class BaseItemCodec<
11
+ Values extends readonly string[] = readonly string[],
12
+ > extends ValueArrayCodec<BaseItemValueType, Values> {
13
+ constructor(
14
+ baseCodec: SharedValueCodec<'string'>,
15
+ encodeValue: CodecEncoder<Values>,
16
+ decodeValue: CodecDecoder<Values>
17
+ ) {
18
+ super(baseCodec, encodeValue, decodeValue);
19
+ }
20
+ }
@@ -0,0 +1,28 @@
1
+ import { xmlXPathWhitespaceSeparatedList } from '@getodk/common/lib/string/whitespace.ts';
2
+ import type { SharedValueCodec } from '../getSharedValueCodec.ts';
3
+ import type { CodecDecoder, CodecEncoder } from '../ValueCodec.ts';
4
+ import { BaseItemCodec } from './BaseItemCodec.ts';
5
+
6
+ /**
7
+ * Value codec implementation for `<select>` and `<odk:rank>` controls.
8
+ *
9
+ * This generalizes the application of a {@link SharedValueCodec} implementation
10
+ * over individual select and rank values, where those values are serialized as a
11
+ * whitespace-separated list. All other encoding and decoding logic is deferred
12
+ * to the provided {@link baseCodec}, ensuring that select and rank value types are
13
+ * treated consistently with the same underlying data types for other controls.
14
+ */
15
+ export class MultipleValueItemCodec extends BaseItemCodec<readonly string[]> {
16
+ constructor(baseCodec: SharedValueCodec<'string'>) {
17
+ const encodeValue: CodecEncoder<readonly string[]> = (value) => {
18
+ return value.join(' ');
19
+ };
20
+ const decodeValue: CodecDecoder<readonly string[]> = (value) => {
21
+ return xmlXPathWhitespaceSeparatedList(value, {
22
+ ignoreEmpty: true,
23
+ });
24
+ };
25
+
26
+ super(baseCodec, encodeValue, decodeValue);
27
+ }
28
+ }
@@ -0,0 +1,67 @@
1
+ import type { SharedValueCodec } from '../getSharedValueCodec.ts';
2
+ import { type CodecDecoder, type CodecEncoder } from '../ValueCodec.ts';
3
+ import { BaseItemCodec } from './BaseItemCodec.ts';
4
+ import type { MultipleValueItemCodec } from './MultipleValueItemCodec.ts';
5
+
6
+ // prettier-ignore
7
+ export type SingleValueSelectRuntimeValues =
8
+ | readonly []
9
+ | readonly [string];
10
+
11
+ /**
12
+ * @see {@link encodeValueFactory}
13
+ */
14
+ // prettier-ignore
15
+ type SingleValueSelectCodecValues =
16
+ | SingleValueSelectRuntimeValues
17
+ | readonly string[];
18
+
19
+ /**
20
+ * @todo This is more permissive than it should be, allowing an array of any
21
+ * length. It's not clear whether a runtime check **MUST** happen here, but
22
+ * if we identify bugs where `<select1>` controls are somehow allowing more
23
+ * than one value to be set, this is where we'd start looking. The check is
24
+ * skipped for now, to reduce performance overhead.
25
+ */
26
+ const encodeValueFactory = (
27
+ baseCodec: SharedValueCodec<'string'>
28
+ ): CodecEncoder<SingleValueSelectCodecValues> => {
29
+ return (values) => {
30
+ const [value] = values;
31
+
32
+ if (value == null) {
33
+ return '';
34
+ }
35
+
36
+ return baseCodec.encodeValue(value);
37
+ };
38
+ };
39
+
40
+ /**
41
+ * Value codec implementation for `<select1>` controls.
42
+ *
43
+ * Note: this implementation is a specialization of the same principles
44
+ * underlying {@link MultipleValueItemCodec}. It is implemented separately:
45
+ *
46
+ * 1. to address a semantic difference between `<select>` and `<select1>`
47
+ * values: the former are serialized as a space-separated list, but that does
48
+ * not apply to the latter;
49
+ *
50
+ * 2. as an optimization, as the more general implementation performs poorly on
51
+ * forms which we monitor for performance.
52
+ */
53
+ export class SingleValueItemCodec extends BaseItemCodec<SingleValueSelectCodecValues> {
54
+ constructor(baseCodec: SharedValueCodec<'string'>) {
55
+ const encodeValue = encodeValueFactory(baseCodec);
56
+
57
+ const decodeValue: CodecDecoder<SingleValueSelectRuntimeValues> = (value) => {
58
+ if (value == null) {
59
+ return [];
60
+ }
61
+
62
+ return [value];
63
+ };
64
+
65
+ super(baseCodec, encodeValue, decodeValue);
66
+ }
67
+ }
@@ -3,7 +3,6 @@ import type {
3
3
  KnownAttributeLocalNamedElement,
4
4
  LocalNamedElement,
5
5
  } from '@getodk/common/types/dom.ts';
6
- import type { SelectElement } from '../../parse/body/control/select/SelectDefinition';
7
6
 
8
7
  const hintLookup = new ScopedElementLookup(':scope > hint', 'hint');
9
8
  const itemLookup = new ScopedElementLookup(':scope > item', 'item');
@@ -38,7 +37,7 @@ export const getHintElement = (parent: Element): HintElement | null => {
38
37
  return hintLookup.getElement<HintElement>(parent);
39
38
  };
40
39
 
41
- export const getItemElements = (parent: SelectElement): readonly ItemElement[] => {
40
+ export const getItemElements = (parent: Element): readonly ItemElement[] => {
42
41
  return Array.from(itemLookup.getElements<ItemElement>(parent));
43
42
  };
44
43