@getodk/xforms-engine 0.1.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 (208) hide show
  1. package/README.md +44 -0
  2. package/dist/.vite/manifest.json +7 -0
  3. package/dist/XFormDOM.d.ts +31 -0
  4. package/dist/XFormDataType.d.ts +26 -0
  5. package/dist/XFormDefinition.d.ts +14 -0
  6. package/dist/body/BodyDefinition.d.ts +52 -0
  7. package/dist/body/BodyElementDefinition.d.ts +32 -0
  8. package/dist/body/RepeatDefinition.d.ts +15 -0
  9. package/dist/body/UnsupportedBodyElementDefinition.d.ts +10 -0
  10. package/dist/body/control/ControlDefinition.d.ts +16 -0
  11. package/dist/body/control/InputDefinition.d.ts +5 -0
  12. package/dist/body/control/select/ItemDefinition.d.ts +13 -0
  13. package/dist/body/control/select/ItemsetDefinition.d.ts +16 -0
  14. package/dist/body/control/select/ItemsetNodesetContext.d.ts +11 -0
  15. package/dist/body/control/select/ItemsetNodesetExpression.d.ts +5 -0
  16. package/dist/body/control/select/ItemsetValueExpression.d.ts +6 -0
  17. package/dist/body/control/select/SelectDefinition.d.ts +23 -0
  18. package/dist/body/group/BaseGroupDefinition.d.ts +46 -0
  19. package/dist/body/group/LogicalGroupDefinition.d.ts +6 -0
  20. package/dist/body/group/PresentationGroupDefinition.d.ts +11 -0
  21. package/dist/body/group/RepeatGroupDefinition.d.ts +12 -0
  22. package/dist/body/group/StructuralGroupDefinition.d.ts +6 -0
  23. package/dist/body/text/HintDefinition.d.ts +11 -0
  24. package/dist/body/text/LabelDefinition.d.ts +20 -0
  25. package/dist/body/text/TextElementDefinition.d.ts +32 -0
  26. package/dist/body/text/TextElementOutputPart.d.ts +12 -0
  27. package/dist/body/text/TextElementPart.d.ts +12 -0
  28. package/dist/body/text/TextElementReferencePart.d.ts +6 -0
  29. package/dist/body/text/TextElementStaticPart.d.ts +6 -0
  30. package/dist/client/BaseNode.d.ts +138 -0
  31. package/dist/client/EngineConfig.d.ts +78 -0
  32. package/dist/client/FormLanguage.d.ts +63 -0
  33. package/dist/client/GroupNode.d.ts +24 -0
  34. package/dist/client/OpaqueReactiveObjectFactory.d.ts +70 -0
  35. package/dist/client/RepeatInstanceNode.d.ts +28 -0
  36. package/dist/client/RepeatRangeNode.d.ts +94 -0
  37. package/dist/client/RootNode.d.ts +31 -0
  38. package/dist/client/SelectNode.d.ts +60 -0
  39. package/dist/client/StringNode.d.ts +41 -0
  40. package/dist/client/SubtreeNode.d.ts +52 -0
  41. package/dist/client/TextRange.d.ts +55 -0
  42. package/dist/client/hierarchy.d.ts +48 -0
  43. package/dist/client/index.d.ts +11 -0
  44. package/dist/client/node-types.d.ts +1 -0
  45. package/dist/expression/DependencyContext.d.ts +12 -0
  46. package/dist/expression/DependentExpression.d.ts +43 -0
  47. package/dist/index.d.ts +16 -0
  48. package/dist/index.js +37622 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/instance/Group.d.ts +31 -0
  51. package/dist/instance/RepeatInstance.d.ts +60 -0
  52. package/dist/instance/RepeatRange.d.ts +81 -0
  53. package/dist/instance/Root.d.ts +70 -0
  54. package/dist/instance/SelectField.d.ts +45 -0
  55. package/dist/instance/StringField.d.ts +39 -0
  56. package/dist/instance/Subtree.d.ts +30 -0
  57. package/dist/instance/abstract/DescendantNode.d.ts +76 -0
  58. package/dist/instance/abstract/InstanceNode.d.ts +107 -0
  59. package/dist/instance/children.d.ts +2 -0
  60. package/dist/instance/hierarchy.d.ts +12 -0
  61. package/dist/instance/identity.d.ts +7 -0
  62. package/dist/instance/index.d.ts +8 -0
  63. package/dist/instance/internal-api/EvaluationContext.d.ts +34 -0
  64. package/dist/instance/internal-api/InstanceConfig.d.ts +8 -0
  65. package/dist/instance/internal-api/SubscribableDependency.d.ts +59 -0
  66. package/dist/instance/internal-api/TranslationContext.d.ts +4 -0
  67. package/dist/instance/internal-api/ValueContext.d.ts +22 -0
  68. package/dist/instance/resource.d.ts +10 -0
  69. package/dist/instance/text/FormattedTextStub.d.ts +1 -0
  70. package/dist/instance/text/TextChunk.d.ts +11 -0
  71. package/dist/instance/text/TextRange.d.ts +10 -0
  72. package/dist/lib/dom/query.d.ts +20 -0
  73. package/dist/lib/reactivity/createChildrenState.d.ts +36 -0
  74. package/dist/lib/reactivity/createComputedExpression.d.ts +12 -0
  75. package/dist/lib/reactivity/createSelectItems.d.ts +16 -0
  76. package/dist/lib/reactivity/createValueState.d.ts +44 -0
  77. package/dist/lib/reactivity/materializeCurrentStateChildren.d.ts +18 -0
  78. package/dist/lib/reactivity/node-state/createClientState.d.ts +9 -0
  79. package/dist/lib/reactivity/node-state/createCurrentState.d.ts +6 -0
  80. package/dist/lib/reactivity/node-state/createEngineState.d.ts +5 -0
  81. package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +22 -0
  82. package/dist/lib/reactivity/node-state/createSpecifiedPropertyDescriptor.d.ts +6 -0
  83. package/dist/lib/reactivity/node-state/createSpecifiedState.d.ts +139 -0
  84. package/dist/lib/reactivity/node-state/representations.d.ts +25 -0
  85. package/dist/lib/reactivity/scope.d.ts +23 -0
  86. package/dist/lib/reactivity/text/createFieldHint.d.ts +5 -0
  87. package/dist/lib/reactivity/text/createNodeLabel.d.ts +5 -0
  88. package/dist/lib/reactivity/text/createTextRange.d.ts +19 -0
  89. package/dist/lib/reactivity/types.d.ts +21 -0
  90. package/dist/lib/unique-id.d.ts +27 -0
  91. package/dist/lib/xpath/analysis.d.ts +22 -0
  92. package/dist/model/BindComputation.d.ts +30 -0
  93. package/dist/model/BindDefinition.d.ts +31 -0
  94. package/dist/model/BindElement.d.ts +6 -0
  95. package/dist/model/DescendentNodeDefinition.d.ts +25 -0
  96. package/dist/model/ModelBindMap.d.ts +15 -0
  97. package/dist/model/ModelDefinition.d.ts +10 -0
  98. package/dist/model/NodeDefinition.d.ts +74 -0
  99. package/dist/model/RepeatInstanceDefinition.d.ts +15 -0
  100. package/dist/model/RepeatSequenceDefinition.d.ts +19 -0
  101. package/dist/model/RepeatTemplateDefinition.d.ts +29 -0
  102. package/dist/model/RootDefinition.d.ts +24 -0
  103. package/dist/model/SubtreeDefinition.d.ts +14 -0
  104. package/dist/model/ValueNodeDefinition.d.ts +15 -0
  105. package/dist/solid.js +37273 -0
  106. package/dist/solid.js.map +1 -0
  107. package/package.json +87 -0
  108. package/src/XFormDOM.ts +224 -0
  109. package/src/XFormDataType.ts +64 -0
  110. package/src/XFormDefinition.ts +40 -0
  111. package/src/body/BodyDefinition.ts +202 -0
  112. package/src/body/BodyElementDefinition.ts +62 -0
  113. package/src/body/RepeatDefinition.ts +54 -0
  114. package/src/body/UnsupportedBodyElementDefinition.ts +17 -0
  115. package/src/body/control/ControlDefinition.ts +42 -0
  116. package/src/body/control/InputDefinition.ts +9 -0
  117. package/src/body/control/select/ItemDefinition.ts +31 -0
  118. package/src/body/control/select/ItemsetDefinition.ts +36 -0
  119. package/src/body/control/select/ItemsetNodesetContext.ts +26 -0
  120. package/src/body/control/select/ItemsetNodesetExpression.ts +8 -0
  121. package/src/body/control/select/ItemsetValueExpression.ts +11 -0
  122. package/src/body/control/select/SelectDefinition.ts +74 -0
  123. package/src/body/group/BaseGroupDefinition.ts +137 -0
  124. package/src/body/group/LogicalGroupDefinition.ts +11 -0
  125. package/src/body/group/PresentationGroupDefinition.ts +28 -0
  126. package/src/body/group/RepeatGroupDefinition.ts +91 -0
  127. package/src/body/group/StructuralGroupDefinition.ts +11 -0
  128. package/src/body/text/HintDefinition.ts +26 -0
  129. package/src/body/text/LabelDefinition.ts +54 -0
  130. package/src/body/text/TextElementDefinition.ts +97 -0
  131. package/src/body/text/TextElementOutputPart.ts +27 -0
  132. package/src/body/text/TextElementPart.ts +31 -0
  133. package/src/body/text/TextElementReferencePart.ts +21 -0
  134. package/src/body/text/TextElementStaticPart.ts +26 -0
  135. package/src/client/BaseNode.ts +180 -0
  136. package/src/client/EngineConfig.ts +83 -0
  137. package/src/client/FormLanguage.ts +77 -0
  138. package/src/client/GroupNode.ts +33 -0
  139. package/src/client/OpaqueReactiveObjectFactory.ts +100 -0
  140. package/src/client/README.md +39 -0
  141. package/src/client/RepeatInstanceNode.ts +41 -0
  142. package/src/client/RepeatRangeNode.ts +100 -0
  143. package/src/client/RootNode.ts +36 -0
  144. package/src/client/SelectNode.ts +69 -0
  145. package/src/client/StringNode.ts +46 -0
  146. package/src/client/SubtreeNode.ts +57 -0
  147. package/src/client/TextRange.ts +63 -0
  148. package/src/client/hierarchy.ts +63 -0
  149. package/src/client/index.ts +29 -0
  150. package/src/client/node-types.ts +10 -0
  151. package/src/expression/DependencyContext.ts +53 -0
  152. package/src/expression/DependentExpression.ts +102 -0
  153. package/src/index.ts +35 -0
  154. package/src/instance/Group.ts +82 -0
  155. package/src/instance/RepeatInstance.ts +164 -0
  156. package/src/instance/RepeatRange.ts +214 -0
  157. package/src/instance/Root.ts +264 -0
  158. package/src/instance/SelectField.ts +204 -0
  159. package/src/instance/StringField.ts +93 -0
  160. package/src/instance/Subtree.ts +79 -0
  161. package/src/instance/abstract/DescendantNode.ts +182 -0
  162. package/src/instance/abstract/InstanceNode.ts +257 -0
  163. package/src/instance/children.ts +52 -0
  164. package/src/instance/hierarchy.ts +54 -0
  165. package/src/instance/identity.ts +11 -0
  166. package/src/instance/index.ts +37 -0
  167. package/src/instance/internal-api/EvaluationContext.ts +41 -0
  168. package/src/instance/internal-api/InstanceConfig.ts +9 -0
  169. package/src/instance/internal-api/SubscribableDependency.ts +61 -0
  170. package/src/instance/internal-api/TranslationContext.ts +5 -0
  171. package/src/instance/internal-api/ValueContext.ts +27 -0
  172. package/src/instance/resource.ts +75 -0
  173. package/src/instance/text/FormattedTextStub.ts +8 -0
  174. package/src/instance/text/TextChunk.ts +20 -0
  175. package/src/instance/text/TextRange.ts +23 -0
  176. package/src/lib/dom/query.ts +49 -0
  177. package/src/lib/reactivity/createChildrenState.ts +60 -0
  178. package/src/lib/reactivity/createComputedExpression.ts +114 -0
  179. package/src/lib/reactivity/createSelectItems.ts +163 -0
  180. package/src/lib/reactivity/createValueState.ts +258 -0
  181. package/src/lib/reactivity/materializeCurrentStateChildren.ts +121 -0
  182. package/src/lib/reactivity/node-state/createClientState.ts +51 -0
  183. package/src/lib/reactivity/node-state/createCurrentState.ts +27 -0
  184. package/src/lib/reactivity/node-state/createEngineState.ts +18 -0
  185. package/src/lib/reactivity/node-state/createSharedNodeState.ts +79 -0
  186. package/src/lib/reactivity/node-state/createSpecifiedPropertyDescriptor.ts +85 -0
  187. package/src/lib/reactivity/node-state/createSpecifiedState.ts +229 -0
  188. package/src/lib/reactivity/node-state/representations.ts +64 -0
  189. package/src/lib/reactivity/scope.ts +106 -0
  190. package/src/lib/reactivity/text/createFieldHint.ts +16 -0
  191. package/src/lib/reactivity/text/createNodeLabel.ts +16 -0
  192. package/src/lib/reactivity/text/createTextRange.ts +155 -0
  193. package/src/lib/reactivity/types.ts +27 -0
  194. package/src/lib/unique-id.ts +34 -0
  195. package/src/lib/xpath/analysis.ts +241 -0
  196. package/src/model/BindComputation.ts +88 -0
  197. package/src/model/BindDefinition.ts +104 -0
  198. package/src/model/BindElement.ts +8 -0
  199. package/src/model/DescendentNodeDefinition.ts +56 -0
  200. package/src/model/ModelBindMap.ts +71 -0
  201. package/src/model/ModelDefinition.ts +19 -0
  202. package/src/model/NodeDefinition.ts +146 -0
  203. package/src/model/RepeatInstanceDefinition.ts +39 -0
  204. package/src/model/RepeatSequenceDefinition.ts +53 -0
  205. package/src/model/RepeatTemplateDefinition.ts +150 -0
  206. package/src/model/RootDefinition.ts +121 -0
  207. package/src/model/SubtreeDefinition.ts +50 -0
  208. package/src/model/ValueNodeDefinition.ts +39 -0
