@getodk/xforms-engine 0.4.0 → 0.5.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 (285) hide show
  1. package/dist/client/BaseNode.d.ts +14 -3
  2. package/dist/client/BaseValueNode.d.ts +32 -0
  3. package/dist/client/EngineConfig.d.ts +49 -48
  4. package/dist/client/InputNode.d.ts +53 -0
  5. package/dist/client/ModelValueNode.d.ts +12 -7
  6. package/dist/client/NoteNode.d.ts +2 -2
  7. package/dist/client/RootNode.d.ts +34 -7
  8. package/dist/client/SelectNode.d.ts +2 -2
  9. package/dist/client/ValueType.d.ts +3 -0
  10. package/dist/client/constants.d.ts +50 -0
  11. package/dist/client/hierarchy.d.ts +4 -4
  12. package/dist/client/identity.d.ts +14 -0
  13. package/dist/client/node-types.d.ts +2 -1
  14. package/dist/client/resources.d.ts +93 -0
  15. package/dist/client/submission/SubmissionData.d.ts +7 -0
  16. package/dist/client/submission/SubmissionDefinition.d.ts +14 -0
  17. package/dist/client/submission/SubmissionInstanceFile.d.ts +6 -0
  18. package/dist/client/submission/SubmissionOptions.d.ts +23 -0
  19. package/dist/client/submission/SubmissionResult.d.ts +91 -0
  20. package/dist/client/submission/SubmissionState.d.ts +12 -0
  21. package/dist/client/validation.d.ts +2 -2
  22. package/dist/error/ErrorProductionDesignPendingError.d.ts +7 -0
  23. package/dist/error/ValueTypeInvariantError.d.ts +17 -0
  24. package/dist/error/XPathFunctionalityError.d.ts +14 -0
  25. package/dist/error/XPathFunctionalityNotSupportedError.d.ts +7 -0
  26. package/dist/error/XPathFunctionalityPendingError.d.ts +7 -0
  27. package/dist/index.d.ts +10 -1
  28. package/dist/index.js +10856 -8585
  29. package/dist/index.js.map +1 -1
  30. package/dist/instance/Group.d.ts +9 -4
  31. package/dist/instance/InputControl.d.ts +36 -0
  32. package/dist/instance/ModelValue.d.ts +16 -26
  33. package/dist/instance/Note.d.ts +10 -3
  34. package/dist/instance/PrimaryInstance.d.ts +84 -0
  35. package/dist/instance/Root.d.ts +29 -25
  36. package/dist/instance/SelectField.d.ts +10 -2
  37. package/dist/instance/Subtree.d.ts +10 -5
  38. package/dist/instance/TriggerControl.d.ts +10 -3
  39. package/dist/instance/abstract/DescendantNode.d.ts +31 -23
  40. package/dist/instance/abstract/InstanceNode.d.ts +49 -29
  41. package/dist/instance/abstract/UnsupportedControl.d.ts +10 -3
  42. package/dist/instance/abstract/ValueNode.d.ts +48 -0
  43. package/dist/instance/hierarchy.d.ts +9 -8
  44. package/dist/instance/identity.d.ts +2 -7
  45. package/dist/instance/internal-api/EvaluationContext.d.ts +53 -12
  46. package/dist/instance/internal-api/InstanceConfig.d.ts +8 -2
  47. package/dist/instance/internal-api/InstanceValueContext.d.ts +20 -0
  48. package/dist/instance/internal-api/PrimaryInstanceDocument.d.ts +36 -0
  49. package/dist/instance/internal-api/TranslationContext.d.ts +3 -2
  50. package/dist/instance/internal-api/ValidationContext.d.ts +3 -4
  51. package/dist/instance/internal-api/ValueContext.d.ts +2 -1
  52. package/dist/instance/internal-api/submission/ClientReactiveSubmittableInstance.d.ts +14 -0
  53. package/dist/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.d.ts +31 -0
  54. package/dist/instance/internal-api/submission/ClientReactiveSubmittableParentNode.d.ts +18 -0
  55. package/dist/instance/internal-api/submission/ClientReactiveSubmittableValueNode.d.ts +17 -0
  56. package/dist/instance/repeat/BaseRepeatRange.d.ts +39 -83
  57. package/dist/instance/repeat/RepeatInstance.d.ts +11 -29
  58. package/dist/instance/repeat/RepeatRangeControlled.d.ts +2 -2
  59. package/dist/instance/repeat/RepeatRangeUncontrolled.d.ts +2 -2
  60. package/dist/instance/resource.d.ts +1 -1
  61. package/dist/instance/text/TextRange.d.ts +1 -1
  62. package/dist/instance/unsupported/RangeControl.d.ts +3 -1
  63. package/dist/instance/unsupported/RankControl.d.ts +3 -1
  64. package/dist/instance/unsupported/UploadControl.d.ts +3 -1
  65. package/dist/integration/xpath/EngineXPathEvaluator.d.ts +14 -0
  66. package/dist/integration/xpath/adapter/XFormsXPathNode.d.ts +71 -0
  67. package/dist/integration/xpath/adapter/engineDOMAdapter.d.ts +5 -0
  68. package/dist/integration/xpath/adapter/kind.d.ts +32 -0
  69. package/dist/integration/xpath/adapter/names.d.ts +16 -0
  70. package/dist/integration/xpath/adapter/traversal.d.ts +25 -0
  71. package/dist/integration/xpath/adapter/values.d.ts +2 -0
  72. package/dist/integration/xpath/static-dom/StaticAttribute.d.ts +19 -0
  73. package/dist/integration/xpath/static-dom/StaticDocument.d.ts +16 -0
  74. package/dist/integration/xpath/static-dom/StaticElement.d.ts +40 -0
  75. package/dist/integration/xpath/static-dom/StaticNamedNode.d.ts +17 -0
  76. package/dist/integration/xpath/static-dom/StaticNode.d.ts +33 -0
  77. package/dist/integration/xpath/static-dom/StaticText.d.ts +16 -0
  78. package/dist/lib/client-reactivity/submission/createInstanceSubmissionState.d.ts +3 -0
  79. package/dist/lib/client-reactivity/submission/createLeafNodeSubmissionState.d.ts +3 -0
  80. package/dist/lib/client-reactivity/submission/createNodeRangeSubmissionState.d.ts +4 -0
  81. package/dist/lib/client-reactivity/submission/createParentNodeSubmissionState.d.ts +4 -0
  82. package/dist/lib/client-reactivity/submission/createValueNodeSubmissionState.d.ts +3 -0
  83. package/dist/lib/client-reactivity/submission/prepareSubmission.d.ts +8 -0
  84. package/dist/lib/codecs/DecimalValueCodec.d.ts +6 -0
  85. package/dist/lib/codecs/IntValueCodec.d.ts +6 -0
  86. package/dist/lib/codecs/StringValueCodec.d.ts +4 -0
  87. package/dist/lib/codecs/ValueCodec.d.ts +30 -0
  88. package/dist/lib/codecs/ValueTypePlaceholderCodec.d.ts +12 -0
  89. package/dist/lib/codecs/getSharedValueCodec.d.ts +47 -0
  90. package/dist/lib/dom/query.d.ts +3 -0
  91. package/dist/lib/reactivity/createChildrenState.d.ts +7 -5
  92. package/dist/lib/reactivity/createComputedExpression.d.ts +18 -5
  93. package/dist/lib/reactivity/createInstanceValueState.d.ts +40 -0
  94. package/dist/lib/reactivity/createNoteReadonlyThunk.d.ts +2 -2
  95. package/dist/lib/reactivity/createTranslationState.d.ts +19 -0
  96. package/dist/lib/reactivity/createValueState.d.ts +6 -10
  97. package/dist/lib/reactivity/materializeCurrentStateChildren.d.ts +4 -4
  98. package/dist/lib/xml-serialization.d.ts +41 -0
  99. package/dist/parse/XFormDOM.d.ts +18 -7
  100. package/dist/parse/XFormDefinition.d.ts +1 -2
  101. package/dist/parse/attachments/FormAttachmentResource.d.ts +27 -0
  102. package/dist/parse/body/BodyDefinition.d.ts +3 -3
  103. package/dist/parse/body/RepeatElementDefinition.d.ts +1 -1
  104. package/dist/parse/body/appearance/inputAppearanceParser.d.ts +1 -1
  105. package/dist/parse/body/control/{InputDefinition.d.ts → InputControlDefinition.d.ts} +1 -1
  106. package/dist/parse/body/control/select/ItemsetNodesetContext.d.ts +0 -2
  107. package/dist/parse/expression/abstract/DependencyContext.d.ts +0 -5
  108. package/dist/parse/expression/abstract/DependentExpression.d.ts +2 -3
  109. package/dist/parse/model/BindDefinition.d.ts +4 -5
  110. package/dist/parse/model/BindTypeDefinition.d.ts +20 -0
  111. package/dist/parse/model/DescendentNodeDefinition.d.ts +0 -1
  112. package/dist/parse/model/FormSubmissionDefinition.d.ts +8 -0
  113. package/dist/parse/model/ItextTranslation/ItextTranslationDefinition.d.ts +4 -0
  114. package/dist/parse/model/ItextTranslation/ItextTranslationRootDefinition.d.ts +8 -0
  115. package/dist/parse/model/ItextTranslation/ItextTranslationsDefinition.d.ts +8 -0
  116. package/dist/parse/model/LeafNodeDefinition.d.ts +3 -1
  117. package/dist/parse/model/ModelBindMap.d.ts +1 -1
  118. package/dist/parse/model/ModelDefinition.d.ts +2 -0
  119. package/dist/parse/model/NodeDefinition.d.ts +0 -1
  120. package/dist/parse/model/NoteNodeDefinition.d.ts +4 -4
  121. package/dist/parse/model/RootDefinition.d.ts +3 -2
  122. package/dist/parse/model/SecondaryInstance/SecondaryInstanceDefinition.d.ts +4 -0
  123. package/dist/parse/model/SecondaryInstance/SecondaryInstanceRootDefinition.d.ts +7 -0
  124. package/dist/parse/model/SecondaryInstance/SecondaryInstancesDefinition.d.ts +13 -0
  125. package/dist/parse/model/SecondaryInstance/sources/BlankSecondaryInstanceSource.d.ts +14 -0
  126. package/dist/parse/model/SecondaryInstance/sources/CSVExternalSecondaryInstance.d.ts +22 -0
  127. package/dist/parse/model/SecondaryInstance/sources/ExternalSecondaryInstanceResource.d.ts +23 -0
  128. package/dist/parse/model/SecondaryInstance/sources/ExternalSecondaryInstanceSource.d.ts +9 -0
  129. package/dist/parse/model/SecondaryInstance/sources/GeoJSONExternalSecondaryInstance.d.ts +5 -0
  130. package/dist/parse/model/SecondaryInstance/sources/InternalSecondaryInstanceSource.d.ts +7 -0
  131. package/dist/parse/model/SecondaryInstance/sources/SecondaryInstanceSource.d.ts +13 -0
  132. package/dist/parse/model/SecondaryInstance/sources/XMLExternalSecondaryInstanceSource.d.ts +12 -0
  133. package/dist/parse/shared/parseStaticDocumentFromDOMSubtree.d.ts +24 -0
  134. package/dist/parse/text/abstract/TextRangeDefinition.d.ts +0 -2
  135. package/dist/parse/xpath/semantic-analysis.d.ts +5 -0
  136. package/dist/solid.js +8174 -5921
  137. package/dist/solid.js.map +1 -1
  138. package/package.json +13 -11
  139. package/src/client/BaseNode.ts +16 -6
  140. package/src/client/BaseValueNode.ts +35 -0
  141. package/src/client/EngineConfig.ts +52 -51
  142. package/src/client/InputNode.ts +77 -0
  143. package/src/client/ModelValueNode.ts +30 -7
  144. package/src/client/NoteNode.ts +2 -2
  145. package/src/client/RootNode.ts +37 -7
  146. package/src/client/SelectNode.ts +3 -3
  147. package/src/client/ValueType.ts +4 -0
  148. package/src/client/constants.ts +67 -0
  149. package/src/client/hierarchy.ts +5 -5
  150. package/src/client/identity.ts +16 -0
  151. package/src/client/node-types.ts +12 -6
  152. package/src/client/resources.ts +118 -0
  153. package/src/client/submission/SubmissionData.ts +12 -0
  154. package/src/client/submission/SubmissionDefinition.ts +16 -0
  155. package/src/client/submission/SubmissionInstanceFile.ts +9 -0
  156. package/src/client/submission/SubmissionOptions.ts +28 -0
  157. package/src/client/submission/SubmissionResult.ts +124 -0
  158. package/src/client/submission/SubmissionState.ts +14 -0
  159. package/src/client/validation.ts +2 -2
  160. package/src/error/ErrorProductionDesignPendingError.ts +6 -0
  161. package/src/error/ValueTypeInvariantError.ts +22 -0
  162. package/src/error/XPathFunctionalityError.ts +26 -0
  163. package/src/error/XPathFunctionalityNotSupportedError.ts +18 -0
  164. package/src/error/XPathFunctionalityPendingError.ts +18 -0
  165. package/src/index.ts +10 -1
  166. package/src/instance/Group.ts +17 -5
  167. package/src/instance/InputControl.ts +119 -0
  168. package/src/instance/ModelValue.ts +47 -54
  169. package/src/instance/Note.ts +24 -8
  170. package/src/instance/PrimaryInstance.ts +244 -0
  171. package/src/instance/Root.ts +62 -134
  172. package/src/instance/SelectField.ts +56 -19
  173. package/src/instance/Subtree.ts +19 -7
  174. package/src/instance/TriggerControl.ts +23 -6
  175. package/src/instance/abstract/DescendantNode.ts +90 -45
  176. package/src/instance/abstract/InstanceNode.ts +103 -105
  177. package/src/instance/abstract/UnsupportedControl.ts +28 -5
  178. package/src/instance/abstract/ValueNode.ts +127 -0
  179. package/src/instance/children.ts +9 -10
  180. package/src/instance/hierarchy.ts +19 -15
  181. package/src/instance/identity.ts +3 -9
  182. package/src/instance/index.ts +25 -7
  183. package/src/instance/internal-api/EvaluationContext.ts +60 -13
  184. package/src/instance/internal-api/InstanceConfig.ts +9 -2
  185. package/src/instance/internal-api/InstanceValueContext.ts +24 -0
  186. package/src/instance/internal-api/PrimaryInstanceDocument.ts +53 -0
  187. package/src/instance/internal-api/TranslationContext.ts +3 -2
  188. package/src/instance/internal-api/ValidationContext.ts +3 -4
  189. package/src/instance/internal-api/ValueContext.ts +2 -1
  190. package/src/instance/internal-api/submission/ClientReactiveSubmittableInstance.ts +20 -0
  191. package/src/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.ts +42 -0
  192. package/src/instance/internal-api/submission/ClientReactiveSubmittableParentNode.ts +25 -0
  193. package/src/instance/internal-api/submission/ClientReactiveSubmittableValueNode.ts +23 -0
  194. package/src/instance/repeat/BaseRepeatRange.ts +50 -131
  195. package/src/instance/repeat/RepeatInstance.ts +20 -46
  196. package/src/instance/repeat/RepeatRangeControlled.ts +5 -5
  197. package/src/instance/repeat/RepeatRangeUncontrolled.ts +2 -4
  198. package/src/instance/resource.ts +1 -1
  199. package/src/instance/text/TextChunk.ts +1 -1
  200. package/src/instance/unsupported/RangeControl.ts +5 -1
  201. package/src/instance/unsupported/RankControl.ts +5 -1
  202. package/src/instance/unsupported/UploadControl.ts +5 -1
  203. package/src/integration/xpath/EngineXPathEvaluator.ts +22 -0
  204. package/src/integration/xpath/adapter/XFormsXPathNode.ts +126 -0
  205. package/src/integration/xpath/adapter/engineDOMAdapter.ts +57 -0
  206. package/src/integration/xpath/adapter/kind.ts +114 -0
  207. package/src/integration/xpath/adapter/names.ts +99 -0
  208. package/src/integration/xpath/adapter/traversal.ts +228 -0
  209. package/src/integration/xpath/adapter/values.ts +5 -0
  210. package/src/integration/xpath/static-dom/StaticAttribute.ts +33 -0
  211. package/src/integration/xpath/static-dom/StaticDocument.ts +38 -0
  212. package/src/integration/xpath/static-dom/StaticElement.ts +106 -0
  213. package/src/integration/xpath/static-dom/StaticNamedNode.ts +45 -0
  214. package/src/integration/xpath/static-dom/StaticNode.ts +68 -0
  215. package/src/integration/xpath/static-dom/StaticText.ts +28 -0
  216. package/src/lib/client-reactivity/README.md +0 -0
  217. package/src/lib/client-reactivity/submission/createInstanceSubmissionState.ts +12 -0
  218. package/src/lib/client-reactivity/submission/createLeafNodeSubmissionState.ts +20 -0
  219. package/src/lib/client-reactivity/submission/createNodeRangeSubmissionState.ts +17 -0
  220. package/src/lib/client-reactivity/submission/createParentNodeSubmissionState.ts +22 -0
  221. package/src/lib/client-reactivity/submission/createValueNodeSubmissionState.ts +21 -0
  222. package/src/lib/client-reactivity/submission/prepareSubmission.ts +172 -0
  223. package/src/lib/codecs/DecimalValueCodec.ts +46 -0
  224. package/src/lib/codecs/IntValueCodec.ts +100 -0
  225. package/src/lib/codecs/StringValueCodec.ts +11 -0
  226. package/src/lib/codecs/ValueCodec.ts +106 -0
  227. package/src/lib/codecs/ValueTypePlaceholderCodec.ts +19 -0
  228. package/src/lib/codecs/getSharedValueCodec.ts +77 -0
  229. package/src/lib/dom/query.ts +7 -0
  230. package/src/lib/reactivity/createChildrenState.ts +8 -6
  231. package/src/lib/reactivity/createComputedExpression.ts +57 -34
  232. package/src/lib/reactivity/createInstanceValueState.ts +166 -0
  233. package/src/lib/reactivity/createNoteReadonlyThunk.ts +12 -7
  234. package/src/lib/reactivity/createSelectItems.ts +21 -21
  235. package/src/lib/reactivity/createTranslationState.ts +61 -0
  236. package/src/lib/reactivity/createValueState.ts +62 -120
  237. package/src/lib/reactivity/materializeCurrentStateChildren.ts +7 -7
  238. package/src/lib/reactivity/text/createTextRange.ts +4 -3
  239. package/src/lib/reactivity/validation/createAggregatedViolations.ts +1 -1
  240. package/src/lib/reactivity/validation/createValidation.ts +2 -3
  241. package/src/lib/xml-serialization.ts +96 -0
  242. package/src/parse/XFormDOM.ts +110 -75
  243. package/src/parse/XFormDefinition.ts +1 -6
  244. package/src/parse/attachments/FormAttachmentResource.ts +40 -0
  245. package/src/parse/body/BodyDefinition.ts +3 -3
  246. package/src/parse/body/appearance/inputAppearanceParser.ts +1 -1
  247. package/src/parse/body/control/{InputDefinition.ts → InputControlDefinition.ts} +3 -5
  248. package/src/parse/body/control/select/ItemsetNodesetContext.ts +0 -5
  249. package/src/parse/expression/abstract/DependencyContext.ts +0 -26
  250. package/src/parse/expression/abstract/DependentExpression.ts +2 -13
  251. package/src/parse/model/BindDefinition.ts +5 -8
  252. package/src/parse/model/BindTypeDefinition.ts +175 -0
  253. package/src/parse/model/DescendentNodeDefinition.ts +0 -6
  254. package/src/parse/model/FormSubmissionDefinition.ts +44 -0
  255. package/src/parse/model/ItextTranslation/ItextTranslationDefinition.ts +4 -0
  256. package/src/parse/model/ItextTranslation/ItextTranslationRootDefinition.ts +41 -0
  257. package/src/parse/model/ItextTranslation/ItextTranslationsDefinition.ts +31 -0
  258. package/src/parse/model/LeafNodeDefinition.ts +4 -1
  259. package/src/parse/model/ModelBindMap.ts +6 -8
  260. package/src/parse/model/ModelDefinition.ts +6 -1
  261. package/src/parse/model/NodeDefinition.ts +0 -3
  262. package/src/parse/model/NoteNodeDefinition.ts +3 -3
  263. package/src/parse/model/RootDefinition.ts +2 -1
  264. package/src/parse/model/SecondaryInstance/SecondaryInstanceDefinition.ts +4 -0
  265. package/src/parse/model/SecondaryInstance/SecondaryInstanceRootDefinition.ts +12 -0
  266. package/src/parse/model/SecondaryInstance/SecondaryInstancesDefinition.ts +102 -0
  267. package/src/parse/model/SecondaryInstance/sources/BlankSecondaryInstanceSource.ts +40 -0
  268. package/src/parse/model/SecondaryInstance/sources/CSVExternalSecondaryInstance.ts +288 -0
  269. package/src/parse/model/SecondaryInstance/sources/ExternalSecondaryInstanceResource.ts +222 -0
  270. package/src/parse/model/SecondaryInstance/sources/ExternalSecondaryInstanceSource.ts +22 -0
  271. package/src/parse/model/SecondaryInstance/sources/GeoJSONExternalSecondaryInstance.ts +414 -0
  272. package/src/parse/model/SecondaryInstance/sources/InternalSecondaryInstanceSource.ts +19 -0
  273. package/src/parse/model/SecondaryInstance/sources/SecondaryInstanceSource.ts +29 -0
  274. package/src/parse/model/SecondaryInstance/sources/XMLExternalSecondaryInstanceSource.ts +32 -0
  275. package/src/parse/shared/parseStaticDocumentFromDOMSubtree.ts +149 -0
  276. package/src/parse/text/abstract/TextRangeDefinition.ts +0 -6
  277. package/src/parse/xpath/semantic-analysis.ts +29 -0
  278. package/dist/client/StringNode.d.ts +0 -46
  279. package/dist/instance/StringField.d.ts +0 -44
  280. package/dist/instance/internal-api/SubscribableDependency.d.ts +0 -59
  281. package/dist/parse/XFormDataType.d.ts +0 -25
  282. package/src/client/StringNode.ts +0 -52
  283. package/src/instance/StringField.ts +0 -120
  284. package/src/instance/internal-api/SubscribableDependency.ts +0 -61
  285. package/src/parse/XFormDataType.ts +0 -62
