@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.
- package/dist/client/BaseNode.d.ts +14 -3
- package/dist/client/BaseValueNode.d.ts +32 -0
- package/dist/client/EngineConfig.d.ts +49 -48
- package/dist/client/InputNode.d.ts +53 -0
- package/dist/client/ModelValueNode.d.ts +12 -7
- package/dist/client/NoteNode.d.ts +2 -2
- package/dist/client/RootNode.d.ts +34 -7
- package/dist/client/SelectNode.d.ts +2 -2
- package/dist/client/ValueType.d.ts +3 -0
- package/dist/client/constants.d.ts +50 -0
- package/dist/client/hierarchy.d.ts +4 -4
- package/dist/client/identity.d.ts +14 -0
- package/dist/client/node-types.d.ts +2 -1
- package/dist/client/resources.d.ts +93 -0
- package/dist/client/submission/SubmissionData.d.ts +7 -0
- package/dist/client/submission/SubmissionDefinition.d.ts +14 -0
- package/dist/client/submission/SubmissionInstanceFile.d.ts +6 -0
- package/dist/client/submission/SubmissionOptions.d.ts +23 -0
- package/dist/client/submission/SubmissionResult.d.ts +91 -0
- package/dist/client/submission/SubmissionState.d.ts +12 -0
- package/dist/client/validation.d.ts +2 -2
- package/dist/error/ErrorProductionDesignPendingError.d.ts +7 -0
- package/dist/error/ValueTypeInvariantError.d.ts +17 -0
- package/dist/error/XPathFunctionalityError.d.ts +14 -0
- package/dist/error/XPathFunctionalityNotSupportedError.d.ts +7 -0
- package/dist/error/XPathFunctionalityPendingError.d.ts +7 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +10856 -8585
- package/dist/index.js.map +1 -1
- package/dist/instance/Group.d.ts +9 -4
- package/dist/instance/InputControl.d.ts +36 -0
- package/dist/instance/ModelValue.d.ts +16 -26
- package/dist/instance/Note.d.ts +10 -3
- package/dist/instance/PrimaryInstance.d.ts +84 -0
- package/dist/instance/Root.d.ts +29 -25
- package/dist/instance/SelectField.d.ts +10 -2
- package/dist/instance/Subtree.d.ts +10 -5
- package/dist/instance/TriggerControl.d.ts +10 -3
- package/dist/instance/abstract/DescendantNode.d.ts +31 -23
- package/dist/instance/abstract/InstanceNode.d.ts +49 -29
- package/dist/instance/abstract/UnsupportedControl.d.ts +10 -3
- package/dist/instance/abstract/ValueNode.d.ts +48 -0
- package/dist/instance/hierarchy.d.ts +9 -8
- package/dist/instance/identity.d.ts +2 -7
- package/dist/instance/internal-api/EvaluationContext.d.ts +53 -12
- package/dist/instance/internal-api/InstanceConfig.d.ts +8 -2
- package/dist/instance/internal-api/InstanceValueContext.d.ts +20 -0
- package/dist/instance/internal-api/PrimaryInstanceDocument.d.ts +36 -0
- package/dist/instance/internal-api/TranslationContext.d.ts +3 -2
- package/dist/instance/internal-api/ValidationContext.d.ts +3 -4
- package/dist/instance/internal-api/ValueContext.d.ts +2 -1
- package/dist/instance/internal-api/submission/ClientReactiveSubmittableInstance.d.ts +14 -0
- package/dist/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.d.ts +31 -0
- package/dist/instance/internal-api/submission/ClientReactiveSubmittableParentNode.d.ts +18 -0
- package/dist/instance/internal-api/submission/ClientReactiveSubmittableValueNode.d.ts +17 -0
- package/dist/instance/repeat/BaseRepeatRange.d.ts +39 -83
- package/dist/instance/repeat/RepeatInstance.d.ts +11 -29
- package/dist/instance/repeat/RepeatRangeControlled.d.ts +2 -2
- package/dist/instance/repeat/RepeatRangeUncontrolled.d.ts +2 -2
- package/dist/instance/resource.d.ts +1 -1
- package/dist/instance/text/TextRange.d.ts +1 -1
- package/dist/instance/unsupported/RangeControl.d.ts +3 -1
- package/dist/instance/unsupported/RankControl.d.ts +3 -1
- package/dist/instance/unsupported/UploadControl.d.ts +3 -1
- package/dist/integration/xpath/EngineXPathEvaluator.d.ts +14 -0
- package/dist/integration/xpath/adapter/XFormsXPathNode.d.ts +71 -0
- package/dist/integration/xpath/adapter/engineDOMAdapter.d.ts +5 -0
- package/dist/integration/xpath/adapter/kind.d.ts +32 -0
- package/dist/integration/xpath/adapter/names.d.ts +16 -0
- package/dist/integration/xpath/adapter/traversal.d.ts +25 -0
- package/dist/integration/xpath/adapter/values.d.ts +2 -0
- package/dist/integration/xpath/static-dom/StaticAttribute.d.ts +19 -0
- package/dist/integration/xpath/static-dom/StaticDocument.d.ts +16 -0
- package/dist/integration/xpath/static-dom/StaticElement.d.ts +40 -0
- package/dist/integration/xpath/static-dom/StaticNamedNode.d.ts +17 -0
- package/dist/integration/xpath/static-dom/StaticNode.d.ts +33 -0
- package/dist/integration/xpath/static-dom/StaticText.d.ts +16 -0
- package/dist/lib/client-reactivity/submission/createInstanceSubmissionState.d.ts +3 -0
- package/dist/lib/client-reactivity/submission/createLeafNodeSubmissionState.d.ts +3 -0
- package/dist/lib/client-reactivity/submission/createNodeRangeSubmissionState.d.ts +4 -0
- package/dist/lib/client-reactivity/submission/createParentNodeSubmissionState.d.ts +4 -0
- package/dist/lib/client-reactivity/submission/createValueNodeSubmissionState.d.ts +3 -0
- package/dist/lib/client-reactivity/submission/prepareSubmission.d.ts +8 -0
- package/dist/lib/codecs/DecimalValueCodec.d.ts +6 -0
- package/dist/lib/codecs/IntValueCodec.d.ts +6 -0
- package/dist/lib/codecs/StringValueCodec.d.ts +4 -0
- package/dist/lib/codecs/ValueCodec.d.ts +30 -0
- package/dist/lib/codecs/ValueTypePlaceholderCodec.d.ts +12 -0
- package/dist/lib/codecs/getSharedValueCodec.d.ts +47 -0
- package/dist/lib/dom/query.d.ts +3 -0
- package/dist/lib/reactivity/createChildrenState.d.ts +7 -5
- package/dist/lib/reactivity/createComputedExpression.d.ts +18 -5
- package/dist/lib/reactivity/createInstanceValueState.d.ts +40 -0
- package/dist/lib/reactivity/createNoteReadonlyThunk.d.ts +2 -2
- package/dist/lib/reactivity/createTranslationState.d.ts +19 -0
- package/dist/lib/reactivity/createValueState.d.ts +6 -10
- package/dist/lib/reactivity/materializeCurrentStateChildren.d.ts +4 -4
- package/dist/lib/xml-serialization.d.ts +41 -0
- package/dist/parse/XFormDOM.d.ts +18 -7
- package/dist/parse/XFormDefinition.d.ts +1 -2
- package/dist/parse/attachments/FormAttachmentResource.d.ts +27 -0
- package/dist/parse/body/BodyDefinition.d.ts +3 -3
- package/dist/parse/body/RepeatElementDefinition.d.ts +1 -1
- package/dist/parse/body/appearance/inputAppearanceParser.d.ts +1 -1
- package/dist/parse/body/control/{InputDefinition.d.ts → InputControlDefinition.d.ts} +1 -1
- package/dist/parse/body/control/select/ItemsetNodesetContext.d.ts +0 -2
- package/dist/parse/expression/abstract/DependencyContext.d.ts +0 -5
- package/dist/parse/expression/abstract/DependentExpression.d.ts +2 -3
- package/dist/parse/model/BindDefinition.d.ts +4 -5
- package/dist/parse/model/BindTypeDefinition.d.ts +20 -0
- package/dist/parse/model/DescendentNodeDefinition.d.ts +0 -1
- package/dist/parse/model/FormSubmissionDefinition.d.ts +8 -0
- package/dist/parse/model/ItextTranslation/ItextTranslationDefinition.d.ts +4 -0
- package/dist/parse/model/ItextTranslation/ItextTranslationRootDefinition.d.ts +8 -0
- package/dist/parse/model/ItextTranslation/ItextTranslationsDefinition.d.ts +8 -0
- package/dist/parse/model/LeafNodeDefinition.d.ts +3 -1
- package/dist/parse/model/ModelBindMap.d.ts +1 -1
- package/dist/parse/model/ModelDefinition.d.ts +2 -0
- package/dist/parse/model/NodeDefinition.d.ts +0 -1
- package/dist/parse/model/NoteNodeDefinition.d.ts +4 -4
- package/dist/parse/model/RootDefinition.d.ts +3 -2
- package/dist/parse/model/SecondaryInstance/SecondaryInstanceDefinition.d.ts +4 -0
- package/dist/parse/model/SecondaryInstance/SecondaryInstanceRootDefinition.d.ts +7 -0
- package/dist/parse/model/SecondaryInstance/SecondaryInstancesDefinition.d.ts +13 -0
- package/dist/parse/model/SecondaryInstance/sources/BlankSecondaryInstanceSource.d.ts +14 -0
- package/dist/parse/model/SecondaryInstance/sources/CSVExternalSecondaryInstance.d.ts +22 -0
- package/dist/parse/model/SecondaryInstance/sources/ExternalSecondaryInstanceResource.d.ts +23 -0
- package/dist/parse/model/SecondaryInstance/sources/ExternalSecondaryInstanceSource.d.ts +9 -0
- package/dist/parse/model/SecondaryInstance/sources/GeoJSONExternalSecondaryInstance.d.ts +5 -0
- package/dist/parse/model/SecondaryInstance/sources/InternalSecondaryInstanceSource.d.ts +7 -0
- package/dist/parse/model/SecondaryInstance/sources/SecondaryInstanceSource.d.ts +13 -0
- package/dist/parse/model/SecondaryInstance/sources/XMLExternalSecondaryInstanceSource.d.ts +12 -0
- package/dist/parse/shared/parseStaticDocumentFromDOMSubtree.d.ts +24 -0
- package/dist/parse/text/abstract/TextRangeDefinition.d.ts +0 -2
- package/dist/parse/xpath/semantic-analysis.d.ts +5 -0
- package/dist/solid.js +8174 -5921
- package/dist/solid.js.map +1 -1
- package/package.json +13 -11
- package/src/client/BaseNode.ts +16 -6
- package/src/client/BaseValueNode.ts +35 -0
- package/src/client/EngineConfig.ts +52 -51
- package/src/client/InputNode.ts +77 -0
- package/src/client/ModelValueNode.ts +30 -7
- package/src/client/NoteNode.ts +2 -2
- package/src/client/RootNode.ts +37 -7
- package/src/client/SelectNode.ts +3 -3
- package/src/client/ValueType.ts +4 -0
- package/src/client/constants.ts +67 -0
- package/src/client/hierarchy.ts +5 -5
- package/src/client/identity.ts +16 -0
- package/src/client/node-types.ts +12 -6
- package/src/client/resources.ts +118 -0
- package/src/client/submission/SubmissionData.ts +12 -0
- package/src/client/submission/SubmissionDefinition.ts +16 -0
- package/src/client/submission/SubmissionInstanceFile.ts +9 -0
- package/src/client/submission/SubmissionOptions.ts +28 -0
- package/src/client/submission/SubmissionResult.ts +124 -0
- package/src/client/submission/SubmissionState.ts +14 -0
- package/src/client/validation.ts +2 -2
- package/src/error/ErrorProductionDesignPendingError.ts +6 -0
- package/src/error/ValueTypeInvariantError.ts +22 -0
- package/src/error/XPathFunctionalityError.ts +26 -0
- package/src/error/XPathFunctionalityNotSupportedError.ts +18 -0
- package/src/error/XPathFunctionalityPendingError.ts +18 -0
- package/src/index.ts +10 -1
- package/src/instance/Group.ts +17 -5
- package/src/instance/InputControl.ts +119 -0
- package/src/instance/ModelValue.ts +47 -54
- package/src/instance/Note.ts +24 -8
- package/src/instance/PrimaryInstance.ts +244 -0
- package/src/instance/Root.ts +62 -134
- package/src/instance/SelectField.ts +56 -19
- package/src/instance/Subtree.ts +19 -7
- package/src/instance/TriggerControl.ts +23 -6
- package/src/instance/abstract/DescendantNode.ts +90 -45
- package/src/instance/abstract/InstanceNode.ts +103 -105
- package/src/instance/abstract/UnsupportedControl.ts +28 -5
- package/src/instance/abstract/ValueNode.ts +127 -0
- package/src/instance/children.ts +9 -10
- package/src/instance/hierarchy.ts +19 -15
- package/src/instance/identity.ts +3 -9
- package/src/instance/index.ts +25 -7
- package/src/instance/internal-api/EvaluationContext.ts +60 -13
- package/src/instance/internal-api/InstanceConfig.ts +9 -2
- package/src/instance/internal-api/InstanceValueContext.ts +24 -0
- package/src/instance/internal-api/PrimaryInstanceDocument.ts +53 -0
- package/src/instance/internal-api/TranslationContext.ts +3 -2
- package/src/instance/internal-api/ValidationContext.ts +3 -4
- package/src/instance/internal-api/ValueContext.ts +2 -1
- package/src/instance/internal-api/submission/ClientReactiveSubmittableInstance.ts +20 -0
- package/src/instance/internal-api/submission/ClientReactiveSubmittableLeafNode.ts +42 -0
- package/src/instance/internal-api/submission/ClientReactiveSubmittableParentNode.ts +25 -0
- package/src/instance/internal-api/submission/ClientReactiveSubmittableValueNode.ts +23 -0
- package/src/instance/repeat/BaseRepeatRange.ts +50 -131
- package/src/instance/repeat/RepeatInstance.ts +20 -46
- package/src/instance/repeat/RepeatRangeControlled.ts +5 -5
- package/src/instance/repeat/RepeatRangeUncontrolled.ts +2 -4
- package/src/instance/resource.ts +1 -1
- package/src/instance/text/TextChunk.ts +1 -1
- package/src/instance/unsupported/RangeControl.ts +5 -1
- package/src/instance/unsupported/RankControl.ts +5 -1
- package/src/instance/unsupported/UploadControl.ts +5 -1
- package/src/integration/xpath/EngineXPathEvaluator.ts +22 -0
- package/src/integration/xpath/adapter/XFormsXPathNode.ts +126 -0
- package/src/integration/xpath/adapter/engineDOMAdapter.ts +57 -0
- package/src/integration/xpath/adapter/kind.ts +114 -0
- package/src/integration/xpath/adapter/names.ts +99 -0
- package/src/integration/xpath/adapter/traversal.ts +228 -0
- package/src/integration/xpath/adapter/values.ts +5 -0
- package/src/integration/xpath/static-dom/StaticAttribute.ts +33 -0
- package/src/integration/xpath/static-dom/StaticDocument.ts +38 -0
- package/src/integration/xpath/static-dom/StaticElement.ts +106 -0
- package/src/integration/xpath/static-dom/StaticNamedNode.ts +45 -0
- package/src/integration/xpath/static-dom/StaticNode.ts +68 -0
- package/src/integration/xpath/static-dom/StaticText.ts +28 -0
- package/src/lib/client-reactivity/README.md +0 -0
- package/src/lib/client-reactivity/submission/createInstanceSubmissionState.ts +12 -0
- package/src/lib/client-reactivity/submission/createLeafNodeSubmissionState.ts +20 -0
- package/src/lib/client-reactivity/submission/createNodeRangeSubmissionState.ts +17 -0
- package/src/lib/client-reactivity/submission/createParentNodeSubmissionState.ts +22 -0
- package/src/lib/client-reactivity/submission/createValueNodeSubmissionState.ts +21 -0
- package/src/lib/client-reactivity/submission/prepareSubmission.ts +172 -0
- package/src/lib/codecs/DecimalValueCodec.ts +46 -0
- package/src/lib/codecs/IntValueCodec.ts +100 -0
- package/src/lib/codecs/StringValueCodec.ts +11 -0
- package/src/lib/codecs/ValueCodec.ts +106 -0
- package/src/lib/codecs/ValueTypePlaceholderCodec.ts +19 -0
- package/src/lib/codecs/getSharedValueCodec.ts +77 -0
- package/src/lib/dom/query.ts +7 -0
- package/src/lib/reactivity/createChildrenState.ts +8 -6
- package/src/lib/reactivity/createComputedExpression.ts +57 -34
- package/src/lib/reactivity/createInstanceValueState.ts +166 -0
- package/src/lib/reactivity/createNoteReadonlyThunk.ts +12 -7
- package/src/lib/reactivity/createSelectItems.ts +21 -21
- package/src/lib/reactivity/createTranslationState.ts +61 -0
- package/src/lib/reactivity/createValueState.ts +62 -120
- package/src/lib/reactivity/materializeCurrentStateChildren.ts +7 -7
- package/src/lib/reactivity/text/createTextRange.ts +4 -3
- package/src/lib/reactivity/validation/createAggregatedViolations.ts +1 -1
- package/src/lib/reactivity/validation/createValidation.ts +2 -3
- package/src/lib/xml-serialization.ts +96 -0
- package/src/parse/XFormDOM.ts +110 -75
- package/src/parse/XFormDefinition.ts +1 -6
- package/src/parse/attachments/FormAttachmentResource.ts +40 -0
- package/src/parse/body/BodyDefinition.ts +3 -3
- package/src/parse/body/appearance/inputAppearanceParser.ts +1 -1
- package/src/parse/body/control/{InputDefinition.ts → InputControlDefinition.ts} +3 -5
- package/src/parse/body/control/select/ItemsetNodesetContext.ts +0 -5
- package/src/parse/expression/abstract/DependencyContext.ts +0 -26
- package/src/parse/expression/abstract/DependentExpression.ts +2 -13
- package/src/parse/model/BindDefinition.ts +5 -8
- package/src/parse/model/BindTypeDefinition.ts +175 -0
- package/src/parse/model/DescendentNodeDefinition.ts +0 -6
- package/src/parse/model/FormSubmissionDefinition.ts +44 -0
- package/src/parse/model/ItextTranslation/ItextTranslationDefinition.ts +4 -0
- package/src/parse/model/ItextTranslation/ItextTranslationRootDefinition.ts +41 -0
- package/src/parse/model/ItextTranslation/ItextTranslationsDefinition.ts +31 -0
- package/src/parse/model/LeafNodeDefinition.ts +4 -1
- package/src/parse/model/ModelBindMap.ts +6 -8
- package/src/parse/model/ModelDefinition.ts +6 -1
- package/src/parse/model/NodeDefinition.ts +0 -3
- package/src/parse/model/NoteNodeDefinition.ts +3 -3
- package/src/parse/model/RootDefinition.ts +2 -1
- package/src/parse/model/SecondaryInstance/SecondaryInstanceDefinition.ts +4 -0
- package/src/parse/model/SecondaryInstance/SecondaryInstanceRootDefinition.ts +12 -0
- package/src/parse/model/SecondaryInstance/SecondaryInstancesDefinition.ts +102 -0
- package/src/parse/model/SecondaryInstance/sources/BlankSecondaryInstanceSource.ts +40 -0
- package/src/parse/model/SecondaryInstance/sources/CSVExternalSecondaryInstance.ts +288 -0
- package/src/parse/model/SecondaryInstance/sources/ExternalSecondaryInstanceResource.ts +222 -0
- package/src/parse/model/SecondaryInstance/sources/ExternalSecondaryInstanceSource.ts +22 -0
- package/src/parse/model/SecondaryInstance/sources/GeoJSONExternalSecondaryInstance.ts +414 -0
- package/src/parse/model/SecondaryInstance/sources/InternalSecondaryInstanceSource.ts +19 -0
- package/src/parse/model/SecondaryInstance/sources/SecondaryInstanceSource.ts +29 -0
- package/src/parse/model/SecondaryInstance/sources/XMLExternalSecondaryInstanceSource.ts +32 -0
- package/src/parse/shared/parseStaticDocumentFromDOMSubtree.ts +149 -0
- package/src/parse/text/abstract/TextRangeDefinition.ts +0 -6
- package/src/parse/xpath/semantic-analysis.ts +29 -0
- package/dist/client/StringNode.d.ts +0 -46
- package/dist/instance/StringField.d.ts +0 -44
- package/dist/instance/internal-api/SubscribableDependency.d.ts +0 -59
- package/dist/parse/XFormDataType.d.ts +0 -25
- package/src/client/StringNode.ts +0 -52
- package/src/instance/StringField.ts +0 -120
- package/src/instance/internal-api/SubscribableDependency.ts +0 -61
- package/src/parse/XFormDataType.ts +0 -62
package/src/parse/XFormDOM.ts
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
import { XFORMS_NAMESPACE_URI, XMLNS_NAMESPACE_URI } from '@getodk/common/constants/xmlns.ts';
|
|
2
|
-
import {
|
|
2
|
+
import type {
|
|
3
|
+
KnownAttributeLocalNamedElement,
|
|
4
|
+
LocalNamedElement,
|
|
5
|
+
} from '@getodk/common/types/dom.ts';
|
|
6
|
+
import { DefaultEvaluator } from '@getodk/xpath';
|
|
7
|
+
|
|
8
|
+
interface DOMBindElement extends KnownAttributeLocalNamedElement<'bind', 'nodeset'> {}
|
|
9
|
+
|
|
10
|
+
export interface DOMItextTranslationElement
|
|
11
|
+
extends KnownAttributeLocalNamedElement<'translation', 'lang'> {}
|
|
12
|
+
|
|
13
|
+
interface DOMInstanceElement extends LocalNamedElement<'instance'> {}
|
|
14
|
+
|
|
15
|
+
export interface DOMSecondaryInstanceElement
|
|
16
|
+
extends KnownAttributeLocalNamedElement<'instance', 'id'> {}
|
|
17
|
+
|
|
18
|
+
type AssertDOMSecondaryInstanceElement = (
|
|
19
|
+
element: DOMInstanceElement
|
|
20
|
+
) => asserts element is DOMSecondaryInstanceElement;
|
|
21
|
+
|
|
22
|
+
const assertDOMSecondaryInstanceElement: AssertDOMSecondaryInstanceElement = (element) => {
|
|
23
|
+
if (!element.hasAttribute('id')) {
|
|
24
|
+
throw new Error('Invalid secondary instance element: missing `id` attribute');
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type AssertDOMSecondaryInstanceElements = (
|
|
29
|
+
elements: readonly DOMInstanceElement[]
|
|
30
|
+
) => asserts elements is readonly DOMSecondaryInstanceElement[];
|
|
31
|
+
|
|
32
|
+
const assertDOMSecondaryInstanceElements: AssertDOMSecondaryInstanceElements = (elements) => {
|
|
33
|
+
elements.forEach(assertDOMSecondaryInstanceElement);
|
|
34
|
+
};
|
|
3
35
|
|
|
4
36
|
const domParser = new DOMParser();
|
|
5
37
|
|
|
@@ -145,61 +177,33 @@ const normalizeRepeatGroups = (body: Element): void => {
|
|
|
145
177
|
}
|
|
146
178
|
};
|
|
147
179
|
|
|
148
|
-
interface NormalizedXForm {
|
|
149
|
-
readonly xformDocument: XMLDocument;
|
|
150
|
-
readonly rootEvaluator: XFormsXPathEvaluator;
|
|
151
|
-
readonly html: Element;
|
|
152
|
-
readonly body: Element;
|
|
153
|
-
readonly normalizedXML: string;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
interface XFormDOMNormalizationOptions {
|
|
157
|
-
readonly isNormalized: boolean;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
180
|
/**
|
|
161
|
-
* Performs preprocess operations to normalize certain aspects of an
|
|
162
|
-
*
|
|
181
|
+
* Performs preprocess operations to normalize certain aspects of the body of an
|
|
182
|
+
* XForm, for consistency when parsing its runtime representations.
|
|
183
|
+
*
|
|
163
184
|
* Currently this preprocessing:
|
|
164
185
|
*
|
|
165
|
-
* - Ensures consistent use of `ref` and `nodeset` where ambiguous in the
|
|
166
|
-
*
|
|
167
|
-
* - Ensures `<repeat>` body elements are always enclosed by a `<group>`
|
|
168
|
-
*
|
|
186
|
+
* - Ensures consistent use of `ref` and `nodeset` where ambiguous in the ODK
|
|
187
|
+
* XForms spec
|
|
188
|
+
* - Ensures `<repeat>` body elements are always enclosed by a `<group>` with
|
|
189
|
+
* the same `ref`
|
|
169
190
|
*/
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
): NormalizedXForm => {
|
|
174
|
-
const xformDocument: XMLDocument = domParser.parseFromString(sourceXML, 'text/xml');
|
|
175
|
-
const rootEvaluator = new XFormsXPathEvaluator({
|
|
176
|
-
rootNode: xformDocument,
|
|
177
|
-
});
|
|
178
|
-
const html = rootEvaluator.evaluateNonNullElement('/h:html');
|
|
179
|
-
const body = rootEvaluator.evaluateNonNullElement('./h:body', {
|
|
180
|
-
contextNode: html,
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
let normalizedXML: string;
|
|
191
|
+
const normalizeXFormBody = <T extends Element>(body: T): T => {
|
|
192
|
+
normalizeBodyRefNodesetAttributes(body);
|
|
193
|
+
normalizeRepeatGroups(body);
|
|
184
194
|
|
|
185
|
-
|
|
186
|
-
normalizedXML = sourceXML;
|
|
187
|
-
} else {
|
|
188
|
-
normalizeBodyRefNodesetAttributes(body);
|
|
189
|
-
normalizeRepeatGroups(body);
|
|
190
|
-
|
|
191
|
-
normalizedXML = html.outerHTML;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
xformDocument,
|
|
196
|
-
rootEvaluator,
|
|
197
|
-
html,
|
|
198
|
-
body,
|
|
199
|
-
normalizedXML,
|
|
200
|
-
};
|
|
195
|
+
return body;
|
|
201
196
|
};
|
|
202
197
|
|
|
198
|
+
interface XFormDOMOptions {
|
|
199
|
+
readonly isNormalized: boolean;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @todo **Everything** in this class should be cacheable. Maybe not worth it
|
|
204
|
+
* for small forms, but may make a pretty substantial difference for very large
|
|
205
|
+
* forms (in bytes) in sessions creating multiple instances of the same form.
|
|
206
|
+
*/
|
|
203
207
|
export class XFormDOM {
|
|
204
208
|
static from(sourceXML: string) {
|
|
205
209
|
return new this(sourceXML, { isNormalized: false });
|
|
@@ -207,10 +211,6 @@ export class XFormDOM {
|
|
|
207
211
|
|
|
208
212
|
protected readonly normalizedXML: string;
|
|
209
213
|
|
|
210
|
-
// XPath
|
|
211
|
-
readonly rootEvaluator: XFormsXPathEvaluator;
|
|
212
|
-
readonly primaryInstanceEvaluator: XFormsXPathEvaluator;
|
|
213
|
-
|
|
214
214
|
// Commonly accessed landmark nodes
|
|
215
215
|
readonly xformDocument: XMLDocument;
|
|
216
216
|
|
|
@@ -220,59 +220,94 @@ export class XFormDOM {
|
|
|
220
220
|
readonly title: Element;
|
|
221
221
|
|
|
222
222
|
readonly model: Element;
|
|
223
|
+
readonly binds: readonly DOMBindElement[];
|
|
223
224
|
readonly primaryInstance: Element;
|
|
224
225
|
readonly primaryInstanceRoot: Element;
|
|
225
226
|
|
|
227
|
+
readonly itextTranslationElements: readonly DOMItextTranslationElement[];
|
|
228
|
+
readonly secondaryInstanceElements: readonly DOMSecondaryInstanceElement[];
|
|
229
|
+
|
|
226
230
|
readonly body: Element;
|
|
227
231
|
|
|
228
232
|
protected constructor(
|
|
229
233
|
protected readonly sourceXML: string,
|
|
230
|
-
options:
|
|
234
|
+
options: XFormDOMOptions
|
|
231
235
|
) {
|
|
232
|
-
const
|
|
233
|
-
const
|
|
234
|
-
const
|
|
236
|
+
const evaluator = new DefaultEvaluator();
|
|
237
|
+
const xformDocument: XMLDocument = domParser.parseFromString(sourceXML, 'text/xml');
|
|
238
|
+
const html = evaluator.evaluateNonNullElement('/h:html', {
|
|
239
|
+
contextNode: xformDocument,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
let body = evaluator.evaluateNonNullElement('./h:body', {
|
|
235
243
|
contextNode: html,
|
|
236
244
|
});
|
|
237
|
-
|
|
245
|
+
let normalizedXML: string;
|
|
246
|
+
|
|
247
|
+
if (options.isNormalized) {
|
|
248
|
+
normalizedXML = sourceXML;
|
|
249
|
+
} else {
|
|
250
|
+
body = normalizeXFormBody(body);
|
|
251
|
+
|
|
252
|
+
// TODO: if we keep doing normalization this way long term (or using the DOM
|
|
253
|
+
// for parsing long term, for that matter!), we should measure this. And we
|
|
254
|
+
// should measure against XMLSerializer while we're at it!
|
|
255
|
+
normalizedXML = xformDocument.documentElement.outerHTML;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const head = evaluator.evaluateNonNullElement('./h:head', {
|
|
259
|
+
contextNode: html,
|
|
260
|
+
});
|
|
261
|
+
const title = evaluator.evaluateNonNullElement('./h:title', {
|
|
238
262
|
contextNode: head,
|
|
239
263
|
});
|
|
240
|
-
const model =
|
|
264
|
+
const model = evaluator.evaluateNonNullElement('./xf:model', {
|
|
241
265
|
contextNode: head,
|
|
242
266
|
});
|
|
243
|
-
|
|
244
|
-
const primaryInstanceRoot = rootEvaluator.evaluateNonNullElement('./xf:instance/*[@id]', {
|
|
267
|
+
const binds = evaluator.evaluateNodes<DOMBindElement>('./xf:bind[@nodeset]', {
|
|
245
268
|
contextNode: model,
|
|
246
269
|
});
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
contextNode:
|
|
270
|
+
|
|
271
|
+
const instances = evaluator.evaluateNodes<DOMInstanceElement>('./xf:instance', {
|
|
272
|
+
contextNode: model,
|
|
250
273
|
});
|
|
251
274
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
275
|
+
const [primaryInstance, ...secondaryInstanceElements] = instances;
|
|
276
|
+
|
|
277
|
+
assertDOMSecondaryInstanceElements(secondaryInstanceElements);
|
|
278
|
+
|
|
279
|
+
if (primaryInstance == null) {
|
|
280
|
+
throw new Error('Form is missing primary instance');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// TODO: Evidently primary instance root will not always have an id
|
|
284
|
+
const primaryInstanceRoot = evaluator.evaluateNonNullElement('./*[@id]', {
|
|
285
|
+
contextNode: primaryInstance,
|
|
256
286
|
});
|
|
287
|
+
|
|
288
|
+
const itextTranslationElements = evaluator.evaluateNodes<DOMItextTranslationElement>(
|
|
289
|
+
'./xf:itext/xf:translation[@lang]',
|
|
290
|
+
{
|
|
291
|
+
contextNode: model,
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
this.normalizedXML = normalizedXML;
|
|
257
296
|
this.xformDocument = xformDocument;
|
|
258
297
|
this.html = html;
|
|
259
298
|
this.head = head;
|
|
260
299
|
this.title = title;
|
|
261
300
|
this.model = model;
|
|
301
|
+
this.binds = binds;
|
|
262
302
|
this.primaryInstance = primaryInstance;
|
|
263
303
|
this.primaryInstanceRoot = primaryInstanceRoot;
|
|
304
|
+
this.itextTranslationElements = itextTranslationElements;
|
|
305
|
+
this.secondaryInstanceElements = secondaryInstanceElements;
|
|
264
306
|
this.body = body;
|
|
265
307
|
}
|
|
266
308
|
|
|
267
|
-
// TODO: anticipating this will be an entry point for edits as well
|
|
268
|
-
createInstance(): XFormDOM {
|
|
269
|
-
return new XFormDOM(this.normalizedXML, { isNormalized: true });
|
|
270
|
-
}
|
|
271
|
-
|
|
272
309
|
toJSON() {
|
|
273
310
|
const {
|
|
274
|
-
rootEvaluator,
|
|
275
|
-
primaryInstanceEvaluator,
|
|
276
311
|
html,
|
|
277
312
|
head,
|
|
278
313
|
title,
|
|
@@ -3,7 +3,6 @@ import { ModelDefinition } from './model/ModelDefinition.ts';
|
|
|
3
3
|
import { XFormDOM } from './XFormDOM.ts';
|
|
4
4
|
|
|
5
5
|
export class XFormDefinition {
|
|
6
|
-
readonly xformDOM: XFormDOM;
|
|
7
6
|
readonly xformDocument: XMLDocument;
|
|
8
7
|
|
|
9
8
|
readonly id: string;
|
|
@@ -14,11 +13,7 @@ export class XFormDefinition {
|
|
|
14
13
|
readonly body: BodyDefinition;
|
|
15
14
|
readonly model: ModelDefinition;
|
|
16
15
|
|
|
17
|
-
constructor(readonly
|
|
18
|
-
const xformDOM = XFormDOM.from(sourceXML);
|
|
19
|
-
|
|
20
|
-
this.xformDOM = xformDOM;
|
|
21
|
-
|
|
16
|
+
constructor(readonly xformDOM: XFormDOM) {
|
|
22
17
|
const { primaryInstanceRoot, title, xformDocument } = xformDOM;
|
|
23
18
|
const id = primaryInstanceRoot.getAttribute('id');
|
|
24
19
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { JRResourceURL } from '@getodk/common/jr-resources/JRResourceURL.ts';
|
|
2
|
+
|
|
3
|
+
export type FormAttachmentDataType = 'media' | 'secondary-instance';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @todo This type anticipates work to support media form attachments, which
|
|
7
|
+
* will tend to be associated with binary data. The
|
|
8
|
+
* expectation is that:
|
|
9
|
+
*
|
|
10
|
+
* - {@link Blob} would be appropriate for representing data from attachment
|
|
11
|
+
* resources which are conventionally loaded to completion (where network
|
|
12
|
+
* conditions are favorable), such as images
|
|
13
|
+
*
|
|
14
|
+
* - {@link MediaSource} or {@link ReadableStream} may be more appropriate for
|
|
15
|
+
* representing data from resources which are conventionally streamed in a
|
|
16
|
+
* browser context (often regardless of network conditions), such as video and
|
|
17
|
+
* audio
|
|
18
|
+
*/
|
|
19
|
+
// prettier-ignore
|
|
20
|
+
export type FormAttachmentMediaData =
|
|
21
|
+
| Blob
|
|
22
|
+
| MediaSource
|
|
23
|
+
| ReadableStream<unknown>;
|
|
24
|
+
|
|
25
|
+
export type FormAttachmentSecondaryInstanceData = string;
|
|
26
|
+
|
|
27
|
+
// prettier-ignore
|
|
28
|
+
type FormAttachmentData<DataType extends FormAttachmentDataType> =
|
|
29
|
+
DataType extends 'media'
|
|
30
|
+
? FormAttachmentMediaData
|
|
31
|
+
: FormAttachmentSecondaryInstanceData;
|
|
32
|
+
|
|
33
|
+
export abstract class FormAttachmentResource<DataType extends FormAttachmentDataType> {
|
|
34
|
+
protected constructor(
|
|
35
|
+
readonly dataType: DataType,
|
|
36
|
+
readonly resourceURL: JRResourceURL,
|
|
37
|
+
readonly contentType: string,
|
|
38
|
+
readonly data: FormAttachmentData<DataType>
|
|
39
|
+
) {}
|
|
40
|
+
}
|
|
@@ -3,7 +3,7 @@ import { TokenListParser } from '../../lib/TokenListParser.ts';
|
|
|
3
3
|
import type { XFormDefinition } from '../../parse/XFormDefinition.ts';
|
|
4
4
|
import { DependencyContext } from '../expression/abstract/DependencyContext.ts';
|
|
5
5
|
import { ControlDefinition } from './control/ControlDefinition.ts';
|
|
6
|
-
import {
|
|
6
|
+
import { InputControlDefinition } from './control/InputControlDefinition.ts';
|
|
7
7
|
import { RangeControlDefinition } from './control/RangeControlDefinition.ts';
|
|
8
8
|
import { RankControlDefinition } from './control/RankControlDefinition.ts';
|
|
9
9
|
import type { AnySelectDefinition } from './control/select/SelectDefinition.ts';
|
|
@@ -25,7 +25,7 @@ export interface BodyElementParentContext {
|
|
|
25
25
|
// prettier-ignore
|
|
26
26
|
export type ControlElementDefinition =
|
|
27
27
|
| AnySelectDefinition
|
|
28
|
-
|
|
|
28
|
+
| InputControlDefinition
|
|
29
29
|
| RangeControlDefinition
|
|
30
30
|
| RankControlDefinition
|
|
31
31
|
| TriggerControlDefinition
|
|
@@ -47,7 +47,7 @@ const BodyElementDefinitionConstructors = [
|
|
|
47
47
|
LogicalGroupDefinition,
|
|
48
48
|
PresentationGroupDefinition,
|
|
49
49
|
StructuralGroupDefinition,
|
|
50
|
-
|
|
50
|
+
InputControlDefinition,
|
|
51
51
|
SelectDefinition,
|
|
52
52
|
RangeControlDefinition,
|
|
53
53
|
RankControlDefinition,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import type { XFormDefinition } from '../../XFormDefinition.ts';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
type InputAppearanceDefinition,
|
|
5
|
-
} from '../appearance/inputAppearanceParser.ts';
|
|
2
|
+
import type { InputAppearanceDefinition } from '../appearance/inputAppearanceParser.ts';
|
|
3
|
+
import { inputAppearanceParser } from '../appearance/inputAppearanceParser.ts';
|
|
6
4
|
import type { BodyElementParentContext } from '../BodyDefinition.ts';
|
|
7
5
|
import { ControlDefinition } from './ControlDefinition.ts';
|
|
8
6
|
|
|
9
|
-
export class
|
|
7
|
+
export class InputControlDefinition extends ControlDefinition<'input'> {
|
|
10
8
|
static override isCompatible(localName: string): boolean {
|
|
11
9
|
return localName === 'input';
|
|
12
10
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DependencyContext } from '../../../expression/abstract/DependencyContext.ts';
|
|
2
|
-
import type { AnyDependentExpression } from '../../../expression/abstract/DependentExpression.ts';
|
|
3
2
|
import type { ItemsetDefinition } from './ItemsetDefinition.ts';
|
|
4
3
|
|
|
5
4
|
export class ItemsetNodesetContext extends DependencyContext {
|
|
@@ -19,8 +18,4 @@ export class ItemsetNodesetContext extends DependencyContext {
|
|
|
19
18
|
super.isTranslated = value;
|
|
20
19
|
this.itemset.isTranslated = value;
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
override registerDependentExpression(expression: AnyDependentExpression): void {
|
|
24
|
-
this.itemset.registerDependentExpression(expression);
|
|
25
|
-
}
|
|
26
21
|
}
|
|
@@ -1,28 +1,7 @@
|
|
|
1
|
-
import type { AnyDependentExpression } from './DependentExpression.ts';
|
|
2
|
-
|
|
3
1
|
export abstract class DependencyContext {
|
|
4
2
|
abstract get parentReference(): string | null;
|
|
5
3
|
abstract get reference(): string | null;
|
|
6
4
|
|
|
7
|
-
protected readonly dependentExpressions = new Set<AnyDependentExpression>();
|
|
8
|
-
|
|
9
|
-
protected dependencyExpressionsCache: ReadonlySet<string> | null = null;
|
|
10
|
-
|
|
11
|
-
get dependencyExpressions(): ReadonlySet<string> {
|
|
12
|
-
let { dependencyExpressionsCache } = this;
|
|
13
|
-
|
|
14
|
-
if (dependencyExpressionsCache == null) {
|
|
15
|
-
dependencyExpressionsCache = new Set(
|
|
16
|
-
Array.from(this.dependentExpressions).flatMap((expression) => {
|
|
17
|
-
return Array.from(expression.dependencyReferences);
|
|
18
|
-
})
|
|
19
|
-
);
|
|
20
|
-
this.dependencyExpressionsCache = dependencyExpressionsCache;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return dependencyExpressionsCache;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
5
|
get isTranslated(): boolean {
|
|
27
6
|
return this._isTranslated;
|
|
28
7
|
}
|
|
@@ -45,9 +24,4 @@ export abstract class DependencyContext {
|
|
|
45
24
|
|
|
46
25
|
// Update note on `set isTranslated` if this internal storage changes.
|
|
47
26
|
protected _isTranslated = false;
|
|
48
|
-
|
|
49
|
-
registerDependentExpression(expression: AnyDependentExpression): void {
|
|
50
|
-
this.dependencyExpressionsCache = null;
|
|
51
|
-
this.dependentExpressions.add(expression);
|
|
52
|
-
}
|
|
53
27
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { resolveDependencyNodesets } from '../../xpath/dependency-analysis.ts';
|
|
1
|
+
import type { EngineXPathEvaluator } from '../../../integration/xpath/EngineXPathEvaluator.ts';
|
|
3
2
|
import type {
|
|
4
3
|
ConstantExpression,
|
|
5
4
|
ConstantTruthyExpression,
|
|
@@ -26,7 +25,7 @@ export type DependentExpressionEvaluatorMethod<Type extends DependentExpressionR
|
|
|
26
25
|
EvaluatorMethodsByResultType[Type];
|
|
27
26
|
|
|
28
27
|
export type DependentExpressionResult<Type extends DependentExpressionResultType> = ReturnType<
|
|
29
|
-
|
|
28
|
+
EngineXPathEvaluator[DependentExpressionEvaluatorMethod<Type>]
|
|
30
29
|
>;
|
|
31
30
|
|
|
32
31
|
interface SemanticDependencyOptions {
|
|
@@ -55,7 +54,6 @@ export interface ConstantTruthyDependentExpression extends ConstantDependentExpr
|
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
export abstract class DependentExpression<Type extends DependentExpressionResultType> {
|
|
58
|
-
readonly dependencyReferences: ReadonlySet<string> = new Set();
|
|
59
57
|
readonly isTranslated: boolean = false;
|
|
60
58
|
readonly evaluatorMethod: DependentExpressionEvaluatorMethod<Type>;
|
|
61
59
|
readonly constantExpression: ConstantExpression | null;
|
|
@@ -81,26 +79,17 @@ export abstract class DependentExpression<Type extends DependentExpressionResult
|
|
|
81
79
|
this.evaluatorMethod = evaluatorMethodsByResultType[resultType];
|
|
82
80
|
|
|
83
81
|
const {
|
|
84
|
-
ignoreContextReference = false,
|
|
85
82
|
semanticDependencies = {
|
|
86
83
|
translations: false,
|
|
87
84
|
},
|
|
88
85
|
} = options;
|
|
89
86
|
|
|
90
|
-
this.dependencyReferences = new Set(
|
|
91
|
-
resolveDependencyNodesets(context.reference, expression, {
|
|
92
|
-
ignoreReferenceToContextPath: ignoreContextReference,
|
|
93
|
-
})
|
|
94
|
-
);
|
|
95
|
-
|
|
96
87
|
const isTranslated = semanticDependencies.translations && isTranslationExpression(expression);
|
|
97
88
|
|
|
98
89
|
if (isTranslated) {
|
|
99
90
|
this.isTranslated = true;
|
|
100
91
|
context.isTranslated = true;
|
|
101
92
|
}
|
|
102
|
-
|
|
103
|
-
context.registerDependentExpression(this);
|
|
104
93
|
}
|
|
105
94
|
|
|
106
95
|
isConstantExpression(): this is ConstantDependentExpression<Type> {
|
|
@@ -2,15 +2,14 @@ import { DependencyContext } from '../expression/abstract/DependencyContext.ts';
|
|
|
2
2
|
import type { DependentExpression } from '../expression/abstract/DependentExpression.ts';
|
|
3
3
|
import { BindComputationExpression } from '../expression/BindComputationExpression.ts';
|
|
4
4
|
import { MessageDefinition } from '../text/MessageDefinition.ts';
|
|
5
|
-
import type { XFormDataType } from '../XFormDataType.ts';
|
|
6
|
-
import { bindDataType } from '../XFormDataType.ts';
|
|
7
5
|
import type { XFormDefinition } from '../XFormDefinition.ts';
|
|
8
6
|
import type { BindElement } from './BindElement.ts';
|
|
7
|
+
import type { BindType } from './BindTypeDefinition.ts';
|
|
8
|
+
import { BindTypeDefinition } from './BindTypeDefinition.ts';
|
|
9
9
|
import type { ModelDefinition } from './ModelDefinition.ts';
|
|
10
10
|
|
|
11
|
-
export class BindDefinition extends DependencyContext {
|
|
12
|
-
readonly
|
|
13
|
-
readonly dataType: XFormDataType;
|
|
11
|
+
export class BindDefinition<T extends BindType = BindType> extends DependencyContext {
|
|
12
|
+
readonly type: BindTypeDefinition<T>;
|
|
14
13
|
readonly parentNodeset: string | null;
|
|
15
14
|
|
|
16
15
|
readonly calculate: BindComputationExpression<'calculate'> | null;
|
|
@@ -78,9 +77,7 @@ export class BindDefinition extends DependencyContext {
|
|
|
78
77
|
) {
|
|
79
78
|
super();
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this.dataType = bindDataType(bindType);
|
|
80
|
+
this.type = BindTypeDefinition.from(bindElement);
|
|
84
81
|
|
|
85
82
|
const parentNodeset = nodeset.replace(/\/[^/]+$/, '');
|
|
86
83
|
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { XSD_NAMESPACE_URI, XSD_PREFIX } from '@getodk/common/constants/xmlns.ts';
|
|
2
|
+
import { parseQualifiedNameExpression } from '../xpath/semantic-analysis.ts';
|
|
3
|
+
import type { BindElement } from './BindElement.ts';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* As specified by {@link https://getodk.github.io/xforms-spec/#bind-attributes}
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_BIND_TYPE = 'string';
|
|
9
|
+
|
|
10
|
+
export type DefaultBindType = typeof DEFAULT_BIND_TYPE;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* As specified by {@link https://getodk.github.io/xforms-spec/#data-types}
|
|
14
|
+
*/
|
|
15
|
+
const BIND_TYPES = [
|
|
16
|
+
DEFAULT_BIND_TYPE,
|
|
17
|
+
'int',
|
|
18
|
+
'boolean',
|
|
19
|
+
'decimal',
|
|
20
|
+
'date',
|
|
21
|
+
'time',
|
|
22
|
+
'dateTime',
|
|
23
|
+
'geopoint',
|
|
24
|
+
'geotrace',
|
|
25
|
+
'geoshape',
|
|
26
|
+
'binary',
|
|
27
|
+
'barcode',
|
|
28
|
+
'intent',
|
|
29
|
+
] as const;
|
|
30
|
+
|
|
31
|
+
type BindTypes = typeof BIND_TYPES;
|
|
32
|
+
|
|
33
|
+
export type BindType = BindTypes[number];
|
|
34
|
+
|
|
35
|
+
const isBindType = (value: string): value is BindType => {
|
|
36
|
+
return BIND_TYPES.includes(value as BindType);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const resolveSupportedBindType = (sourceType: string): BindType | null => {
|
|
40
|
+
return isBindType(sourceType) ? sourceType : null;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type BindTypeAliasMapping = Readonly<Record<string, BindType>>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @todo should we make these aliases explicit (rather than relying on {@link resolveUnsupportedBindType})?
|
|
47
|
+
*
|
|
48
|
+
* - select1
|
|
49
|
+
* - rank
|
|
50
|
+
* - odk:rank
|
|
51
|
+
*/
|
|
52
|
+
const BIND_TYPE_ALIASES: BindTypeAliasMapping = {
|
|
53
|
+
integer: 'int',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @todo Should this be marked deprecated?
|
|
58
|
+
* @todo Should we warn on alias resolution?
|
|
59
|
+
*/
|
|
60
|
+
const resolveAliasedBindType = (sourceType: string): BindType | null => {
|
|
61
|
+
return BIND_TYPE_ALIASES[sourceType] ?? null;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @todo Should we warn on this fallback?
|
|
66
|
+
*/
|
|
67
|
+
const resolveUnsupportedBindType = (_unsupportedType: string): BindType => {
|
|
68
|
+
return DEFAULT_BIND_TYPE;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
interface BindDataTypeNamespaceResolver {
|
|
72
|
+
lookupNamespaceURI(prefix: string | null): string | null;
|
|
73
|
+
lookupPrefix(namespaceURI: string | null): string | null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolves the XML Schema namespace prefix by:
|
|
78
|
+
*
|
|
79
|
+
* - Explicitly declared prefix, if any
|
|
80
|
+
* - Default to {@link XSD_PREFIX} _unless that prefix is bound to some other
|
|
81
|
+
* namespace URI_
|
|
82
|
+
*/
|
|
83
|
+
const resolveXMLSchemaNamespacePrefix = (
|
|
84
|
+
resolver: BindDataTypeNamespaceResolver
|
|
85
|
+
): string | null => {
|
|
86
|
+
const declaredPrefix = resolver.lookupPrefix(XSD_NAMESPACE_URI);
|
|
87
|
+
|
|
88
|
+
if (declaredPrefix != null) {
|
|
89
|
+
return declaredPrefix;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (resolver.lookupNamespaceURI(XSD_PREFIX) == null) {
|
|
93
|
+
return XSD_PREFIX;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return null;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
interface MaybeBindElementNamespaceResolver
|
|
100
|
+
extends BindElement,
|
|
101
|
+
Partial<BindDataTypeNamespaceResolver> {}
|
|
102
|
+
|
|
103
|
+
interface BindElementNamespaceResolver extends BindElement, BindDataTypeNamespaceResolver {}
|
|
104
|
+
|
|
105
|
+
const isNamespaceResolver = (
|
|
106
|
+
bindElement: MaybeBindElementNamespaceResolver
|
|
107
|
+
): bindElement is BindElementNamespaceResolver => {
|
|
108
|
+
return (
|
|
109
|
+
typeof bindElement.lookupNamespaceURI === 'function' &&
|
|
110
|
+
typeof bindElement.lookupPrefix === 'function'
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const resolveNamespacedBindType = (
|
|
115
|
+
bindElement: BindElement,
|
|
116
|
+
sourceType: string
|
|
117
|
+
): BindType | null => {
|
|
118
|
+
if (!isNamespaceResolver(bindElement)) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const qualifiedName = parseQualifiedNameExpression(sourceType);
|
|
123
|
+
|
|
124
|
+
if (qualifiedName == null) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const xsdPrefix = resolveXMLSchemaNamespacePrefix(bindElement);
|
|
129
|
+
|
|
130
|
+
if (xsdPrefix == null) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const { prefix, localPart } = qualifiedName;
|
|
135
|
+
|
|
136
|
+
if (prefix === xsdPrefix) {
|
|
137
|
+
return (
|
|
138
|
+
resolveSupportedBindType(localPart) ??
|
|
139
|
+
resolveAliasedBindType(localPart) ??
|
|
140
|
+
resolveUnsupportedBindType(localPart)
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return null;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const resolveBindType = (bindElement: BindElement, sourceType: string): BindType => {
|
|
148
|
+
return (
|
|
149
|
+
resolveSupportedBindType(sourceType) ??
|
|
150
|
+
resolveAliasedBindType(sourceType) ??
|
|
151
|
+
resolveNamespacedBindType(bindElement, sourceType) ??
|
|
152
|
+
resolveUnsupportedBindType(sourceType)
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export class BindTypeDefinition<T extends BindType = BindType> {
|
|
157
|
+
static from<T extends BindType>(bindElement: BindElement): BindTypeDefinition<T> {
|
|
158
|
+
const sourceType = bindElement.getAttribute('type');
|
|
159
|
+
|
|
160
|
+
if (sourceType == null) {
|
|
161
|
+
return new this(sourceType, DEFAULT_BIND_TYPE);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const resolved = resolveBindType(bindElement, sourceType);
|
|
165
|
+
|
|
166
|
+
return new this(sourceType, resolved);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private constructor(source: null, resolved: DefaultBindType);
|
|
170
|
+
private constructor(source: string, resolved: BindType);
|
|
171
|
+
private constructor(
|
|
172
|
+
readonly source: string | null,
|
|
173
|
+
readonly resolved: T
|
|
174
|
+
) {}
|
|
175
|
+
}
|