@@ -0,0 +1,146 @@
1
+ import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
2
+ import type { RepeatDefinition } from '../body/RepeatDefinition.ts';
3
+ import type { BindDefinition } from './BindDefinition.ts';
4
+ import type { RepeatInstanceDefinition } from './RepeatInstanceDefinition.ts';
5
+ import type { RepeatSequenceDefinition } from './RepeatSequenceDefinition.ts';
6
+ import type { RepeatTemplateDefinition } from './RepeatTemplateDefinition.ts';
7
+ import type { RootDefinition } from './RootDefinition.ts';
8
+ import type { SubtreeDefinition } from './SubtreeDefinition.ts';
9
+ import type { ValueNodeDefinition } from './ValueNodeDefinition.ts';
10
+
11
+ /**
12
+ * Corresponds to the model/entry DOM root node, i.e.:
13
+ *
14
+ * - the element matching `/*` in primary instance expressions, a.k.a.
15
+ * - `/h:html/h:head/xf:model/xf:instance[1]/*`
16
+ */
17
+ export type RootNodeType = 'root';
18
+
19
+ /**
20
+ * Corresponds to a sequence of model/entry DOM subtrees which in turn
21
+ * corresponds to a <repeat> in the form body definition.
22
+ */
23
+ export type RepeatSequenceType = 'repeat-sequence';
24
+
25
+ /**
26
+ * Corresponds to a template definition for a repeat sequence, which either has
27
+ * an explicit `jr:template=""` attribute in the form definition or is inferred
28
+ * as a template from the form's first element matched by a <repeat nodeset>.
29
+ */
30
+ export type RepeatTemplateType = 'repeat-template';
31
+
32
+ /**
33
+ * Corresponds to a single instance of a model/entry DOM subtree which
34
+ * in turn corresponds to a <repeat> in the form body definition, and a
35
+ * 'repeat-sequence' definition.
36
+ */
37
+ export type RepeatInstanceType = 'repeat-instance';
38
+
39
+ /**
40
+ * Corresponds to a model/entry DOM subtree which **does not** correspond to a
41
+ * <repeat> in the form definition. This will typically correspond to a <group>,
42
+ * but this is not strictly necessary per spec (hence the distinct name).
43
+ */
44
+ export type SubtreeNodeType = 'subtree';
45
+
46
+ /**
47
+ * Corresponds to a model/entry DOM leaf node, i.e. one of:
48
+ *
49
+ * - An element with no child elements
50
+ * - Any attribute corresponding to a bind's `nodeset` expression
51
+ */
52
+ export type ValueNodeType = 'value-node';
53
+
54
+ // prettier-ignore
55
+ export type NodeDefinitionType =
56
+ // eslint-disable-next-line @typescript-eslint/sort-type-constituents
57
+ | RootNodeType
58
+ | RepeatSequenceType
59
+ | RepeatTemplateType
60
+ | RepeatInstanceType
61
+ | SubtreeNodeType
62
+ | ValueNodeType;
63
+
64
+ // prettier-ignore
65
+ export type ParentNodeDefinition =
66
+ // eslint-disable-next-line @typescript-eslint/sort-type-constituents
67
+ | RootDefinition
68
+ | RepeatTemplateDefinition
69
+ | RepeatInstanceDefinition
70
+ | SubtreeDefinition;
71
+
72
+ // prettier-ignore
73
+ export type ChildNodeDefinition =
74
+ | RepeatSequenceDefinition
75
+ | SubtreeDefinition
76
+ | ValueNodeDefinition;
77
+
78
+ // prettier-ignore
79
+ export type ChildNodeInstanceDefinition =
80
+ // eslint-disable-next-line @typescript-eslint/sort-type-constituents
81
+ | RepeatTemplateDefinition
82
+ | RepeatInstanceDefinition
83
+ | SubtreeDefinition
84
+ | ValueNodeDefinition;
85
+
86
+ // prettier-ignore
87
+ export type NodeChildren<Type extends NodeDefinitionType> =
88
+ Type extends ParentNodeDefinition['type']
89
+ ? readonly ChildNodeDefinition[]
90
+ : null;
91
+
92
+ // prettier-ignore
93
+ export type NodeInstances<Type extends NodeDefinitionType> =
94
+ Type extends 'repeat-sequence'
95
+ ? readonly RepeatInstanceDefinition[]
96
+ : null;
97
+
98
+ // prettier-ignore
99
+ export type NodeParent<Type extends NodeDefinitionType> =
100
+ Type extends ChildNodeDefinition['type'] | ChildNodeInstanceDefinition['type']
101
+ ? ParentNodeDefinition
102
+ : null;
103
+
104
+ // TODO: value-node may be Attr
105
+ // prettier-ignore
106
+ export type ModelNode<Type extends NodeDefinitionType> =
107
+ Type extends 'repeat-sequence'
108
+ ? null
109
+ : Element;
110
+
111
+ // prettier-ignore
112
+ export type NodeDefaultValue<Type extends NodeDefinitionType> =
113
+ Type extends 'value-node'
114
+ ? string
115
+ : null;
116
+
117
+ export interface NodeDefinition<Type extends NodeDefinitionType> {
118
+ readonly type: Type;
119
+
120
+ readonly bind: BindDefinition;
121
+ readonly nodeset: string;
122
+ readonly nodeName: string;
123
+ readonly bodyElement: AnyBodyElementDefinition | RepeatDefinition | null;
124
+
125
+ readonly isTranslated: boolean;
126
+ readonly dependencyExpressions: ReadonlySet<string>;
127
+
128
+ readonly root: RootDefinition;
129
+ readonly parent: NodeParent<Type>;
130
+ readonly children: NodeChildren<Type>;
131
+ readonly instances: NodeInstances<Type>;
132
+
133
+ readonly node: ModelNode<Type>;
134
+ readonly defaultValue: NodeDefaultValue<Type>;
135
+ }
136
+
137
+ export type AnyNodeDefinition =
138
+ // eslint-disable-next-line @typescript-eslint/sort-type-constituents
139
+ | RootDefinition
140
+ | RepeatSequenceDefinition
141
+ | RepeatTemplateDefinition
142
+ | RepeatInstanceDefinition
143
+ | SubtreeDefinition
144
+ | ValueNodeDefinition;
145
+
146
+ export type TypedNodeDefinition<Type> = Extract<AnyNodeDefinition, { readonly type: Type }>;
@@ -0,0 +1,39 @@
1
+ import { RepeatDefinition } from '../body/RepeatDefinition.ts';
2
+ import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
3
+ import type { ChildNodeDefinition, NodeDefinition } from './NodeDefinition.ts';
4
+ import type { RepeatSequenceDefinition } from './RepeatSequenceDefinition.ts';
5
+
6
+ export class RepeatInstanceDefinition
7
+ extends DescendentNodeDefinition<'repeat-instance', RepeatDefinition>
8
+ implements NodeDefinition<'repeat-instance'>
9
+ {
10
+ readonly type = 'repeat-instance';
11
+
12
+ readonly nodeName: string;
13
+ readonly children: readonly ChildNodeDefinition[];
14
+ readonly instances = null;
15
+ readonly defaultValue = null;
16
+
17
+ constructor(
18
+ protected readonly sequence: RepeatSequenceDefinition,
19
+ readonly node: Element
20
+ ) {
21
+ const {
22
+ bind,
23
+ bodyElement: repeatGroupBodyElement,
24
+ parent: repeatSequenceParent,
25
+ root,
26
+ } = sequence;
27
+
28
+ super(repeatSequenceParent, bind, repeatGroupBodyElement.repeat);
29
+
30
+ this.nodeName = sequence.nodeName;
31
+ this.children = root.buildSubtree(this);
32
+ }
33
+
34
+ toJSON() {
35
+ const { bind, bodyElement, parent, root, sequence, ...rest } = this;
36
+
37
+ return rest;
38
+ }
39
+ }
@@ -0,0 +1,53 @@
1
+ import { RepeatGroupDefinition } from '../body/group/RepeatGroupDefinition.ts';
2
+ import type { BindDefinition } from './BindDefinition.ts';
3
+ import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
4
+ import type { NodeDefinition, ParentNodeDefinition } from './NodeDefinition.ts';
5
+ import { RepeatInstanceDefinition } from './RepeatInstanceDefinition.ts';
6
+ import { RepeatTemplateDefinition } from './RepeatTemplateDefinition.ts';
7
+
8
+ export class RepeatSequenceDefinition
9
+ extends DescendentNodeDefinition<'repeat-sequence', RepeatGroupDefinition>
10
+ implements NodeDefinition<'repeat-sequence'>
11
+ {
12
+ // TODO: if an implicit template is derived from an instance in a form
13
+ // definition, should its default values (if any) be cleared? Probably!
14
+ static createTemplateElement(instanceElement: Element): Element {
15
+ return instanceElement.cloneNode(true) as Element;
16
+ }
17
+
18
+ static createInstanceElement(templateElement: Element): Element {
19
+ return templateElement.cloneNode(true) as Element;
20
+ }
21
+
22
+ readonly type = 'repeat-sequence';
23
+
24
+ readonly template: RepeatTemplateDefinition;
25
+ readonly children = null;
26
+ readonly instances: RepeatInstanceDefinition[];
27
+
28
+ readonly node = null;
29
+ readonly nodeName: string;
30
+ readonly defaultValue = null;
31
+
32
+ constructor(
33
+ parent: ParentNodeDefinition,
34
+ bind: BindDefinition,
35
+ bodyElement: RepeatGroupDefinition,
36
+ modelNodes: readonly [Element, ...Element[]]
37
+ ) {
38
+ super(parent, bind, bodyElement);
39
+ const { template, instanceNodes } = RepeatTemplateDefinition.parseModelNodes(this, modelNodes);
40
+
41
+ this.template = template;
42
+ this.nodeName = template.nodeName;
43
+ this.instances = instanceNodes.map((element) => {
44
+ return new RepeatInstanceDefinition(this, element);
45
+ });
46
+ }
47
+
48
+ toJSON() {
49
+ const { bind, bodyElement: groupDefinition, parent, root, ...rest } = this;
50
+
51
+ return rest;
52
+ }
53
+ }
@@ -0,0 +1,150 @@
1
+ import { JAVAROSA_NAMESPACE_URI } from '@getodk/common/constants/xmlns.ts';
2
+ import type { RepeatDefinition } from '../body/RepeatDefinition.ts';
3
+ import { BindDefinition } from './BindDefinition.ts';
4
+ import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
5
+ import type { ChildNodeDefinition, NodeDefinition } from './NodeDefinition.ts';
6
+ import type { RepeatSequenceDefinition } from './RepeatSequenceDefinition.ts';
7
+
8
+ const repeatTemplates = new WeakMap<BindDefinition, RepeatTemplateDefinition>();
9
+
10
+ interface ExplicitRepeatTemplateElement extends Element {
11
+ getAttributeNS(namespaceURI: typeof JAVAROSA_NAMESPACE_URI, name: 'template'): string;
12
+ getAttributeNS(namespaceURI: string | null, name: string): string | null;
13
+
14
+ hasAttributeNS(namespaceURI: typeof JAVAROSA_NAMESPACE_URI, name: 'template'): true;
15
+ (namespaceURI: string | null, name: string): boolean;
16
+ }
17
+
18
+ const isExplicitRepeatTemplateElement = (
19
+ element: Element
20
+ ): element is ExplicitRepeatTemplateElement => {
21
+ return element.hasAttributeNS(JAVAROSA_NAMESPACE_URI, 'template');
22
+ };
23
+
24
+ type InstanceNodes = readonly [template: ExplicitRepeatTemplateElement, ...rest: Element[]];
25
+
26
+ interface LeafNode extends Element {
27
+ readonly childElementCount: 0;
28
+ }
29
+
30
+ const isLeafNode = (element: Element): element is LeafNode => {
31
+ return element.childElementCount === 0;
32
+ };
33
+
34
+ const clearLeafNodes = <T extends Element>(element: T): T => {
35
+ if (isLeafNode(element)) {
36
+ element.textContent = '';
37
+ } else {
38
+ for (const child of element.children) {
39
+ clearLeafNodes(child);
40
+ }
41
+ }
42
+
43
+ return element;
44
+ };
45
+
46
+ const getOrCreateTemplateElement = (element: Element): ExplicitRepeatTemplateElement => {
47
+ if (isExplicitRepeatTemplateElement(element)) {
48
+ return element;
49
+ }
50
+
51
+ const clone = element.cloneNode(true) as Element;
52
+ clone.setAttributeNS(JAVAROSA_NAMESPACE_URI, 'template', '');
53
+
54
+ return clearLeafNodes(clone as ExplicitRepeatTemplateElement);
55
+ };
56
+
57
+ // TODO: under what circumstances should a default instance be created, and is
58
+ // this the appropriate place for that?
59
+ const splitInstanceNodes = (modelNodes: readonly [Element, ...Element[]]): InstanceNodes => {
60
+ const [first, ...rest] = modelNodes;
61
+ const template = getOrCreateTemplateElement(first);
62
+
63
+ if (template === first) {
64
+ return [template, ...rest];
65
+ }
66
+
67
+ return [template, ...modelNodes];
68
+ };
69
+
70
+ interface ParsedRepeatNodes {
71
+ readonly template: RepeatTemplateDefinition;
72
+ readonly instanceNodes: readonly Element[];
73
+ }
74
+
75
+ export class RepeatTemplateDefinition
76
+ extends DescendentNodeDefinition<'repeat-template', RepeatDefinition>
77
+ implements NodeDefinition<'repeat-template'>
78
+ {
79
+ static parseModelNodes(
80
+ sequence: RepeatSequenceDefinition,
81
+ modelNodes: readonly [Element, ...Element[]]
82
+ ): ParsedRepeatNodes {
83
+ const { bind } = sequence;
84
+
85
+ let template = repeatTemplates.get(bind);
86
+ let instanceNodes: readonly Element[];
87
+
88
+ if (template == null) {
89
+ const [templateNode, ...rest] = splitInstanceNodes(modelNodes);
90
+
91
+ instanceNodes = rest;
92
+ template = new this(sequence, templateNode);
93
+ } else {
94
+ // TODO: this is under the assumption that for any depth > 1, if a
95
+ // template has already been defined for the given form definition, any
96
+ // subsequent nodes matching the repeat's nodeset are implicitly default
97
+ // instances. Is this right?
98
+ const duplicateTemplate = modelNodes.find((node) =>
99
+ node.hasAttributeNS(JAVAROSA_NAMESPACE_URI, 'template')
100
+ );
101
+
102
+ if (duplicateTemplate != null) {
103
+ throw new Error(`Multiple explicit templates defined for ${bind.nodeset}`);
104
+ }
105
+
106
+ instanceNodes = modelNodes;
107
+ }
108
+
109
+ return {
110
+ template,
111
+ instanceNodes,
112
+ };
113
+ }
114
+
115
+ readonly type = 'repeat-template';
116
+
117
+ readonly node: Element;
118
+ readonly nodeName: string;
119
+ readonly children: readonly ChildNodeDefinition[];
120
+ readonly instances = null;
121
+ readonly defaultValue = null;
122
+
123
+ protected constructor(
124
+ protected readonly sequence: RepeatSequenceDefinition,
125
+ protected readonly templateNode: ExplicitRepeatTemplateElement
126
+ ) {
127
+ const {
128
+ bind,
129
+ bodyElement: repeatGroupBodyElement,
130
+ parent: repeatSequenceParent,
131
+ root,
132
+ } = sequence;
133
+
134
+ super(repeatSequenceParent, bind, repeatGroupBodyElement.repeat);
135
+
136
+ const node = templateNode.cloneNode(true) as Element;
137
+
138
+ node.removeAttributeNS(JAVAROSA_NAMESPACE_URI, 'template');
139
+
140
+ this.node = node;
141
+ this.nodeName = node.localName;
142
+ this.children = root.buildSubtree(this);
143
+ }
144
+
145
+ toJSON() {
146
+ const { bind, bodyElement, parent, root, sequence, ...rest } = this;
147
+
148
+ return rest;
149
+ }
150
+ }
@@ -0,0 +1,121 @@
1
+ import type { XFormDefinition } from '../XFormDefinition.ts';
2
+ import type { RepeatGroupDefinition } from '../body/group/RepeatGroupDefinition.ts';
3
+ import type { BindDefinition } from './BindDefinition.ts';
4
+ import type { ModelDefinition } from './ModelDefinition.ts';
5
+ import type {
6
+ ChildNodeDefinition,
7
+ NodeDefinition,
8
+ ParentNodeDefinition,
9
+ } from './NodeDefinition.ts';
10
+ import { RepeatSequenceDefinition } from './RepeatSequenceDefinition.ts';
11
+ import { SubtreeDefinition } from './SubtreeDefinition.ts';
12
+ import { ValueNodeDefinition } from './ValueNodeDefinition.ts';
13
+
14
+ export class RootDefinition implements NodeDefinition<'root'> {
15
+ readonly type = 'root';
16
+ readonly bind: BindDefinition;
17
+ readonly nodeset: string;
18
+ readonly nodeName: string;
19
+ readonly bodyElement = null;
20
+ readonly root = this;
21
+ readonly parent = null;
22
+ readonly children: readonly ChildNodeDefinition[];
23
+ readonly instances = null;
24
+ readonly node: Element;
25
+ readonly defaultValue = null;
26
+
27
+ readonly isTranslated = false;
28
+ readonly dependencyExpressions: ReadonlySet<string> = new Set<string>();
29
+
30
+ constructor(
31
+ protected readonly form: XFormDefinition,
32
+ protected readonly model: ModelDefinition
33
+ ) {
34
+ // TODO: theoretically the pertinent step in the bind's `nodeset` *could* be
35
+ // namespaced. It also may make more sense to determine the root nodeset
36
+ // earlier (i.e. in the appropriate definition class).
37
+ //
38
+ // TODO: while it's unlikely a form actually defines a <bind> for the root,
39
+ // if it did, bind nodesets are not yet normalized, so `/root` may currently
40
+ // be defined as `/ root` (or even `/ *` or any other valid expression
41
+ // resolving to the root).
42
+ const { primaryInstanceRoot } = form.xformDOM;
43
+ const { localName: rootNodeName } = primaryInstanceRoot;
44
+
45
+ this.nodeName = rootNodeName;
46
+
47
+ const nodeset = `/${rootNodeName}`;
48
+ const bind = model.binds.get(nodeset);
49
+
50
+ if (bind == null) {
51
+ throw new Error('Missing root node bind definition');
52
+ }
53
+
54
+ this.bind = bind;
55
+ this.nodeset = nodeset;
56
+ this.node = primaryInstanceRoot;
57
+ this.children = this.buildSubtree(this);
58
+ }
59
+
60
+ buildSubtree(parent: ParentNodeDefinition): readonly ChildNodeDefinition[] {
61
+ const { form, model } = this;
62
+ const { body } = form;
63
+ const { binds } = model;
64
+ const { bind: parentBind, node } = parent;
65
+ const { nodeset: parentNodeset } = parentBind;
66
+
67
+ const childrenByName = new Map<string, [Element, ...Element[]]>();
68
+
69
+ for (const child of node.children) {
70
+ const { localName } = child;
71
+
72
+ let elements = childrenByName.get(localName);
73
+
74
+ if (elements == null) {
75
+ elements = [child];
76
+ childrenByName.set(localName, elements);
77
+ } else {
78
+ // TODO: check if previous element exists, was it previous element
79
+ // sibling. Highly likely this should otherwise fail!
80
+ elements.push(child);
81
+ }
82
+ }
83
+
84
+ return Array.from(childrenByName).map(([localName, children]) => {
85
+ const nodeset = `${parentNodeset}/${localName}`;
86
+ const bind = binds.getOrCreateBindDefinition(nodeset);
87
+ const bodyElement = body.getBodyElement(nodeset);
88
+ const [firstChild, ...restChildren] = children;
89
+ const repeatGroup = body.getRepeatGroup(nodeset);
90
+
91
+ if (repeatGroup != null) {
92
+ const repeatDefinition = (bodyElement as RepeatGroupDefinition).repeat;
93
+
94
+ if (repeatDefinition == null) {
95
+ throw 'TODO: this is why I have hesitated to pick an "is repeat" predicate direction';
96
+ }
97
+
98
+ return new RepeatSequenceDefinition(parent, bind, repeatGroup, children);
99
+ }
100
+
101
+ if (restChildren.length) {
102
+ throw new Error(`Unexpected: multiple elements for non-repeat nodeset: ${nodeset}`);
103
+ }
104
+
105
+ const element = firstChild;
106
+ const isLeafNode = element.childElementCount === 0;
107
+
108
+ if (isLeafNode) {
109
+ return new ValueNodeDefinition(parent, bind, bodyElement, element);
110
+ }
111
+
112
+ return new SubtreeDefinition(parent, bind, bodyElement, element);
113
+ });
114
+ }
115
+
116
+ toJSON() {
117
+ const { bind, bodyElement, form, model, root, ...rest } = this;
118
+
119
+ return rest;
120
+ }
121
+ }
@@ -0,0 +1,50 @@
1
+ import type {
2
+ AnyBodyElementDefinition,
3
+ NonRepeatGroupElementDefinition,
4
+ } from '../body/BodyDefinition.ts';
5
+ import type { BindDefinition } from './BindDefinition.ts';
6
+ import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
7
+ import type {
8
+ ChildNodeDefinition,
9
+ NodeDefinition,
10
+ ParentNodeDefinition,
11
+ } from './NodeDefinition.ts';
12
+
13
+ export class SubtreeDefinition
14
+ extends DescendentNodeDefinition<'subtree', NonRepeatGroupElementDefinition | null>
15
+ implements NodeDefinition<'subtree'>
16
+ {
17
+ readonly type = 'subtree';
18
+
19
+ readonly nodeName: string;
20
+ readonly children: readonly ChildNodeDefinition[];
21
+ readonly instances = null;
22
+ readonly defaultValue = null;
23
+
24
+ constructor(
25
+ parent: ParentNodeDefinition,
26
+ bind: BindDefinition,
27
+ bodyElement: AnyBodyElementDefinition | null,
28
+ readonly node: Element
29
+ ) {
30
+ if (
31
+ bodyElement != null &&
32
+ (bodyElement.category !== 'structure' || bodyElement.type === 'repeat-group')
33
+ ) {
34
+ throw new Error(`Unexpected body element for nodeset ${bind.nodeset}`);
35
+ }
36
+
37
+ super(parent, bind, bodyElement);
38
+
39
+ const { root } = parent;
40
+
41
+ this.nodeName = node.localName;
42
+ this.children = root.buildSubtree(this);
43
+ }
44
+
45
+ toJSON() {
46
+ const { parent, bodyElement, bind, root, ...rest } = this;
47
+
48
+ return rest;
49
+ }
50
+ }
@@ -0,0 +1,39 @@
1
+ import type { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
2
+ import type { AnyControlDefinition } from '../body/control/ControlDefinition.ts';
3
+ import type { BindDefinition } from './BindDefinition.ts';
4
+ import { DescendentNodeDefinition } from './DescendentNodeDefinition.ts';
5
+ import type { NodeDefinition, ParentNodeDefinition } from './NodeDefinition.ts';
6
+
7
+ export class ValueNodeDefinition
8
+ extends DescendentNodeDefinition<'value-node', AnyControlDefinition | null>
9
+ implements NodeDefinition<'value-node'>
10
+ {
11
+ readonly type = 'value-node';
12
+
13
+ readonly nodeName: string;
14
+ readonly children = null;
15
+ readonly instances = null;
16
+ readonly defaultValue: string;
17
+
18
+ constructor(
19
+ parent: ParentNodeDefinition,
20
+ bind: BindDefinition,
21
+ bodyElement: AnyBodyElementDefinition | null,
22
+ readonly node: Element
23
+ ) {
24
+ if (bodyElement != null && bodyElement.category !== 'control') {
25
+ throw new Error(`Unexpected body element for nodeset ${bind.nodeset}`);
26
+ }
27
+
28
+ super(parent, bind, bodyElement);
29
+
30
+ this.nodeName = node.localName;
31
+ this.defaultValue = node.textContent ?? '';
32
+ }
33
+
34
+ toJSON() {
35
+ const { bind, bodyElement, parent, root, ...rest } = this;
36
+
37
+ return rest;
38
+ }
39
+ }