@getodk/xforms-engine 0.6.0 → 0.7.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 (296) hide show
  1. package/dist/client/BaseNode.d.ts +4 -4
  2. package/dist/client/BaseValueNode.d.ts +7 -3
  3. package/dist/client/RootNode.d.ts +20 -16
  4. package/dist/client/constants.d.ts +11 -12
  5. package/dist/client/form/CreateFormInstance.d.ts +14 -0
  6. package/dist/client/form/EditFormInstance.d.ts +62 -0
  7. package/dist/client/form/FormInstance.d.ts +104 -0
  8. package/dist/client/form/FormInstanceConfig.d.ts +17 -0
  9. package/dist/client/form/FormResource.d.ts +1 -0
  10. package/dist/client/form/LoadForm.d.ts +79 -0
  11. package/dist/client/form/LoadFormResult.d.ts +67 -0
  12. package/dist/client/form/RestoreFormInstance.d.ts +8 -0
  13. package/dist/client/index.d.ts +37 -11
  14. package/dist/client/node-types.d.ts +2 -2
  15. package/dist/client/repeat/BaseRepeatRangeNode.d.ts +3 -4
  16. package/dist/client/repeat/RepeatInstanceNode.d.ts +4 -5
  17. package/dist/client/repeat/RepeatRangeControlledNode.d.ts +2 -2
  18. package/dist/client/repeat/RepeatRangeUncontrolledNode.d.ts +2 -2
  19. package/dist/client/resources.d.ts +1 -1
  20. package/dist/client/serialization/InstanceData.d.ts +12 -0
  21. package/dist/client/serialization/InstanceFile.d.ts +6 -0
  22. package/dist/client/serialization/InstancePayload.d.ts +93 -0
  23. package/dist/client/serialization/InstancePayloadOptions.d.ts +23 -0
  24. package/dist/client/serialization/InstanceState.d.ts +12 -0
  25. package/dist/client/submission/{SubmissionDefinition.d.ts → SubmissionMeta.d.ts} +1 -1
  26. package/dist/entrypoints/FormInstance.d.ts +20 -0
  27. package/dist/entrypoints/FormResult/BaseFormResult.d.ts +22 -0
  28. package/dist/entrypoints/FormResult/BaseInstantiableFormResult.d.ts +25 -0
  29. package/dist/entrypoints/FormResult/FormFailureResult.d.ts +17 -0
  30. package/dist/entrypoints/FormResult/FormSuccessResult.d.ts +15 -0
  31. package/dist/entrypoints/FormResult/FormWarningResult.d.ts +15 -0
  32. package/dist/entrypoints/createInstance.d.ts +9 -0
  33. package/dist/entrypoints/editInstance.d.ts +9 -0
  34. package/dist/entrypoints/index.d.ts +4 -0
  35. package/dist/entrypoints/loadForm.d.ts +4 -0
  36. package/dist/entrypoints/restoreInstance.d.ts +9 -0
  37. package/dist/error/LoadFormFailureError.d.ts +9 -0
  38. package/dist/error/MalformedInstanceDataError.d.ts +3 -0
  39. package/dist/error/TemplatedNodeAttributeSerializationError.d.ts +22 -0
  40. package/dist/index.d.ts +2 -33
  41. package/dist/index.js +1915 -1419
  42. package/dist/index.js.map +1 -1
  43. package/dist/instance/Group.d.ts +6 -5
  44. package/dist/instance/InputControl.d.ts +5 -4
  45. package/dist/instance/ModelValue.d.ts +5 -4
  46. package/dist/instance/Note.d.ts +4 -3
  47. package/dist/instance/PrimaryInstance.d.ts +35 -8
  48. package/dist/instance/RangeControl.d.ts +5 -4
  49. package/dist/instance/RankControl.d.ts +6 -5
  50. package/dist/instance/Root.d.ts +7 -9
  51. package/dist/instance/SelectControl.d.ts +4 -3
  52. package/dist/instance/Subtree.d.ts +6 -5
  53. package/dist/instance/TriggerControl.d.ts +4 -3
  54. package/dist/instance/abstract/DescendantNode.d.ts +10 -1
  55. package/dist/instance/abstract/InstanceNode.d.ts +7 -4
  56. package/dist/instance/abstract/ValueNode.d.ts +7 -5
  57. package/dist/instance/children/DescendantNodeInitOptions.d.ts +32 -0
  58. package/dist/instance/{children.d.ts → children/buildChildren.d.ts} +1 -1
  59. package/dist/instance/children/childrenInitOptions.d.ts +9 -0
  60. package/dist/instance/children/normalizeChildInitOptions.d.ts +2 -0
  61. package/dist/instance/input/InitialInstanceState.d.ts +13 -0
  62. package/dist/instance/input/InstanceAttachmentMap.d.ts +19 -0
  63. package/dist/instance/internal-api/InstanceConfig.d.ts +2 -9
  64. package/dist/instance/internal-api/InstanceValueContext.d.ts +8 -1
  65. package/dist/instance/internal-api/serialization/ClientReactiveSerializableInstance.d.ts +14 -0
  66. package/dist/instance/internal-api/serialization/ClientReactiveSerializableLeafNode.d.ts +32 -0
  67. package/dist/instance/internal-api/serialization/ClientReactiveSerializableParentNode.d.ts +18 -0
  68. package/dist/instance/internal-api/serialization/ClientReactiveSerializableTemplatedNode.d.ts +13 -0
  69. package/dist/instance/internal-api/serialization/ClientReactiveSerializableValueNode.d.ts +21 -0
  70. package/dist/instance/repeat/BaseRepeatRange.d.ts +10 -8
  71. package/dist/instance/repeat/RepeatInstance.d.ts +10 -7
  72. package/dist/instance/repeat/RepeatRangeControlled.d.ts +27 -3
  73. package/dist/instance/repeat/RepeatRangeUncontrolled.d.ts +4 -3
  74. package/dist/instance/resource.d.ts +5 -1
  75. package/dist/instance/unsupported/UploadControl.d.ts +32 -4
  76. package/dist/integration/xpath/EngineXPathEvaluator.d.ts +2 -2
  77. package/dist/integration/xpath/adapter/kind.d.ts +4 -10
  78. package/dist/integration/xpath/static-dom/StaticAttribute.d.ts +4 -5
  79. package/dist/integration/xpath/static-dom/StaticDocument.d.ts +13 -10
  80. package/dist/integration/xpath/static-dom/StaticElement.d.ts +21 -22
  81. package/dist/integration/xpath/static-dom/StaticNode.d.ts +1 -1
  82. package/dist/integration/xpath/static-dom/StaticParentNode.d.ts +13 -0
  83. package/dist/integration/xpath/static-dom/staticNodeName.d.ts +3 -0
  84. package/dist/lib/client-reactivity/instance-state/createNodeRangeInstanceState.d.ts +4 -0
  85. package/dist/lib/client-reactivity/instance-state/createParentNodeInstanceState.d.ts +4 -0
  86. package/dist/lib/client-reactivity/instance-state/createPrimaryInstanceState.d.ts +3 -0
  87. package/dist/lib/client-reactivity/instance-state/createRootInstanceState.d.ts +3 -0
  88. package/dist/lib/client-reactivity/instance-state/createTemplatedNodeInstanceState.d.ts +6 -0
  89. package/dist/lib/client-reactivity/instance-state/createValueNodeInstanceState.d.ts +3 -0
  90. package/dist/lib/client-reactivity/instance-state/prepareInstancePayload.d.ts +8 -0
  91. package/dist/lib/codecs/TempUnsupportedControlCodec.d.ts +7 -0
  92. package/dist/lib/names/UnprefixedXFormsName.d.ts +4 -0
  93. package/dist/lib/reactivity/createInstanceValueState.d.ts +2 -27
  94. package/dist/parse/XFormDOM.d.ts +1 -1
  95. package/dist/parse/model/BindDefinition.d.ts +1 -1
  96. package/dist/parse/model/ItextTranslationsDefinition.d.ts +18 -0
  97. package/dist/parse/model/LeafNodeDefinition.d.ts +4 -5
  98. package/dist/parse/model/ModelBindMap.d.ts +1 -1
  99. package/dist/parse/model/ModelDefinition.d.ts +9 -2
  100. package/dist/parse/model/NodeDefinition.d.ts +18 -30
  101. package/dist/parse/model/NoteNodeDefinition.d.ts +3 -2
  102. package/dist/parse/model/RangeNodeDefinition.d.ts +2 -1
  103. package/dist/parse/model/RepeatDefinition.d.ts +62 -0
  104. package/dist/parse/model/RootAttributeDefinition.d.ts +1 -4
  105. package/dist/parse/model/RootAttributeMap.d.ts +2 -1
  106. package/dist/parse/model/RootDefinition.d.ts +7 -8
  107. package/dist/parse/model/SecondaryInstance/SecondaryInstancesDefinition.d.ts +15 -2
  108. package/dist/parse/model/SecondaryInstance/assertSecondaryInstanceDefinition.d.ts +5 -0
  109. package/dist/parse/model/SecondaryInstance/defineSecondaryInstance.d.ts +5 -0
  110. package/dist/parse/model/SecondaryInstance/sources/BlankSecondaryInstanceSource.d.ts +1 -7
  111. package/dist/parse/model/SecondaryInstance/sources/CSVExternalSecondaryInstance.d.ts +1 -1
  112. package/dist/parse/model/SecondaryInstance/sources/GeoJSONExternalSecondaryInstance.d.ts +1 -1
  113. package/dist/parse/model/SecondaryInstance/sources/InternalSecondaryInstanceSource.d.ts +1 -1
  114. package/dist/parse/model/SecondaryInstance/sources/SecondaryInstanceSource.d.ts +1 -1
  115. package/dist/parse/model/SecondaryInstance/sources/XMLExternalSecondaryInstanceSource.d.ts +1 -1
  116. package/dist/parse/model/{FormSubmissionDefinition.d.ts → SubmissionDefinition.d.ts} +2 -2
  117. package/dist/parse/model/SubtreeDefinition.d.ts +4 -5
  118. package/dist/parse/model/nodeDefinitionMap.d.ts +5 -0
  119. package/dist/parse/shared/parseInstanceXML.d.ts +21 -0
  120. package/dist/parse/shared/parseStaticDocumentFromDOMSubtree.d.ts +4 -21
  121. package/dist/solid.js +1709 -1213
  122. package/dist/solid.js.map +1 -1
  123. package/package.json +1 -1
  124. package/src/client/BaseNode.ts +4 -4
  125. package/src/client/BaseValueNode.ts +7 -3
  126. package/src/client/RootNode.ts +27 -19
  127. package/src/client/constants.ts +12 -14
  128. package/src/client/form/CreateFormInstance.ts +19 -0
  129. package/src/client/form/EditFormInstance.ts +93 -0
  130. package/src/client/form/FormInstance.ts +114 -0
  131. package/src/client/form/FormInstanceConfig.ts +18 -0
  132. package/src/client/form/FormResource.ts +1 -0
  133. package/src/client/form/LoadForm.ts +92 -0
  134. package/src/client/form/LoadFormResult.ts +103 -0
  135. package/src/client/form/RestoreFormInstance.ts +14 -0
  136. package/src/client/index.ts +47 -29
  137. package/src/client/node-types.ts +2 -4
  138. package/src/client/repeat/BaseRepeatRangeNode.ts +3 -4
  139. package/src/client/repeat/RepeatInstanceNode.ts +4 -8
  140. package/src/client/repeat/RepeatRangeControlledNode.ts +2 -2
  141. package/src/client/repeat/RepeatRangeUncontrolledNode.ts +2 -2
  142. package/src/client/resources.ts +2 -2
  143. package/src/client/serialization/InstanceData.ts +16 -0
  144. package/src/client/serialization/InstanceFile.ts +9 -0
  145. package/src/client/serialization/InstancePayload.ts +126 -0
  146. package/src/client/serialization/InstancePayloadOptions.ts +28 -0
  147. package/src/client/serialization/InstanceState.ts +14 -0
  148. package/src/client/submission/{SubmissionDefinition.ts → SubmissionMeta.ts} +1 -1
  149. package/src/entrypoints/FormInstance.ts +55 -0
  150. package/src/entrypoints/FormResult/BaseFormResult.ts +40 -0
  151. package/src/entrypoints/FormResult/BaseInstantiableFormResult.ts +109 -0
  152. package/src/entrypoints/FormResult/FormFailureResult.ts +44 -0
  153. package/src/entrypoints/FormResult/FormSuccessResult.ts +25 -0
  154. package/src/entrypoints/FormResult/FormWarningResult.ts +25 -0
  155. package/src/entrypoints/createInstance.ts +23 -0
  156. package/src/entrypoints/editInstance.ts +24 -0
  157. package/src/entrypoints/index.ts +4 -0
  158. package/src/entrypoints/loadForm.ts +154 -0
  159. package/src/entrypoints/restoreInstance.ts +27 -0
  160. package/src/error/LoadFormFailureError.ts +114 -0
  161. package/src/error/MalformedInstanceDataError.ts +3 -0
  162. package/src/error/TemplatedNodeAttributeSerializationError.ts +24 -0
  163. package/src/index.ts +2 -46
  164. package/src/instance/Group.ts +16 -15
  165. package/src/instance/InputControl.ts +17 -11
  166. package/src/instance/ModelValue.ts +17 -11
  167. package/src/instance/Note.ts +10 -9
  168. package/src/instance/PrimaryInstance.ts +69 -31
  169. package/src/instance/RangeControl.ts +17 -11
  170. package/src/instance/RankControl.ts +28 -19
  171. package/src/instance/Root.ts +20 -31
  172. package/src/instance/SelectControl.ts +21 -12
  173. package/src/instance/Subtree.ts +16 -15
  174. package/src/instance/TriggerControl.ts +21 -12
  175. package/src/instance/abstract/DescendantNode.ts +12 -2
  176. package/src/instance/abstract/InstanceNode.ts +9 -5
  177. package/src/instance/abstract/ValueNode.ts +11 -13
  178. package/src/instance/children/DescendantNodeInitOptions.ts +35 -0
  179. package/src/instance/{children.ts → children/buildChildren.ts} +55 -48
  180. package/src/instance/children/childrenInitOptions.ts +117 -0
  181. package/src/instance/children/normalizeChildInitOptions.ts +332 -0
  182. package/src/instance/input/InitialInstanceState.ts +108 -0
  183. package/src/instance/input/InstanceAttachmentMap.ts +142 -0
  184. package/src/instance/internal-api/InstanceConfig.ts +3 -10
  185. package/src/instance/internal-api/InstanceValueContext.ts +9 -1
  186. package/src/instance/internal-api/serialization/ClientReactiveSerializableInstance.ts +20 -0
  187. package/src/instance/internal-api/serialization/ClientReactiveSerializableLeafNode.ts +43 -0
  188. package/src/instance/internal-api/serialization/ClientReactiveSerializableParentNode.ts +26 -0
  189. package/src/instance/internal-api/serialization/ClientReactiveSerializableTemplatedNode.ts +24 -0
  190. package/src/instance/internal-api/serialization/ClientReactiveSerializableValueNode.ts +28 -0
  191. package/src/instance/repeat/BaseRepeatRange.ts +19 -24
  192. package/src/instance/repeat/RepeatInstance.ts +26 -19
  193. package/src/instance/repeat/RepeatRangeControlled.ts +90 -17
  194. package/src/instance/repeat/RepeatRangeUncontrolled.ts +10 -9
  195. package/src/instance/resource.ts +14 -1
  196. package/src/instance/unsupported/UploadControl.ts +116 -5
  197. package/src/integration/xpath/EngineXPathEvaluator.ts +2 -2
  198. package/src/integration/xpath/adapter/kind.ts +1 -28
  199. package/src/integration/xpath/adapter/traversal.ts +2 -2
  200. package/src/integration/xpath/static-dom/StaticAttribute.ts +6 -5
  201. package/src/integration/xpath/static-dom/StaticDocument.ts +17 -16
  202. package/src/integration/xpath/static-dom/StaticElement.ts +196 -50
  203. package/src/integration/xpath/static-dom/StaticNode.ts +1 -1
  204. package/src/integration/xpath/static-dom/StaticParentNode.ts +22 -0
  205. package/src/integration/xpath/static-dom/staticNodeName.ts +20 -0
  206. package/src/lib/client-reactivity/instance-state/createNodeRangeInstanceState.ts +17 -0
  207. package/src/lib/client-reactivity/instance-state/createParentNodeInstanceState.ts +22 -0
  208. package/src/lib/client-reactivity/instance-state/createPrimaryInstanceState.ts +12 -0
  209. package/src/lib/client-reactivity/{submission/createRootSubmissionState.ts → instance-state/createRootInstanceState.ts} +4 -4
  210. package/src/lib/client-reactivity/instance-state/createTemplatedNodeInstanceState.ts +31 -0
  211. package/src/lib/client-reactivity/instance-state/createValueNodeInstanceState.ts +21 -0
  212. package/src/lib/client-reactivity/instance-state/prepareInstancePayload.ts +173 -0
  213. package/src/lib/codecs/TempUnsupportedControlCodec.ts +32 -0
  214. package/src/lib/names/UnprefixedXFormsName.ts +12 -0
  215. package/src/lib/reactivity/createInstanceValueState.ts +27 -51
  216. package/src/parse/model/ItextTranslationsDefinition.ts +79 -0
  217. package/src/parse/model/LeafNodeDefinition.ts +3 -5
  218. package/src/parse/model/ModelDefinition.ts +36 -3
  219. package/src/parse/model/NodeDefinition.ts +19 -45
  220. package/src/parse/model/NoteNodeDefinition.ts +4 -3
  221. package/src/parse/model/RangeNodeDefinition.ts +3 -2
  222. package/src/parse/model/RepeatDefinition.ts +382 -0
  223. package/src/parse/model/RootAttributeDefinition.ts +6 -7
  224. package/src/parse/model/RootAttributeMap.ts +15 -10
  225. package/src/parse/model/RootDefinition.ts +17 -19
  226. package/src/parse/model/SecondaryInstance/SecondaryInstancesDefinition.ts +23 -2
  227. package/src/parse/model/SecondaryInstance/assertSecondaryInstanceDefinition.ts +14 -0
  228. package/src/parse/model/SecondaryInstance/defineSecondaryInstance.ts +32 -0
  229. package/src/parse/model/SecondaryInstance/sources/BlankSecondaryInstanceSource.ts +3 -24
  230. package/src/parse/model/SecondaryInstance/sources/CSVExternalSecondaryInstance.ts +33 -86
  231. package/src/parse/model/SecondaryInstance/sources/GeoJSONExternalSecondaryInstance.ts +64 -137
  232. package/src/parse/model/SecondaryInstance/sources/InternalSecondaryInstanceSource.ts +9 -7
  233. package/src/parse/model/SecondaryInstance/sources/SecondaryInstanceSource.ts +1 -1
  234. package/src/parse/model/SecondaryInstance/sources/XMLExternalSecondaryInstanceSource.ts +7 -7
  235. package/src/parse/model/{FormSubmissionDefinition.ts → SubmissionDefinition.ts} +2 -2
  236. package/src/parse/model/SubtreeDefinition.ts +4 -5
  237. package/src/parse/model/nodeDefinitionMap.ts +34 -0
  238. package/src/parse/shared/parseInstanceXML.ts +79 -0
  239. package/src/parse/shared/parseStaticDocumentFromDOMSubtree.ts +45 -130
  240. package/dist/client/EngineConfig.d.ts +0 -79
  241. package/dist/client/submission/SubmissionData.d.ts +0 -7
  242. package/dist/client/submission/SubmissionInstanceFile.d.ts +0 -6
  243. package/dist/client/submission/SubmissionOptions.d.ts +0 -23
  244. package/dist/client/submission/SubmissionResult.d.ts +0 -91
  245. package/dist/client/submission/SubmissionState.d.ts +0 -12
  246. package/dist/instance/abstract/UnsupportedControl.d.ts +0 -54
  247. package/dist/instance/index.d.ts +0 -8
  248. package/dist/instance/internal-api/ValueContext.d.ts +0 -23
  249. package/dist/instance/internal-api/submission/ClientReactiveSubmittableInstance.d.ts +0 -14
  250. package/dist/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.d.ts +0 -32
  251. package/dist/instance/internal-api/submission/ClientReactiveSubmittableParentNode.d.ts +0 -19
  252. package/dist/instance/internal-api/submission/ClientReactiveSubmittableValueNode.d.ts +0 -18
  253. package/dist/lib/client-reactivity/submission/createInstanceSubmissionState.d.ts +0 -3
  254. package/dist/lib/client-reactivity/submission/createLeafNodeSubmissionState.d.ts +0 -3
  255. package/dist/lib/client-reactivity/submission/createNodeRangeSubmissionState.d.ts +0 -4
  256. package/dist/lib/client-reactivity/submission/createParentNodeSubmissionState.d.ts +0 -4
  257. package/dist/lib/client-reactivity/submission/createRootSubmissionState.d.ts +0 -3
  258. package/dist/lib/client-reactivity/submission/createValueNodeSubmissionState.d.ts +0 -3
  259. package/dist/lib/client-reactivity/submission/prepareSubmission.d.ts +0 -8
  260. package/dist/lib/reactivity/createValueState.d.ts +0 -40
  261. package/dist/parse/model/ItextTranslation/ItextTranslationDefinition.d.ts +0 -4
  262. package/dist/parse/model/ItextTranslation/ItextTranslationRootDefinition.d.ts +0 -9
  263. package/dist/parse/model/ItextTranslation/ItextTranslationsDefinition.d.ts +0 -8
  264. package/dist/parse/model/RepeatInstanceDefinition.d.ts +0 -17
  265. package/dist/parse/model/RepeatRangeDefinition.d.ts +0 -32
  266. package/dist/parse/model/RepeatTemplateDefinition.d.ts +0 -31
  267. package/dist/parse/model/SecondaryInstance/SecondaryInstanceDefinition.d.ts +0 -4
  268. package/dist/parse/model/SecondaryInstance/SecondaryInstanceRootDefinition.d.ts +0 -7
  269. package/src/client/EngineConfig.ts +0 -84
  270. package/src/client/submission/SubmissionData.ts +0 -12
  271. package/src/client/submission/SubmissionInstanceFile.ts +0 -9
  272. package/src/client/submission/SubmissionOptions.ts +0 -28
  273. package/src/client/submission/SubmissionResult.ts +0 -124
  274. package/src/client/submission/SubmissionState.ts +0 -14
  275. package/src/instance/abstract/UnsupportedControl.ts +0 -175
  276. package/src/instance/index.ts +0 -55
  277. package/src/instance/internal-api/ValueContext.ts +0 -28
  278. package/src/instance/internal-api/submission/ClientReactiveSubmittableInstance.ts +0 -20
  279. package/src/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.ts +0 -43
  280. package/src/instance/internal-api/submission/ClientReactiveSubmittableParentNode.ts +0 -26
  281. package/src/instance/internal-api/submission/ClientReactiveSubmittableValueNode.ts +0 -24
  282. package/src/lib/client-reactivity/submission/createInstanceSubmissionState.ts +0 -12
  283. package/src/lib/client-reactivity/submission/createLeafNodeSubmissionState.ts +0 -20
  284. package/src/lib/client-reactivity/submission/createNodeRangeSubmissionState.ts +0 -17
  285. package/src/lib/client-reactivity/submission/createParentNodeSubmissionState.ts +0 -22
  286. package/src/lib/client-reactivity/submission/createValueNodeSubmissionState.ts +0 -21
  287. package/src/lib/client-reactivity/submission/prepareSubmission.ts +0 -172
  288. package/src/lib/reactivity/createValueState.ts +0 -200
  289. package/src/parse/model/ItextTranslation/ItextTranslationDefinition.ts +0 -4
  290. package/src/parse/model/ItextTranslation/ItextTranslationRootDefinition.ts +0 -42
  291. package/src/parse/model/ItextTranslation/ItextTranslationsDefinition.ts +0 -31
  292. package/src/parse/model/RepeatInstanceDefinition.ts +0 -38
  293. package/src/parse/model/RepeatRangeDefinition.ts +0 -98
  294. package/src/parse/model/RepeatTemplateDefinition.ts +0 -149
  295. package/src/parse/model/SecondaryInstance/SecondaryInstanceDefinition.ts +0 -4
  296. package/src/parse/model/SecondaryInstance/SecondaryInstanceRootDefinition.ts +0 -12