@@ -0,0 +1,414 @@
1
+ import { XFORMS_NAMESPACE_URI } from '@getodk/common/constants/xmlns.ts';
2
+ import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
3
+ import { assertUnknownArray } from '@getodk/common/lib/type-assertions/assertUnknownArray.ts';
4
+ import type { UnknownObject } from '@getodk/common/lib/type-assertions/assertUnknownObject.ts';
5
+ import { assertUnknownObject } from '@getodk/common/lib/type-assertions/assertUnknownObject.ts';
6
+ import type {
7
+ GeoJsonProperties as BaseGeoJSONProperties,
8
+ LineString as BaseLineString,
9
+ Point as BasePoint,
10
+ Polygon as BasePolygon,
11
+ Feature as GeoJSONFeature,
12
+ FeatureCollection as GeoJSONFeatureCollection,
13
+ } from 'geojson';
14
+ import { ErrorProductionDesignPendingError } from '../../../../error/ErrorProductionDesignPendingError.ts';
15
+ import { StaticAttribute } from '../../../../integration/xpath/static-dom/StaticAttribute.ts';
16
+ import { StaticElement } from '../../../../integration/xpath/static-dom/StaticElement.ts';
17
+ import { StaticText } from '../../../../integration/xpath/static-dom/StaticText.ts';
18
+ import { SecondaryInstanceDefinition } from '../SecondaryInstanceDefinition.ts';
19
+ import { SecondaryInstanceRootDefinition } from '../SecondaryInstanceRootDefinition.ts';
20
+ import { ExternalSecondaryInstanceSource } from './ExternalSecondaryInstanceSource.ts';
21
+
22
+ const FEATURE_COLLECTION = 'FeatureCollection';
23
+ type FEATURE_COLLECTION = typeof FEATURE_COLLECTION;
24
+
25
+ const FEATURE = 'Feature';
26
+ type FEATURE = typeof FEATURE;
27
+
28
+ const POINT = 'Point';
29
+ type POINT = typeof POINT;
30
+
31
+ const LINE_STRING = 'LineString';
32
+ type LINE_STRING = typeof LINE_STRING;
33
+
34
+ const POLYGON = 'Polygon';
35
+ type POLYGON = typeof POLYGON;
36
+
37
+ type SupportedGeometryType = LINE_STRING | POINT | POLYGON;
38
+
39
+ const SUPPORTED_TYPES = new Set<SupportedGeometryType>([POINT, LINE_STRING, POLYGON]);
40
+
41
+ const SUPPORTED_TYPES_MESSAGE = 'Only Points, LineStrings and Polygons are currently supported';
42
+
43
+ type LongLatCoordinates = readonly [longitude: number, latitude: number];
44
+
45
+ interface UnknownCoordinatesObject {
46
+ readonly coordinates: unknown;
47
+ }
48
+
49
+ // prettier-ignore
50
+ type ExtensibleBaseGeoJSONType<T> =
51
+ T extends UnknownCoordinatesObject
52
+ ? Readonly<Omit<T, 'coordinates'>>
53
+ : Readonly<T>;
54
+
55
+ interface Point extends ExtensibleBaseGeoJSONType<BasePoint> {
56
+ readonly coordinates: LongLatCoordinates;
57
+ }
58
+
59
+ type LineStringCoordinates = readonly LongLatCoordinates[];
60
+
61
+ interface LineString extends ExtensibleBaseGeoJSONType<BaseLineString> {
62
+ readonly coordinates: LineStringCoordinates;
63
+ }
64
+
65
+ // prettier-ignore
66
+ type PolygonCoordinates =
67
+ | readonly []
68
+ | readonly [LineStringCoordinates];
69
+
70
+ interface Polygon extends ExtensibleBaseGeoJSONType<BasePolygon> {
71
+ readonly coordinates: PolygonCoordinates;
72
+ }
73
+
74
+ // prettier-ignore
75
+ type SupportedGeometry =
76
+ | LineString
77
+ | Point
78
+ | Polygon;
79
+
80
+ interface UnparsedGeometry<Type extends SupportedGeometryType> {
81
+ readonly type: Type;
82
+ readonly coordinates: readonly unknown[];
83
+ }
84
+
85
+ type UnknownGeometry = {
86
+ [Type in SupportedGeometryType]: UnparsedGeometry<Type>;
87
+ }[SupportedGeometryType];
88
+
89
+ /**
90
+ * This is a stricter variant of {@link BaseGeoJSONProperties}.
91
+ */
92
+ type GeoJSONProperties = UnknownObject;
93
+
94
+ /**
95
+ * This is our supported variant/subset of {@link GeoJSONFeature} where:
96
+ *
97
+ * - {@link geometry} is parsed/validated to be one of our
98
+ * {@link SupportedGeometry | supported geometries}
99
+ *
100
+ * - all properties and non-primitive property values are deeply `readonly`
101
+ */
102
+ interface Feature {
103
+ readonly type: FEATURE;
104
+ readonly geometry: SupportedGeometry;
105
+ readonly id?: number | string | undefined;
106
+
107
+ /**
108
+ * Perhaps surprising: this property is required by the
109
+ * {@link https://datatracker.ietf.org/doc/html/rfc7946#section-3.2 | GeoJSON spec}!
110
+ */
111
+ readonly properties: GeoJSONProperties | null;
112
+ }
113
+
114
+ /**
115
+ * This is our supported variant/subset of {@link GeoJSONFeatureCollection}, as
116
+ * described for {@link Feature}.
117
+ */
118
+ interface FeatureCollection {
119
+ readonly type: FEATURE_COLLECTION;
120
+ readonly features: readonly Feature[];
121
+ }
122
+
123
+ type AssertLongLatCoordinates = (data: unknown) => asserts data is LongLatCoordinates;
124
+
125
+ const assertLongLatCoordinates: AssertLongLatCoordinates = (data) => {
126
+ assertUnknownArray(data);
127
+
128
+ const [longitude, latitude, ...rest] = data;
129
+
130
+ if (typeof longitude === 'number' && typeof latitude === 'number' && rest.length === 0) {
131
+ return;
132
+ }
133
+
134
+ throw new ErrorProductionDesignPendingError(
135
+ `Only ${POINT}s with latitude and longitude are currently supported`
136
+ );
137
+ };
138
+
139
+ type AssertUnknownGeometry = (value: unknown) => asserts value is UnknownGeometry;
140
+
141
+ const assertUnknownGeometry: AssertUnknownGeometry = (value) => {
142
+ assertUnknownObject(value);
143
+
144
+ if (!SUPPORTED_TYPES.has(value.type as SupportedGeometryType)) {
145
+ throw new ErrorProductionDesignPendingError(SUPPORTED_TYPES_MESSAGE);
146
+ }
147
+
148
+ assertUnknownArray(value.coordinates);
149
+ };
150
+
151
+ type AssertSupportedGeometry = (value: unknown) => asserts value is SupportedGeometry;
152
+
153
+ const assertSupportedGeometry: AssertSupportedGeometry = (value) => {
154
+ assertUnknownGeometry(value);
155
+
156
+ switch (value.type) {
157
+ case LINE_STRING:
158
+ value.coordinates.forEach(assertLongLatCoordinates);
159
+ break;
160
+
161
+ case POINT:
162
+ assertLongLatCoordinates(value.coordinates);
163
+ break;
164
+
165
+ case POLYGON: {
166
+ const [coordinates, ...rest] = value.coordinates;
167
+
168
+ if (coordinates == null) {
169
+ return;
170
+ }
171
+
172
+ if (rest.length > 0) {
173
+ throw new ErrorProductionDesignPendingError(
174
+ `Unsupported ${POLYGON}: multiple sets of coordinates`
175
+ );
176
+ }
177
+
178
+ assertUnknownArray(coordinates);
179
+ coordinates.forEach(assertLongLatCoordinates);
180
+
181
+ break;
182
+ }
183
+
184
+ default:
185
+ throw new UnreachableError(value);
186
+ }
187
+ };
188
+
189
+ type AssertFeature = (value: unknown) => asserts value is Feature;
190
+
191
+ const assertFeature: AssertFeature = (value) => {
192
+ assertUnknownObject(value);
193
+
194
+ const { geometry, type, id, properties } = value;
195
+
196
+ if (type !== FEATURE) {
197
+ throw new ErrorProductionDesignPendingError(
198
+ `Expected Feature.type ${FEATURE}, got ${String(type)}`
199
+ );
200
+ }
201
+
202
+ assertSupportedGeometry(geometry);
203
+
204
+ if ('id' in value) {
205
+ const typeofId = typeof id;
206
+
207
+ switch (typeofId) {
208
+ case 'number':
209
+ case 'string':
210
+ case 'undefined':
211
+ break;
212
+
213
+ default:
214
+ throw new ErrorProductionDesignPendingError(
215
+ `Unexpected type of feature id property: ${typeofId}`
216
+ );
217
+ }
218
+ }
219
+
220
+ // Note: atypical strict check for `null` value! The `properties` key is
221
+ // required per
222
+ if (properties === null) {
223
+ return;
224
+ }
225
+
226
+ assertUnknownObject(properties);
227
+ };
228
+
229
+ type AssertFeatureCollection = (value: unknown) => asserts value is FeatureCollection;
230
+
231
+ const assertFeatureCollection: AssertFeatureCollection = (value) => {
232
+ assertUnknownObject(value);
233
+
234
+ const { type, features } = value;
235
+
236
+ if (type !== FEATURE_COLLECTION) {
237
+ throw new ErrorProductionDesignPendingError(
238
+ `Expected FeatureCollection.type ${FEATURE_COLLECTION}, got ${String(type)}`
239
+ );
240
+ }
241
+
242
+ assertUnknownArray(features);
243
+
244
+ features.forEach(assertFeature);
245
+ };
246
+
247
+ type SerializedCoordinates = `${LongLatCoordinates[1]} ${LongLatCoordinates[0]} 0 0`;
248
+
249
+ const serializeCoordinates = (coordinates: LongLatCoordinates): SerializedCoordinates => {
250
+ const [longitude, latitude] = coordinates;
251
+
252
+ return `${latitude} ${longitude} 0 0`;
253
+ };
254
+
255
+ class GeoJSONSecondaryInstanceFeatureGeometryElement extends StaticElement {
256
+ static from(
257
+ parent: StaticElement,
258
+ feature: Feature
259
+ ): GeoJSONSecondaryInstanceFeatureGeometryElement {
260
+ const { geometry } = feature;
261
+
262
+ switch (geometry.type) {
263
+ case 'LineString':
264
+ return new this(parent, geometry.coordinates);
265
+
266
+ case 'Point':
267
+ return new this(parent, [geometry.coordinates]);
268
+
269
+ case 'Polygon':
270
+ return new this(parent, geometry.coordinates[0] ?? []);
271
+
272
+ default:
273
+ throw new UnreachableError(geometry);
274
+ }
275
+ }
276
+
277
+ constructor(parent: StaticElement, points: readonly LongLatCoordinates[]) {
278
+ const values = points.map(serializeCoordinates);
279
+ const value = values.join('; ');
280
+
281
+ super(
282
+ parent,
283
+ () => [],
284
+ (self) => [new StaticText(self, value)],
285
+ {
286
+ namespaceURI: XFORMS_NAMESPACE_URI,
287
+ localName: 'geometry',
288
+ }
289
+ );
290
+ }
291
+ }
292
+
293
+ class GeoJSONSecondaryInstanceFeaturePropertyElement extends StaticElement {
294
+ static *buildPropertyElements(
295
+ parent: StaticElement,
296
+ feature: Feature
297
+ ): Iterable<GeoJSONSecondaryInstanceFeaturePropertyElement> {
298
+ const { properties } = feature;
299
+
300
+ if (properties == null) {
301
+ return [];
302
+ }
303
+
304
+ const { id: propertiesId, ...nonIdProperties } = properties;
305
+ const { id = propertiesId } = feature;
306
+
307
+ if (id !== undefined) {
308
+ yield new this(parent, 'id', String(id));
309
+ }
310
+
311
+ for (const [propertyName, propertyValue] of Object.entries(nonIdProperties)) {
312
+ yield new this(parent, propertyName, String(propertyValue));
313
+ }
314
+ }
315
+
316
+ constructor(parent: StaticElement, propertyName: string, propertyValue: string) {
317
+ super(
318
+ parent,
319
+ () => [],
320
+ (self) => [new StaticText(self, propertyValue)],
321
+ {
322
+ namespaceURI: XFORMS_NAMESPACE_URI,
323
+ localName: propertyName,
324
+ }
325
+ );
326
+ }
327
+ }
328
+
329
+ class GeoJSONSecondaryInstanceFeatureItemElement extends StaticElement {
330
+ constructor(parent: StaticElement, feature: Feature) {
331
+ super(
332
+ parent,
333
+ () => [],
334
+ (self) => {
335
+ const geometry = GeoJSONSecondaryInstanceFeatureGeometryElement.from(self, feature);
336
+ const properties = GeoJSONSecondaryInstanceFeaturePropertyElement.buildPropertyElements(
337
+ self,
338
+ feature
339
+ );
340
+
341
+ return [geometry, ...properties];
342
+ },
343
+ {
344
+ namespaceURI: XFORMS_NAMESPACE_URI,
345
+ localName: 'item',
346
+ }
347
+ );
348
+ }
349
+ }
350
+
351
+ class GeoJSONSecondaryInstanceRootElement extends StaticElement {
352
+ constructor(parent: StaticElement, featureCollection: FeatureCollection) {
353
+ super(
354
+ parent,
355
+ () => [],
356
+ (self) => {
357
+ return featureCollection.features.map((feature) => {
358
+ return new GeoJSONSecondaryInstanceFeatureItemElement(self, feature);
359
+ });
360
+ },
361
+ {
362
+ namespaceURI: XFORMS_NAMESPACE_URI,
363
+ localName: 'root',
364
+ }
365
+ );
366
+ }
367
+ }
368
+
369
+ class GeoJSONExternalSecondaryInstanceDocumentElement extends SecondaryInstanceRootDefinition {
370
+ constructor(
371
+ instanceId: string,
372
+ parent: GeoJSONExternalSecondaryInstanceDefinition,
373
+ featureCollection: FeatureCollection
374
+ ) {
375
+ super(
376
+ parent,
377
+ (self) => [
378
+ new StaticAttribute(self, {
379
+ namespaceURI: XFORMS_NAMESPACE_URI,
380
+ localName: 'id',
381
+ value: instanceId,
382
+ }),
383
+ ],
384
+ (self) => [new GeoJSONSecondaryInstanceRootElement(self, featureCollection)],
385
+ {
386
+ namespaceURI: XFORMS_NAMESPACE_URI,
387
+ localName: 'instance',
388
+ }
389
+ );
390
+ }
391
+ }
392
+
393
+ class GeoJSONExternalSecondaryInstanceDefinition extends SecondaryInstanceDefinition {
394
+ constructor(instanceId: string, featureCollection: FeatureCollection) {
395
+ super((self) => {
396
+ return new GeoJSONExternalSecondaryInstanceDocumentElement(
397
+ instanceId,
398
+ self,
399
+ featureCollection
400
+ );
401
+ });
402
+ }
403
+ }
404
+
405
+ export class GeoJSONExternalSecondaryInstanceSource extends ExternalSecondaryInstanceSource<'geojson'> {
406
+ parseDefinition(): SecondaryInstanceDefinition {
407
+ const { data } = this.resource;
408
+ const value = JSON.parse(data) as unknown;
409
+
410
+ assertFeatureCollection(value);
411
+
412
+ return new GeoJSONExternalSecondaryInstanceDefinition(this.instanceId, value);
413
+ }
414
+ }
@@ -0,0 +1,19 @@
1
+ import { parseStaticDocumentFromDOMSubtree } from '../../../shared/parseStaticDocumentFromDOMSubtree.ts';
2
+ import type { DOMSecondaryInstanceElement } from '../../../XFormDOM.ts';
3
+ import { SecondaryInstanceDefinition } from '../SecondaryInstanceDefinition.ts';
4
+ import { SecondaryInstanceRootDefinition } from '../SecondaryInstanceRootDefinition.ts';
5
+ import { SecondaryInstanceSource } from './SecondaryInstanceSource.ts';
6
+
7
+ export class InternalSecondaryInstanceSource extends SecondaryInstanceSource<'internal'> {
8
+ constructor(instanceId: string, resourceURL: null, domElement: DOMSecondaryInstanceElement) {
9
+ super('internal', instanceId, resourceURL, domElement);
10
+ }
11
+
12
+ parseDefinition(): SecondaryInstanceDefinition {
13
+ return parseStaticDocumentFromDOMSubtree(
14
+ SecondaryInstanceDefinition,
15
+ SecondaryInstanceRootDefinition,
16
+ this.domElement
17
+ );
18
+ }
19
+ }
@@ -0,0 +1,29 @@
1
+ import type { JRResourceURL } from '@getodk/common/jr-resources/JRResourceURL.ts';
2
+ import type { DOMSecondaryInstanceElement } from '../../../XFormDOM.ts';
3
+ import type { SecondaryInstanceDefinition } from '../SecondaryInstanceDefinition.ts';
4
+
5
+ // prettier-ignore
6
+ export type ExternalSecondaryInstanceSourceFormat =
7
+ | 'csv'
8
+ | 'geojson'
9
+ | 'xml';
10
+
11
+ // prettier-ignore
12
+ export type SecondaryInstanceSourceFormat =
13
+ // eslint-disable-next-line @typescript-eslint/sort-type-constituents
14
+ | ExternalSecondaryInstanceSourceFormat
15
+ | 'internal'
16
+ | 'blank';
17
+
18
+ export abstract class SecondaryInstanceSource<
19
+ Format extends SecondaryInstanceSourceFormat = SecondaryInstanceSourceFormat,
20
+ > {
21
+ constructor(
22
+ readonly format: Format,
23
+ readonly instanceId: string,
24
+ readonly resourceURL: JRResourceURL | null,
25
+ readonly domElement: DOMSecondaryInstanceElement
26
+ ) {}
27
+
28
+ abstract parseDefinition(): SecondaryInstanceDefinition;
29
+ }
@@ -0,0 +1,32 @@
1
+ import { XFORMS_NAMESPACE_URI } from '@getodk/common/constants/xmlns.ts';
2
+ import { parseStaticDocumentFromDOMSubtree } from '../../../shared/parseStaticDocumentFromDOMSubtree.ts';
3
+ import { SecondaryInstanceDefinition } from '../SecondaryInstanceDefinition.ts';
4
+ import { SecondaryInstanceRootDefinition } from '../SecondaryInstanceRootDefinition.ts';
5
+ import { ExternalSecondaryInstanceSource } from './ExternalSecondaryInstanceSource.ts';
6
+ import type { InternalSecondaryInstanceSource } from './InternalSecondaryInstanceSource.ts';
7
+
8
+ export class XMLExternalSecondaryInstanceSource extends ExternalSecondaryInstanceSource<'xml'> {
9
+ /**
10
+ * Note: this logic is a superset of the logic in
11
+ * {@link InternalSecondaryInstanceSource.parseDefinition}. That subset is so
12
+ * trivial/already sufficiently abstracted that it doesn't really make a lot
13
+ * of sense to abstract further, but it might be worth considering if both
14
+ * method implementations grow their responsibilities in the same ways.
15
+ */
16
+ parseDefinition(): SecondaryInstanceDefinition {
17
+ const xmlDocument = this.domElement.ownerDocument.implementation.createDocument(
18
+ XFORMS_NAMESPACE_URI,
19
+ 'instance'
20
+ );
21
+ const instanceElement = xmlDocument.documentElement;
22
+
23
+ instanceElement.setAttribute('id', this.instanceId);
24
+ instanceElement.innerHTML = this.resource.data;
25
+
26
+ return parseStaticDocumentFromDOMSubtree(
27
+ SecondaryInstanceDefinition,
28
+ SecondaryInstanceRootDefinition,
29
+ instanceElement
30
+ );
31
+ }
32
+ }
@@ -0,0 +1,149 @@
1
+ import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
2
+ import type { AnyConstructor } from '@getodk/common/types/helpers.js';
3
+ import { StaticAttribute } from '../../integration/xpath/static-dom/StaticAttribute.ts';
4
+ import type {
5
+ StaticDocument,
6
+ StaticDocumentRootFactory,
7
+ } from '../../integration/xpath/static-dom/StaticDocument.ts';
8
+ import type {
9
+ StaticElementAttributesFactory,
10
+ StaticElementChildNodesFactory,
11
+ } from '../../integration/xpath/static-dom/StaticElement.ts';
12
+ import { StaticElement } from '../../integration/xpath/static-dom/StaticElement.ts';
13
+ import type { StaticNamedNodeOptions } from '../../integration/xpath/static-dom/StaticNamedNode.ts';
14
+ import type { StaticParentNode } from '../../integration/xpath/static-dom/StaticNode.ts';
15
+ import { StaticText } from '../../integration/xpath/static-dom/StaticText.ts';
16
+
17
+ type ConcreteConstructor<T extends AnyConstructor> = Pick<T, keyof T>;
18
+
19
+ // prettier-ignore
20
+ export type ConcreteStaticDocumentConstructor<
21
+ T extends StaticDocument<Root>,
22
+ Root extends StaticElement,
23
+ > =
24
+ & ConcreteConstructor<typeof StaticDocument<Root>>
25
+ & (new (rootFactory: StaticDocumentRootFactory<T, Root>) => T);
26
+
27
+ const domElementAttributesFactory = (domElement: Element): StaticElementAttributesFactory => {
28
+ const attributes = Array.from(domElement.attributes);
29
+
30
+ return (element) => {
31
+ return attributes.map((attr) => {
32
+ return new StaticAttribute(element, attr);
33
+ });
34
+ };
35
+ };
36
+
37
+ const { ELEMENT_NODE, CDATA_SECTION_NODE, TEXT_NODE } = Node;
38
+
39
+ interface StaticChildElementSource {
40
+ readonly domType: typeof ELEMENT_NODE;
41
+ readonly domNode: Element;
42
+ }
43
+
44
+ interface StaticChildTextSource {
45
+ readonly domType: typeof CDATA_SECTION_NODE | typeof TEXT_NODE;
46
+ readonly domNode: CharacterData;
47
+ }
48
+
49
+ // prettier-ignore
50
+ type StaticChildNodeSource =
51
+ | StaticChildElementSource
52
+ | StaticChildTextSource;
53
+
54
+ export const toStaticChildNodeSource = (childNode: ChildNode): StaticChildNodeSource | null => {
55
+ const domType = childNode.nodeType;
56
+
57
+ switch (domType) {
58
+ case ELEMENT_NODE:
59
+ return {
60
+ domType,
61
+ domNode: childNode as Element,
62
+ };
63
+
64
+ case TEXT_NODE:
65
+ return {
66
+ domType,
67
+ domNode: childNode as Text,
68
+ };
69
+
70
+ case CDATA_SECTION_NODE:
71
+ return {
72
+ domType,
73
+ domNode: childNode as CDATASection,
74
+ };
75
+
76
+ default:
77
+ return null;
78
+ }
79
+ };
80
+
81
+ const toStaticChildNodeSources = (childNode: ChildNode): StaticChildNodeSource | readonly [] => {
82
+ return toStaticChildNodeSource(childNode) ?? [];
83
+ };
84
+
85
+ const domElementChildNodesFactory = (domElement: Element): StaticElementChildNodesFactory => {
86
+ const childNodeSources = Array.from(domElement.childNodes).flatMap(toStaticChildNodeSources);
87
+
88
+ return (parent) => {
89
+ return childNodeSources.map((domSource) => {
90
+ switch (domSource.domType) {
91
+ case ELEMENT_NODE:
92
+ return parseStaticElementFromDOMElement(parent, StaticElement, domSource.domNode);
93
+
94
+ case TEXT_NODE:
95
+ case CDATA_SECTION_NODE:
96
+ return new StaticText(parent, domSource.domNode.data);
97
+
98
+ default:
99
+ throw new UnreachableError(domSource);
100
+ }
101
+ });
102
+ };
103
+ };
104
+
105
+ // prettier-ignore
106
+ export type StaticElementConstructor<
107
+ T extends StaticElement<Parent>,
108
+ Parent extends StaticParentNode = StaticParentNode
109
+ > =
110
+ & ConcreteConstructor<typeof StaticElement>
111
+ & {
112
+ readonly prototype: T;
113
+
114
+ new (
115
+ parent: Parent,
116
+ attributesFactory: StaticElementAttributesFactory,
117
+ childNodesFactory: StaticElementChildNodesFactory,
118
+ options: StaticNamedNodeOptions
119
+ ): T;
120
+ };
121
+
122
+ const parseStaticElementFromDOMElement = <
123
+ T extends StaticElement,
124
+ Parent extends StaticParentNode = StaticParentNode,
125
+ >(
126
+ parent: Parent,
127
+ ElementConstructor: StaticElementConstructor<T, Parent>,
128
+ domElement: Element
129
+ ): T => {
130
+ const attributesFactory = domElementAttributesFactory(domElement);
131
+ const childNodesFactory = domElementChildNodesFactory(domElement);
132
+
133
+ return new ElementConstructor(parent, attributesFactory, childNodesFactory, domElement);
134
+ };
135
+
136
+ export const parseStaticDocumentFromDOMSubtree = <
137
+ T extends StaticDocument<Root>,
138
+ Root extends StaticElement<T>,
139
+ >(
140
+ DocumentConstructor: ConcreteStaticDocumentConstructor<T, Root>,
141
+ DocumentRootConstructor: StaticElementConstructor<Root, T>,
142
+ subtreeRootElement: Element
143
+ ): T => {
144
+ const rootFactory: StaticDocumentRootFactory<T, Root> = (parent) => {
145
+ return parseStaticElementFromDOMElement(parent, DocumentRootConstructor, subtreeRootElement);
146
+ };
147
+
148
+ return new DocumentConstructor(rootFactory);
149
+ };
@@ -2,7 +2,6 @@ import type { LocalNamedElement } from '@getodk/common/types/dom.ts';
2
2
  import type { TextRole } from '../../../client/TextRange.ts';