@@ -0,0 +1,117 @@
1
+ import type { StaticDocument } from '../../integration/xpath/static-dom/StaticDocument.ts';
2
+ import type { StaticElement } from '../../integration/xpath/static-dom/StaticElement.ts';
3
+ import type { ModelDefinition } from '../../parse/model/ModelDefinition.ts';
4
+ import type {
5
+ AnyNodeDefinition,
6
+ ChildNodeDefinition,
7
+ NodeDefinition,
8
+ } from '../../parse/model/NodeDefinition.ts';
9
+ import type { InstanceNode } from '../abstract/InstanceNode.ts';
10
+ import type { GeneralParentNode } from '../hierarchy.ts';
11
+ import type { DescendantNodeInitOptions } from './DescendantNodeInitOptions.ts';
12
+ import { normalizeChildInitOptions } from './normalizeChildInitOptions.ts';
13
+
14
+ /**
15
+ * Child nodesets are collected from the {@link parent}'s
16
+ * {@link NodeDefinition.template}, ensuring that we produce
17
+ * {@link InstanceNode}s for every **model-defined** node, even if a
18
+ * corresponding node was not serialized in a {@link parent.instanceNode}.
19
+ *
20
+ * In other words, by referencing the model-defined template, we are able to
21
+ * reproduce nodes which were omitted as non-relevant in a prior serialization
22
+ * and/or submission.
23
+ *
24
+ * @todo Since we're building an instance node's children from the nodesets of
25
+ * the model-defined node's children, we are _implicitly dropping_ any excess
26
+ * nodes from non-model instance data. That's probably the right behavior, but
27
+ * we may want to warn for such nodes if/when we do drop them.
28
+ */
29
+ const collectModelChildNodesets = (parentTemplate: StaticElement): readonly string[] => {
30
+ const nodesets = parentTemplate.childElements.map(({ nodeset }) => {
31
+ return nodeset;
32
+ });
33
+
34
+ return Array.from(new Set(nodesets));
35
+ };
36
+
37
+ type InstanceNodesByNodeset = ReadonlyMap<string, readonly [StaticElement, ...StaticElement[]]>;
38
+
39
+ const groupChildElementsByNodeset = (
40
+ parent: StaticDocument | StaticElement
41
+ ): InstanceNodesByNodeset => {
42
+ const result = new Map<string, [StaticElement, ...StaticElement[]]>();
43
+
44
+ for (const child of parent.childElements) {
45
+ const { nodeset } = child;
46
+ const group = result.get(nodeset);
47
+
48
+ if (group == null) {
49
+ result.set(nodeset, [child]);
50
+ } else {
51
+ group.push(child);
52
+ }
53
+ }
54
+
55
+ return result;
56
+ };
57
+
58
+ type AssertChildNodeDefinition = (
59
+ definition: AnyNodeDefinition,
60
+ childNodeset: string
61
+ ) => asserts definition is ChildNodeDefinition;
62
+
63
+ const assertChildNodeDefinition: AssertChildNodeDefinition = (definition, childNodeset) => {
64
+ if (definition.type === 'root') {
65
+ throw new Error(`Unexpected root definition for child nodeset: ${childNodeset}`);
66
+ }
67
+ };
68
+
69
+ export interface ChildrenInitOptions {
70
+ readonly parent: GeneralParentNode;
71
+ readonly model: ModelDefinition;
72
+ readonly children: readonly DescendantNodeInitOptions[];
73
+ }
74
+
75
+ export const childrenInitOptions = (parent: GeneralParentNode): ChildrenInitOptions => {
76
+ const { model } = parent.rootDocument;
77
+ const childNodesets = collectModelChildNodesets(parent.definition.template);
78
+
79
+ let instanceChildren: InstanceNodesByNodeset | null;
80
+
81
+ if (parent.instanceNode == null) {
82
+ instanceChildren = null;
83
+ } else {
84
+ instanceChildren = groupChildElementsByNodeset(parent.instanceNode);
85
+ }
86
+
87
+ const children = childNodesets.map((childNodeset) => {
88
+ const definition = model.getNodeDefinition(childNodeset);
89
+
90
+ assertChildNodeDefinition(definition, childNodeset);
91
+
92
+ /**
93
+ * Get children of the target nodeset from {@link parent.instanceNode}, if
94
+ * that node exists, and if children with that nodeset exist.
95
+ *
96
+ * If either does not exist (e.g. it was omitted as non-relevant in a prior
97
+ * serialization), we continue to reference model-defined templates as we
98
+ * recurse down the {@link InstanceNode} subtree.
99
+ *
100
+ * @see {@link childNodesets}
101
+ */
102
+ const instanceNodes = instanceChildren?.get(childNodeset) ?? [];
103
+
104
+ return {
105
+ childNodeset,
106
+ definition,
107
+ instanceNodes,
108
+ };
109
+ });
110
+ const baseResult: ChildrenInitOptions = {
111
+ parent,
112
+ model,
113
+ children,
114
+ };
115
+
116
+ return normalizeChildInitOptions(baseResult);
117
+ };
@@ -0,0 +1,332 @@
1
+ import {
2
+ OPENROSA_XFORMS_NAMESPACE_URI,
3
+ XFORMS_NAMESPACE_URI,
4
+ } from '@getodk/common/constants/xmlns.ts';
5
+ import type { GroupDefinition } from '../../client/GroupNode.ts';
6
+ import type { SubtreeDefinition } from '../../client/SubtreeNode.ts';
7
+ import { ErrorProductionDesignPendingError } from '../../error/ErrorProductionDesignPendingError.ts';
8
+ import { StaticDocument } from '../../integration/xpath/static-dom/StaticDocument.ts';
9
+ import type { StaticLeafElement } from '../../integration/xpath/static-dom/StaticElement.ts';
10
+ import { StaticElement } from '../../integration/xpath/static-dom/StaticElement.ts';
11
+ import type { NamespaceURL } from '../../lib/names/NamespaceURL.ts';
12
+ import type { QualifiedName } from '../../lib/names/QualifiedName.ts';
13
+ import { LeafNodeDefinition } from '../../parse/model/LeafNodeDefinition.ts';
14
+ import type { SubtreeDefinition as ModelSubtreeDefinition } from '../../parse/model/SubtreeDefinition.ts';
15
+ import type { XFormDOM } from '../../parse/XFormDOM.ts';
16
+ import type { Group } from '../Group.ts';
17
+ import type { GeneralParentNode } from '../hierarchy.ts';
18
+ import type { PrimaryInstance } from '../PrimaryInstance.ts';
19
+ import type { Root } from '../Root.ts';
20
+ import type { Subtree } from '../Subtree.ts';
21
+ import type { ChildrenInitOptions } from './childrenInitOptions.ts';
22
+ import type { DescendantNodeInitOptions } from './DescendantNodeInitOptions.ts';
23
+
24
+ const META_LOCAL_NAME = 'meta';
25
+ const INSTANCE_ID_LOCAL_NAME = 'instanceID';
26
+ const DEPRECATED_ID_LOCAL_NAME = 'deprecatedID';
27
+
28
+ type MetaLocalName = typeof META_LOCAL_NAME;
29
+
30
+ const META_NAMESPACE_URIS = [OPENROSA_XFORMS_NAMESPACE_URI, XFORMS_NAMESPACE_URI] as const;
31
+
32
+ type MetaNamespaceURIValue = (typeof META_NAMESPACE_URIS)[number];
33
+
34
+ interface MetaNamespaceURI extends NamespaceURL {
35
+ readonly href: MetaNamespaceURIValue;
36
+ }
37
+
38
+ interface MetaNamespacedName extends QualifiedName {
39
+ readonly namespaceURI: MetaNamespaceURI;
40
+ }
41
+
42
+ const isMetaNamespacedName = (
43
+ qualifiedName: QualifiedName
44
+ ): qualifiedName is MetaNamespacedName => {
45
+ const namespaceURI = qualifiedName.namespaceURI?.href;
46
+
47
+ return (
48
+ namespaceURI != null && META_NAMESPACE_URIS.includes(namespaceURI as MetaNamespaceURIValue)
49
+ );
50
+ };
51
+
52
+ interface MetaName extends MetaNamespacedName {
53
+ readonly localName: MetaLocalName;
54
+ }
55
+
56
+ const isMetaName = (qualifiedName: QualifiedName): qualifiedName is MetaName => {
57
+ return isMetaNamespacedName(qualifiedName) && qualifiedName.localName === META_LOCAL_NAME;
58
+ };
59
+
60
+ const isDirectRootDescendant = (parent: GeneralParentNode) => {
61
+ return parent.parent === parent.root;
62
+ };
63
+
64
+ interface BaseMetaDefinition {
65
+ readonly qualifiedName: MetaName;
66
+ }
67
+
68
+ const isBaseMetaDefinition = <T extends ModelSubtreeDefinition>(
69
+ definition: T
70
+ ): definition is BaseMetaDefinition & T => {
71
+ return isMetaName(definition.qualifiedName);
72
+ };
73
+
74
+ interface MetaSubtreeDefinition extends SubtreeDefinition {
75
+ readonly qualifiedName: MetaName;
76
+ }
77
+
78
+ interface MetaSubtree extends Subtree {
79
+ readonly parent: Root;
80
+ readonly definition: MetaSubtreeDefinition;
81
+ }
82
+
83
+ interface MetaGroupDefinition extends GroupDefinition {
84
+ readonly qualifiedName: MetaName;
85
+ }
86
+
87
+ interface MetaGroup extends Group {
88
+ readonly parent: Root;
89
+ readonly definition: MetaGroupDefinition;
90
+ }
91
+
92
+ type MetaSubroot = MetaGroup | MetaSubtree;
93
+
94
+ interface MetaSubrootInitOptions extends ChildrenInitOptions {
95
+ readonly parent: MetaGroup | MetaSubtree;
96
+ }
97
+
98
+ const isMetaSubroot = (options: ChildrenInitOptions): options is MetaSubrootInitOptions => {
99
+ const { nodeType } = options.parent;
100
+
101
+ return (
102
+ (nodeType === 'subtree' || nodeType === 'group') &&
103
+ isDirectRootDescendant(options.parent) &&
104
+ isBaseMetaDefinition(options.parent.definition)
105
+ );
106
+ };
107
+
108
+ interface EditModeInstance extends PrimaryInstance {
109
+ readonly initializationMode: 'edit';
110
+ }
111
+
112
+ const isEditModeInstance = (
113
+ primaryInstance: PrimaryInstance
114
+ ): primaryInstance is EditModeInstance => {
115
+ return primaryInstance.initializationMode === 'edit';
116
+ };
117
+
118
+ interface EditModeInstanceDescendant {
119
+ readonly rootDocument: EditModeInstance;
120
+ }
121
+
122
+ type EditModeMetaSubroot = EditModeInstanceDescendant & MetaSubroot;
123
+
124
+ interface EditModeMetaSubrootInitOptions extends MetaSubrootInitOptions {
125
+ readonly parent: EditModeMetaSubroot;
126
+ }
127
+
128
+ const isEditModeMetaSubroot = (
129
+ subroot: MetaSubrootInitOptions
130
+ ): subroot is EditModeMetaSubrootInitOptions => {
131
+ return isEditModeInstance(subroot.parent.rootDocument);
132
+ };
133
+
134
+ interface LeafNodeInitOptions extends DescendantNodeInitOptions {
135
+ readonly instanceNodes: readonly [StaticLeafElement];
136
+ }
137
+
138
+ const isLeafNodeInitOptions = (
139
+ options: DescendantNodeInitOptions
140
+ ): options is LeafNodeInitOptions => {
141
+ const { instanceNodes } = options;
142
+ const [instanceNode, ...rest] = instanceNodes;
143
+
144
+ return instanceNode != null && rest.length === 0 && instanceNode.isLeafElement();
145
+ };
146
+
147
+ type MetaLeafChildEntry = readonly [index: number, child: LeafNodeInitOptions];
148
+
149
+ const findMetaLeafChild = (
150
+ subroot: MetaSubrootInitOptions,
151
+ localName: string
152
+ ): MetaLeafChildEntry | null => {
153
+ const metaName = subroot.parent.definition.qualifiedName satisfies MetaName;
154
+ const namespaceURI = metaName.namespaceURI.href satisfies MetaNamespaceURIValue;
155
+
156
+ const result = Array.from(subroot.children.entries()).find(
157
+ (entry): entry is [number, LeafNodeInitOptions] => {
158
+ const [, child] = entry;
159
+
160
+ if (!isLeafNodeInitOptions(child)) {
161
+ return false;
162
+ }
163
+
164
+ const [instanceNode] = child.instanceNodes;
165
+ const { qualifiedName } = instanceNode;
166
+
167
+ return (
168
+ qualifiedName.localName === localName && qualifiedName.namespaceURI?.href === namespaceURI
169
+ );
170
+ }
171
+ );
172
+
173
+ return result ?? null;
174
+ };
175
+
176
+ const getInstanceIDValue = (subroot: MetaSubrootInitOptions): string | null => {
177
+ const [, child = null] = findMetaLeafChild(subroot, INSTANCE_ID_LOCAL_NAME) ?? [];
178
+
179
+ if (child == null) {
180
+ return null;
181
+ }
182
+
183
+ const [instanceIDNode] = child.instanceNodes;
184
+
185
+ return instanceIDNode.value satisfies string;
186
+ };
187
+
188
+ type AssertStaticLeafElement = (element: StaticElement) => asserts element is StaticLeafElement;
189
+
190
+ const assertStaticLeafElement: AssertStaticLeafElement = (element) => {
191
+ if (!element.isLeafElement()) {
192
+ throw new ErrorProductionDesignPendingError(
193
+ `Expected a leaf element, got a non-leaf element at nodeset: ${element.nodeset}`
194
+ );
195
+ }
196
+ };
197
+
198
+ /**
199
+ * @todo this name and signature are general, for the same reasons as the TODO
200
+ * on {@link populateDeprecatedID}.
201
+ */
202
+ const buildMetaValueElement = (
203
+ subroot: MetaSubrootInitOptions,
204
+ localName: string,
205
+ value: string
206
+ ): StaticLeafElement => {
207
+ const { qualifiedName, nodeset } = subroot.parent.definition;
208
+ const { namespaceURI, prefix } = qualifiedName;
209
+ const { root } = new StaticDocument({
210
+ documentRoot: {
211
+ name: {
212
+ namespaceURI,
213
+ prefix,
214
+ localName,
215
+ },
216
+ children: [value],
217
+ },
218
+ nodesetPrefix: nodeset,
219
+ });
220
+
221
+ assertStaticLeafElement(root);
222
+
223
+ return root;
224
+ };
225
+
226
+ const buildDeprecatedIDDefinition = (
227
+ subroot: EditModeMetaSubrootInitOptions,
228
+ instanceNode: StaticLeafElement
229
+ ): LeafNodeDefinition => {
230
+ const nodeset = instanceNode.nodeset;
231
+ const bind = subroot.model.binds.getOrCreateBindDefinition(nodeset);
232
+
233
+ return new LeafNodeDefinition(subroot.parent.definition, bind, null, instanceNode);
234
+ };
235
+
236
+ const buildDeprecatedID = (
237
+ subroot: EditModeMetaSubrootInitOptions,
238
+ value: string
239
+ ): LeafNodeInitOptions => {
240
+ const instanceNode = buildMetaValueElement(subroot, DEPRECATED_ID_LOCAL_NAME, value);
241
+ const definition = buildDeprecatedIDDefinition(subroot, instanceNode);
242
+
243
+ return {
244
+ childNodeset: instanceNode.nodeset,
245
+ definition,
246
+ instanceNodes: [instanceNode],
247
+ };
248
+ };
249
+
250
+ const updateDeprecatedID = (
251
+ subroot: EditModeMetaSubrootInitOptions,
252
+ child: LeafNodeInitOptions,
253
+ value: string
254
+ ): LeafNodeInitOptions => {
255
+ const instanceNode = buildMetaValueElement(subroot, DEPRECATED_ID_LOCAL_NAME, value);
256
+
257
+ return {
258
+ childNodeset: child.childNodeset,
259
+ definition: child.definition,
260
+ instanceNodes: [instanceNode],
261
+ };
262
+ };
263
+
264
+ /**
265
+ * Should be: values.with(index, value)
266
+ *
267
+ * @todo update tsconfig.json `lib` throughout project!
268
+ */
269
+ const replace = <T>(values: readonly T[], index: number, value: T): readonly T[] => {
270
+ const results = values.slice();
271
+
272
+ results.splice(index, 1, value);
273
+
274
+ return results;
275
+ };
276
+
277
+ const replaceOrConcat = <T>(values: readonly T[], index: number | null, value: T): readonly T[] => {
278
+ if (index == null) {
279
+ return values.concat(value);
280
+ }
281
+
282
+ return replace(values, index, value);
283
+ };
284
+
285
+ /**
286
+ * @todo Whenever we have bandwidth to start migrating away from DOM usage in
287
+ * {@link XFormDOM}, this is a good place to start for logic equivalent to the
288
+ * hacky normalization for `instanceID`.
289
+ */
290
+ const populateDeprecatedID = (
291
+ subroot: EditModeMetaSubrootInitOptions
292
+ ): EditModeMetaSubrootInitOptions => {
293
+ const value = getInstanceIDValue(subroot);
294
+
295
+ if (value == null) {
296
+ return subroot;
297
+ }
298
+
299
+ const [index, currentDeprecatedID] = findMetaLeafChild(subroot, DEPRECATED_ID_LOCAL_NAME) ?? [
300
+ null,
301
+ ];
302
+
303
+ let deprecatedID: LeafNodeInitOptions;
304
+
305
+ if (currentDeprecatedID == null) {
306
+ deprecatedID = buildDeprecatedID(subroot, value);
307
+ } else {
308
+ deprecatedID = updateDeprecatedID(subroot, currentDeprecatedID, value);
309
+ }
310
+
311
+ return {
312
+ model: subroot.model,
313
+ parent: subroot.parent,
314
+ children: replaceOrConcat(subroot.children, index, deprecatedID),
315
+ };
316
+ };
317
+
318
+ const normalizeMetaSubroot = (subroot: MetaSubrootInitOptions): MetaSubrootInitOptions => {
319
+ if (isEditModeMetaSubroot(subroot)) {
320
+ return populateDeprecatedID(subroot);
321
+ }
322
+
323
+ return subroot;
324
+ };
325
+
326
+ export const normalizeChildInitOptions = (options: ChildrenInitOptions): ChildrenInitOptions => {
327
+ if (isMetaSubroot(options)) {
328
+ return normalizeMetaSubroot(options);
329
+ }
330
+
331
+ return options;
332
+ };
@@ -0,0 +1,108 @@
1
+ import { getBlobText } from '@getodk/common/lib/web-compat/blob.ts';
2
+ import { INSTANCE_FILE_NAME } from '../../client/constants.ts';
3
+ import type {
4
+ EditFormInstanceInput,
5
+ ResolvableFormInstanceInput,
6
+ } from '../../client/form/EditFormInstance.ts';
7
+ import type { InstanceData } from '../../client/serialization/InstanceData.ts';
8
+ import { ErrorProductionDesignPendingError } from '../../error/ErrorProductionDesignPendingError.ts';
9
+ import type { StaticDocument } from '../../integration/xpath/static-dom/StaticDocument.ts';
10
+ import type { ModelDefinition } from '../../parse/model/ModelDefinition.ts';
11
+ import { parseInstanceXML } from '../../parse/shared/parseInstanceXML.ts';
12
+ import type { XFormDOM } from '../../parse/XFormDOM.ts';
13
+ import { InstanceAttachmentMap } from './InstanceAttachmentMap.ts';
14
+
15
+ export type InitialInstanceStateSources = readonly [InstanceData, ...InstanceData[]];
16
+
17
+ interface InitialInstanceStateOptions {
18
+ readonly instanceXML: string;
19
+ readonly attachments: InstanceAttachmentMap;
20
+ }
21
+
22
+ const resolveInstanceXML = async (input: ResolvableFormInstanceInput): Promise<string> => {
23
+ const instanceResult = await input.resolveInstance();
24
+
25
+ if (typeof instanceResult === 'string') {
26
+ return instanceResult;
27
+ }
28
+
29
+ if (instanceResult instanceof Blob) {
30
+ return getBlobText(instanceResult);
31
+ }
32
+
33
+ return instanceResult.text();
34
+ };
35
+
36
+ const parseInstanceDocument = (model: ModelDefinition, instanceXML: string): StaticDocument => {
37
+ const doc = parseInstanceXML(model, instanceXML);
38
+
39
+ /**
40
+ * Note: this is presently inferred as nullable. Its presence is _currently_
41
+ * enforced by {@link XFormDOM}, though that's definitely not ideal!
42
+ *
43
+ * @todo we would probably benefit from a single parse function responsible
44
+ * for both instance documents (form and input), with all `id` enforcement
45
+ * handled in one place (presence in both cases, matching in case of form +
46
+ * input). We could then also express this at the type level without weird DOM
47
+ * shenanigans in {@link XFormDOM} (which aren't likely to stick around in
48
+ * their current state forever!).
49
+ */
50
+ const expectedId = model.instance.root.getAttributeValue('id');
51
+ const actualId = doc.root.getAttributeValue('id');
52
+
53
+ if (expectedId !== actualId) {
54
+ throw new ErrorProductionDesignPendingError(
55
+ `Invalid instance input. Expected instance id to be "${expectedId}", got: "${actualId}"`
56
+ );
57
+ }
58
+
59
+ return doc;
60
+ };
61
+
62
+ export class InitialInstanceState {
63
+ static async from(
64
+ model: ModelDefinition,
65
+ data: InitialInstanceStateSources
66
+ ): Promise<InitialInstanceState> {
67
+ return this.resolve(model, {
68
+ inputType: 'FORM_INSTANCE_INPUT_RESOLVED',
69
+ data,
70
+ });
71
+ }
72
+
73
+ static async resolve(
74
+ model: ModelDefinition,
75
+ input: EditFormInstanceInput
76
+ ): Promise<InitialInstanceState> {
77
+ if (input.inputType === 'FORM_INSTANCE_INPUT_RESOLVED') {
78
+ const { data } = input;
79
+ const [instanceData] = data;
80
+ const instanceFile = instanceData.get(INSTANCE_FILE_NAME);
81
+ const instanceXML = await getBlobText(instanceFile);
82
+ const attachments = InstanceAttachmentMap.from(data);
83
+
84
+ return new this(model, {
85
+ instanceXML,
86
+ attachments,
87
+ });
88
+ }
89
+
90
+ const [instanceXML, attachments] = await Promise.all([
91
+ resolveInstanceXML(input),
92
+ InstanceAttachmentMap.resolve(input.attachments),
93
+ ]);
94
+
95
+ return new this(model, {
96
+ instanceXML,
97
+ attachments,
98
+ });
99
+ }
100
+
101
+ readonly document: StaticDocument;
102
+ readonly attachments: InstanceAttachmentMap;
103
+
104
+ private constructor(model: ModelDefinition, options: InitialInstanceStateOptions) {
105
+ this.document = parseInstanceDocument(model, options.instanceXML);
106
+ this.attachments = options.attachments;
107
+ }
108
+ }
@@ -0,0 +1,142 @@
1
+ import { INSTANCE_FILE_NAME } from '../../client/constants.ts';
2
+ import type { ResolvableInstanceAttachmentsMap } from '../../client/form/EditFormInstance.ts';
3
+ import { MalformedInstanceDataError } from '../../error/MalformedInstanceDataError.ts';
4
+
5
+ type InstanceAttachmentMapSourceEntry = readonly [key: string, value: FormDataEntryValue];
6
+
7
+ interface InstanceAttachmentMapSource {
8
+ entries(): Iterable<InstanceAttachmentMapSourceEntry>;
9
+ }
10
+
11
+ type InstanceAttachmentMapSources = readonly [
12
+ InstanceAttachmentMapSource,
13
+ ...InstanceAttachmentMapSource[],
14
+ ];
15
+
16
+ /**
17
+ * @todo This currently short-circuits if there are actually any instance
18
+ * attachments to resolve. As described below, much of the approach is pretty
19
+ * naive now anyway, and none of it is really "ready" until we have something to
20
+ * actually _use the instance attachments_ once they're resolved! When we are
21
+ * ready, the functionality can be unblocked as in
22
+ * {@link https://github.com/getodk/web-forms/commit/88ee1b91c1f68d53ce9ba551bab334852e1e60cd | this commit}.
23
+ *
24
+ * @todo Everything about this is incredibly naive! We should almost certainly
25
+ * do _at least_ the following:
26
+ *
27
+ * - Limit how many attachments we attempt to resolve concurrently
28
+ * - Lazy resolution of large attachments (i.e. probably streaming, maybe range
29
+ * requests, ?)
30
+ *
31
+ * @todo Once lazy resolution is a thing, we will **also** need a clear path
32
+ * from there to eager resolution (i.e. for offline caching: it doesn't make
33
+ * sense to cache a stream in progress, as it won't load the resource once the
34
+ * user actually is offline/lacks network access). This may be something we can
35
+ * evolve gradually!
36
+ */
37
+ const resolveInstanceAttachmentMapSource = async (
38
+ input: ResolvableInstanceAttachmentsMap
39
+ ): Promise<InstanceAttachmentMapSource> => {
40
+ const inputEntries = Array.from(input.entries());
41
+
42
+ if (inputEntries.length > 0) {
43
+ const fileNames = Array.from(input.keys());
44
+ const errors = fileNames.map((fileName) => {
45
+ return new Error(`Failed to resolve instance attachment with file name "${fileName}"`);
46
+ });
47
+
48
+ throw new AggregateError(errors, 'Not implemented: instance attachment resource resolution');
49
+ }
50
+
51
+ const entries = await Promise.all<InstanceAttachmentMapSourceEntry>(
52
+ inputEntries.map(async ([fileName, resolveAttachment]) => {
53
+ const response = await resolveAttachment();
54
+ const blob = await response.blob();
55
+ const value = new File([blob], fileName);
56
+
57
+ return [fileName, value] as const;
58
+ })
59
+ );
60
+
61
+ return { entries: () => entries };
62
+ };
63
+
64
+ interface KeyedInstanceDataFile<Key extends string> extends File {
65
+ readonly name: Key;
66
+ }
67
+
68
+ type AssertKeyedInstanceDataFile = <Key extends string>(
69
+ key: Key,
70
+ file: File
71
+ ) => asserts file is KeyedInstanceDataFile<Key>;
72
+
73
+ const assertKeyedInstanceDataFile: AssertKeyedInstanceDataFile = (key, file) => {
74
+ if (file.name !== key) {
75
+ throw new MalformedInstanceDataError(
76
+ `Unexpected InstanceData file. Expected file name to match key, got key: ${JSON.stringify(key)} and file name: ${JSON.stringify(file.name)}`
77
+ );
78
+ }
79
+ };
80
+
81
+ type UnknownInstanceDataEntry = readonly [key: string, value: FormDataEntryValue];
82
+
83
+ type KeyedInstanceDataEntry<Key extends string> = readonly [key: Key, KeyedInstanceDataFile<Key>];
84
+
85
+ type AssertInstanceDataEntry = <Key extends string>(
86
+ entry: UnknownInstanceDataEntry
87
+ ) => asserts entry is KeyedInstanceDataEntry<Key>;
88
+
89
+ const assertInstanceDataEntry: AssertInstanceDataEntry = (entry) => {
90
+ const [key, value] = entry;
91
+
92
+ if (!(value instanceof File)) {
93
+ throw new MalformedInstanceDataError(
94
+ `Unexpected non-file attachment in InstanceData for key: ${key}`
95
+ );
96
+ }
97
+
98
+ assertKeyedInstanceDataFile(key, value);
99
+ };
100
+
101
+ export class InstanceAttachmentMap extends Map<string, File> {
102
+ static from(sources: InstanceAttachmentMapSources): InstanceAttachmentMap {
103
+ return new this(sources);
104
+ }
105
+
106
+ /**
107
+ * @todo
108
+ * @see {@link resolveInstanceAttachmentMapSource}
109
+ */
110
+ static async resolve(input: ResolvableInstanceAttachmentsMap): Promise<InstanceAttachmentMap> {
111
+ const source = await resolveInstanceAttachmentMapSource(input);
112
+
113
+ return new this([source]);
114
+ }
115
+
116
+ private constructor(sources: InstanceAttachmentMapSources) {
117
+ super();
118
+
119
+ for (const source of sources) {
120
+ for (const entry of source.entries()) {
121
+ const [key] = entry;
122
+
123
+ // Skip the instance XML file: it's not an attachment!
124
+ if (key === INSTANCE_FILE_NAME) {
125
+ continue;
126
+ }
127
+
128
+ if (this.has(key)) {
129
+ throw new MalformedInstanceDataError(
130
+ `Unexpected InstanceData entry. Duplicate instance attachment for key: ${JSON.stringify(key)}`
131
+ );
132
+ }
133
+
134
+ assertInstanceDataEntry(entry);
135
+
136
+ const [, value] = entry;
137
+
138
+ this.set(key, value);
139
+ }
140
+ }
141
+ }
142
+ }