3
3
  import type { XFormDefinition } from '../../../parse/XFormDefinition.ts';
4
4
  import { DependencyContext } from '../../expression/abstract/DependencyContext.ts';
5
- import type { AnyDependentExpression } from '../../expression/abstract/DependentExpression.ts';
6
5
  import type { AnyTextChunkExpression } from '../../expression/abstract/TextChunkExpression.ts';
7
6
  import type { AnyMessageDefinition } from '../MessageDefinition.ts';
8
7
  import type { AnyTextElementDefinition } from './TextElementDefinition.ts';
@@ -52,11 +51,6 @@ export abstract class TextRangeDefinition<Role extends TextRole> extends Depende
52
51
  this.parentReference = ownerContext.parentReference;
53
52
  }
54
53
 
55
- override registerDependentExpression(expression: AnyDependentExpression): void {
56
- this.ownerContext.registerDependentExpression(expression);
57
- super.registerDependentExpression(expression);
58
- }
59
-
60
54
  toJSON(): object {
61
55
  const { form, ownerContext, ...rest } = this;
62
56
 
@@ -9,7 +9,9 @@ import type {
9
9
  FilterPathExprNode,
10
10
  FunctionCallNode,
11
11
  FunctionNameNode,
12
+ LocalPartNode,
12
13
  NumberNode,
14
+ PrefixNode,
13
15
  RelativeLocationPathNode,
14
16
  StringLiteralNode,
15
17
  } from '@getodk/xpath/static/grammar/SyntaxNode.js';
@@ -464,3 +466,30 @@ export const isConstantTruthyExpression = (
464
466
  throw new UnreachableError(syntaxNode);
465
467
  }
466
468
  };
469
+
470
+ interface QualifiedNameExpression {
471
+ readonly prefix: string;
472
+ readonly localPart: string;
473
+ }
474
+
475
+ export const parseQualifiedNameExpression = (
476
+ expression: string
477
+ ): QualifiedNameExpression | null => {
478
+ const { rootNode } = expressionParser.parse(expression);
479
+ const syntaxNode = findTypedPrincipalExpressionNode(['prefixed_name'], rootNode);
480
+
481
+ if (syntaxNode == null) {
482
+ return null;
483
+ }
484
+
485
+ const [prefixNode, localPartNode, ...rest] = syntaxNode.children;
486
+
487
+ prefixNode satisfies PrefixNode;
488
+ localPartNode satisfies LocalPartNode;
489
+ rest satisfies readonly [];
490
+
491
+ return {
492
+ prefix: prefixNode.text,
493
+ localPart: localPartNode.text,
494
+ };
495
+ };