@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,138 @@
1
+ import type { AnyNodeDefinition } from '../model/NodeDefinition.ts';
2
+ import type { InstanceNodeType } from './node-types.js';
3
+ import type { TextRange } from './TextRange.ts';
4
+ export interface BaseNodeState {
5
+ /**
6
+ * Location path reference to the node's primary instance state. This property
7
+ * may change if a node's position changes, e.g. when a repeat instance is
8
+ * removed. Its potential reactivity allows nodes to re-run computations which
9
+ * depend on the node's position itself, or when any other relative reference
10
+ * might target different nodes as a result of the positional change.
11
+ *
12
+ * @example
13
+ * /data/repeat[1]/foo
14
+ * /data/repeat[2]/foo
15
+ */
16
+ get reference(): string;
17
+ /**
18
+ * Note: a node's `readonly` state may become `true` by inheriting that state
19
+ * from one of its ancestors. Computing this inheritance is handled by the
20
+ * engine, but it may be of interest to clients.
21
+ *
22
+ * In the future, a more granular type might convey this detail more
23
+ * explicitly (at the expense of a more complex type). For now, a client can
24
+ * infer that inheritance by visiting the
25
+ * {@link BaseNode.parent | parent node}.
26
+ */
27
+ get readonly(): boolean;
28
+ /**
29
+ * Note: a node's `relevant` state may become `false` by inheriting that state
30
+ * from one of its ancestors. Computing this inheritance is handled by the
31
+ * engine, but it may be of interest to clients.
32
+ *
33
+ * In the future, a more granular type might convey this detail more
34
+ * explicitly (at the expense of a more complex type). For now, a client can
35
+ * infer that inheritance by visiting the
36
+ * {@link BaseNode.parent | parent node}.
37
+ */
38
+ get relevant(): boolean;
39
+ get required(): boolean;
40
+ /**
41
+ * Interfaces for nodes which cannot provide a label should override this to
42
+ * specify that the property will always be `null`.
43
+ */
44
+ get label(): TextRange<'label'> | null;
45
+ /**
46
+ * Interfaces for nodes which cannot provide a hint should override this to
47
+ * specify that the property will always be `null`.
48
+ */
49
+ get hint(): TextRange<'hint'> | null;
50
+ /**
51
+ * Each node's children (if it is a parent node) will be accessed on that
52
+ * node's state. While some node types will technically have static children,
53
+ * other nodes' children will be stateful (i.e. repeats). For a client, both
54
+ * cases are accessed the same way for consistency.
55
+ *
56
+ * Certain kinds of nodes are considered parent nodes: they may have child
57
+ * nodes. In some cases (presently, repeat ranges), children may be added or
58
+ * removed while a user is filling a form. As such, those children must be
59
+ * accessed as part of the node's
60
+ * {@link BaseNode.currentState | current state}. (In contrast, child nodes
61
+ * are never moved between different parents, so their
62
+ * {@link BaseNode.parent | parent} is static rather than part of their
63
+ * current state).
64
+ *
65
+ * A node is either:
66
+ *
67
+ * - Always a parent, in which case its `children` state should always produce
68
+ * an array. When the parent node's children can be added or removed, an
69
+ * empty array should be used to represent the absence of any children in
70
+ * its current state.
71
+ * - Never a parent, in which case its `children` state should always produce
72
+ * `null`. Such a node will instead have a {@link value}.
73
+ */
74
+ get children(): readonly BaseNode[] | null;
75
+ /**
76
+ * Certain kinds of nodes restrict their {@link value} to a specific set of
77
+ * valid values. Where they do, they will provide a collection (typically an
78
+ * array) of those values. This collection may be updated depending on other
79
+ * aspects of form state, which is why it is treated as a part of the node's
80
+ * state.
81
+ *
82
+ * Nodes which do not have this restriction (including nodes which cannot have
83
+ * a value at all) will always produce `valueOptions: null`.
84
+ */
85
+ get valueOptions(): unknown;
86
+ /**
87
+ * Certain kinds of nodes store a value state. Where they do, they will
88
+ * specify the type of the value directly.
89
+ *
90
+ * Parent nodes, i.e. nodes which can contain {@link children}, do not store a
91
+ * value state. For those nodes, their value state should always be `null`.
92
+ */
93
+ get value(): unknown;
94
+ }
95
+ type FormNodeID = string;
96
+ /**
97
+ * Base interface for common/shared aspects of any node type.
98
+ */
99
+ export interface BaseNode {
100
+ /**
101
+ * Specifies the node's general type. This can be useful for narrowing types,
102
+ * e.g. those of children.
103
+ */
104
+ readonly nodeType: InstanceNodeType;
105
+ /**
106
+ * Each node has a unique identifier. This identifier is stable throughout
107
+ * the lifetime of an active session filling a form.
108
+ */
109
+ readonly nodeId: FormNodeID;
110
+ /**
111
+ * Each node has a definition which specifies aspects of the node defined in
112
+ * the form. These aspects include (but are not limited to) the node's data
113
+ * type, its body presentation constraints (if any), its bound nodeset, and
114
+ * so on...
115
+ *
116
+ * @todo Interfaces for specific (non-base) node types should override this
117
+ * to specify the actual (concrete or union) type of their `definition`.
118
+ */
119
+ readonly definition: AnyNodeDefinition;
120
+ /**
121
+ * Each node links back to the node representing the root of the form.
122
+ *
123
+ * @todo Interfaces for all concrete node types should override this to
124
+ * specify the actual root node type.
125
+ */
126
+ readonly root: BaseNode;
127
+ /**
128
+ * Each node links back to its parent, if any. All nodes have a parent except
129
+ * the form's {@link root}.
130
+ */
131
+ readonly parent: BaseNode | null;
132
+ /**
133
+ * Each node provides a discrete object representing the stateful aspects of
134
+ * that node which will change over time. When a client provides a {@link OpaqueReactiveObjectFactory}
135
+ */
136
+ readonly currentState: BaseNodeState;
137
+ }
138
+ export {};
@@ -0,0 +1,78 @@
1
+ import type { OpaqueReactiveObjectFactory } from './OpaqueReactiveObjectFactory.ts';
2
+ /**
3
+ * @todo this is currently a strict subset of the web standard `Response`. Is it
4
+ * sufficient? Ways it might not be:
5
+ *
6
+ * - No way to convey metadata about the resource
7
+ * - Ambiguous if a client supplies an alternative implementation which doesn't
8
+ * exhaust the body on access
9
+ */
10
+ export interface FetchResourceResponse {
11
+ readonly ok?: boolean;
12
+ readonly body?: ReadableStream<Uint8Array> | null;
13
+ readonly bodyUsed?: boolean;
14
+ readonly blob: () => Promise<Blob>;
15
+ readonly text: () => Promise<string>;
16
+ }
17
+ /**
18
+ * @todo this is a strict subset of the web standard `fetch` interface. It
19
+ * implicitly assumes that the engine itself will only ever issue `GET`-like
20
+ * requests. It also provides no further request-like semantics to the engine.
21
+ * This is presumed sufficient for now, but notably doesn't expose any notion of
22
+ * content negotiation (e.g. the ability to supply `Accept` headers).
23
+ *
24
+ * This also completely ignores any notion of mapping
25
+ * {@link https://getodk.github.io/xforms-spec/#uris | `jr:` URLs} to their
26
+ * actual resources (likely, but not necessarily, accessed at a corresponding
27
+ * HTTP URL).
28
+ */
29
+ export type FetchResource = (resource: URL) => Promise<FetchResourceResponse>;
30
+ /**
31
+ * Options provided by a client to specify certain aspects of engine runtime
32
+ * behavior. These options will generally be intended to facilitate cooperation
33
+ * where there is mixed responsibility between a client and the engine, or where
34
+ * the engine may provide sensible defaults which a client could be expected to
35
+ * override or augment.
36
+ */
37
+ export interface EngineConfig {
38
+ /**
39
+ * A client may specify a generic function for constructing stateful objects.
40
+ * The only hard requirement of this function is that it accepts an **object**
41
+ * and returns an object of the same shape. The engine will use this function
42
+ * to initialize client-facing state, and will mutate properties of the object
43
+ * when their corresponding state is changed.
44
+ *
45
+ * A client may use this function to provide its own implementation of
46
+ * reactivity with semantics like those described above. The mechanism of
47
+ * reactivity, if any, is at the discretion of the client. It is expected
48
+ * that clients providing this function will use a reactive subscribe-on-read
49
+ * mechanism to handle state updates conveyed by the engine.
50
+ */
51
+ readonly stateFactory?: OpaqueReactiveObjectFactory;
52
+ /**
53
+ * A client may specify a generic function for retrieving resources referenced
54
+ * by a form, such as:
55
+ *
56
+ * - Form definitions themselves (if not provided directly to the engine by
57
+ * the client)
58
+ * - External secondary instances
59
+ * - Media (images, audio, video, etc.)
60
+ *
61
+ * The function is expected to be a subset of the
62
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API | Fetch API},
63
+ * performing `GET` requests for:
64
+ *
65
+ * - Text resources (e.g. XML, CSV, JSON/GeoJSON)
66
+ * - Binary resources (e.g. media)
67
+ * - Optionally streamed binary data of either (e.g. for optimized
68
+ * presentation of audio/video)
69
+ *
70
+ * If provided by a client, this function will be used by the engine to
71
+ * retrieve any such resource, as required for engine functionality. If
72
+ * absent, the engine will use the native `fetch` function (if available, a
73
+ * polyfill otherwise). Clients may use this function to provide resources
74
+ * from sources other than the network, (or even in a test client to provide
75
+ * e.g. resources from test fixtures).
76
+ */
77
+ readonly fetchResource?: FetchResource;
78
+ }
@@ -0,0 +1,63 @@
1
+ interface BaseFormLanguage {
2
+ /**
3
+ * @see {@link ActiveLanguage} for details.
4
+ */
5
+ readonly isSyntheticDefault?: true;
6
+ /**
7
+ * As derived directly from the form's
8
+ * {@link https://getodk.github.io/xforms-spec/#languages | `itext` translations}.
9
+ */
10
+ readonly language: string;
11
+ /**
12
+ * Where possible, a form's languages may detect the standardized locale
13
+ * corresponding to the language as specified in the form.
14
+ */
15
+ readonly locale?: Intl.Locale;
16
+ }
17
+ /**
18
+ * @see {@link ActiveLanguage} for details.
19
+ */
20
+ export interface SyntheticDefaultLanguage extends BaseFormLanguage {
21
+ readonly isSyntheticDefault: true;
22
+ readonly language: '';
23
+ }
24
+ /**
25
+ * A language available for a given form, as defined by that form's {@link https://getodk.github.io/xforms-spec/#languages | `itext` translations}.
26
+ *
27
+ * @see {@link ActiveLanguage} for additional details.
28
+ */
29
+ export interface FormLanguage extends BaseFormLanguage {
30
+ readonly isSyntheticDefault?: never;
31
+ }
32
+ /**
33
+ * A form with translations will always have one or more {@link FormLanguage}s,
34
+ * with the default chosen as described in the ODK XForms specification.
35
+ *
36
+ * A form that specifies no translations will always have a single
37
+ * {@link SyntheticDefaultLanguage}.
38
+ *
39
+ * No form will ever combine both types of language.
40
+ *
41
+ * This distinction is intended to avoid confusion about the **potential** state
42
+ * of a form's active language. A naive type to express the possibility, without
43
+ * a synthetic default, would be something like `FormLanguage | null`, which
44
+ * would seem to suggest to a client that a form may **only sometimes** have an
45
+ * active language—for instance, that there might be a way for a client to turn
46
+ * translation off and on.
47
+ *
48
+ * By ensuring there is always _some active language value_, and by expressing
49
+ * the {@link FormLanguages} type to correspond to each of the possibilities
50
+ * discussed above, it's hoped that the set of potential translation states a
51
+ * client might encounter/establish are more clear.
52
+ */
53
+ export type ActiveLanguage = FormLanguage | SyntheticDefaultLanguage;
54
+ /**
55
+ * A form may either have:
56
+ *
57
+ * - one or more available {@link FormLanguage}s, as defined by the form
58
+ * - exactly one {@link SyntheticDefaultLanguage}
59
+ *
60
+ * @see {@link ActiveLanguage} for additional details.
61
+ */
62
+ export type FormLanguages = readonly [FormLanguage, ...FormLanguage[]] | readonly [SyntheticDefaultLanguage];
63
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { NonRepeatGroupElementDefinition } from '../body/BodyDefinition.ts';
2
+ import type { SubtreeDefinition } from '../model/SubtreeDefinition.ts';
3
+ import type { BaseNode, BaseNodeState } from './BaseNode.ts';
4
+ import type { RootNode } from './RootNode.ts';
5
+ import type { GeneralChildNode, GeneralParentNode } from './hierarchy.ts';
6
+ export interface GroupNodeState extends BaseNodeState {
7
+ get hint(): null;
8
+ get children(): readonly GeneralChildNode[];
9
+ get valueOptions(): null;
10
+ get value(): null;
11
+ }
12
+ export interface GroupDefinition extends SubtreeDefinition {
13
+ readonly bodyElement: NonRepeatGroupElementDefinition;
14
+ }
15
+ /**
16
+ * A node corresponding to an XForms `<group>`.
17
+ */
18
+ export interface GroupNode extends BaseNode {
19
+ readonly nodeType: 'group';
20
+ readonly definition: GroupDefinition;
21
+ readonly root: RootNode;
22
+ readonly parent: GeneralParentNode;
23
+ readonly currentState: GroupNodeState;
24
+ }
@@ -0,0 +1,70 @@
1
+ interface BaseOpaqueReactiveObjectFactory<in out Input extends object = object> {
2
+ <T extends Input>(object: T): T;
3
+ <T extends object>(object: T): T;
4
+ }
5
+ /**
6
+ * A client-provided reactivity factory. We assume little about the mechanism
7
+ * of reactivity a client provides (and a client may opt to provide no reactive
8
+ * mechanism at all, if it will consume state changes by other means). From an
9
+ * API perspective, the expectation is:
10
+ *
11
+ * - The factory accepts a single argument, which **MUST** be an object.
12
+ * Reactive primitive values are unsupported by this interface.
13
+ * - The factory **MUST** return an object of the same shape (i.e. it must
14
+ * have the same property key/value pairs).
15
+ * - The factory's return object **MUST** be mutable by the web-forms engine.
16
+ * - The web-forms engine **WILL** propagate changes to state as they occur,
17
+ * by mutating properties of the object corresponding to those aspects of
18
+ * state.
19
+ * - The client **MAY** read any property of the factory's returned object.
20
+ * - Because the client's reactivity mechanism (if any) is unknown, it is
21
+ * **ASSUMED** that the engine's mutations are observable by the client,
22
+ * and that the client has a defined means to subscribe to those mutations.
23
+ * It is **IMPLIED** (but **NOT REQUIRED**) that the typical reactive client
24
+ * will subscribe to mutations by reading the pertinent properties of the
25
+ * reactive object in a reactive context appropriate for that client.
26
+ * - In common usage, the engine **MAY** convey computed getter property types
27
+ * back to the client, to indicate that certain aspects the engine mutates
28
+ * are read-only to the client (even if they are mutable by the engine).
29
+ *
30
+ * Real world examples of reactive implementations include:
31
+ *
32
+ * - {@link https://vuejs.org/api/reactivity-core.html#reactive | `reactive` (Vue)}
33
+ * - {@link https://docs.solidjs.com/reference/store-utilities/create-mutable | `createMutable` (Solid)}
34
+ * - {@link DefineMutableObject | our internal `mutable` test helper}
35
+ *
36
+ * Each of these implementations are targeted as part of our effort to ensure
37
+ * the `xforms-engine` is agnostic to a client's framework and/or implementation
38
+ * of state.
39
+ *
40
+ * @example
41
+ *
42
+ * ```ts
43
+ * declare const clientFactory: OpaqueReactiveObjectFactory;
44
+ *
45
+ * interface ClientFoo {
46
+ * get bar(): string;
47
+ * }
48
+ *
49
+ * interface MutableFoo {
50
+ * bar: string;
51
+ * }
52
+ *
53
+ * // State internally updated by engine
54
+ * let mutableFoo = clientFactory<MutableFoo>({ bar: 'bat' });
55
+ *
56
+ * // Same state, consumed by the client as computed but read-only
57
+ * let clientFoo: ClientFoo = engineFoo;
58
+ *
59
+ * // Client: subscribe to mutations by the engine
60
+ * reactiveContext(() => {
61
+ * // Implied client subscription to `clientFoo.bar` on property access
62
+ * doSomethingWith(clientFoo.bar);
63
+ * });
64
+ *
65
+ * // Engine: mutate values to convey state changes to subscribed client
66
+ * engineFoo.bar = 'quux';
67
+ * ```
68
+ */
69
+ export type OpaqueReactiveObjectFactory<Input extends object = object> = BaseOpaqueReactiveObjectFactory<Input> & ((object: object) => unknown);
70
+ export {};
@@ -0,0 +1,28 @@
1
+ import type { RepeatInstanceDefinition } from '../model/RepeatInstanceDefinition.ts';
2
+ import type { RepeatTemplateDefinition } from '../model/RepeatTemplateDefinition.ts';
3
+ import type { BaseNode, BaseNodeState } from './BaseNode.ts';
4
+ import type { RepeatRangeNode } from './RepeatRangeNode.ts';
5
+ import type { RootNode } from './RootNode.ts';
6
+ import type { GeneralChildNode } from './hierarchy.ts';
7
+ export interface RepeatInstanceNodeState extends BaseNodeState {
8
+ get hint(): null;
9
+ get children(): readonly GeneralChildNode[];
10
+ get valueOptions(): null;
11
+ get value(): null;
12
+ }
13
+ export type RepeatDefinition = RepeatInstanceDefinition | RepeatTemplateDefinition;
14
+ export interface RepeatInstanceNode extends BaseNode {
15
+ readonly nodeType: 'repeat-instance';
16
+ readonly definition: RepeatDefinition;
17
+ readonly root: RootNode;
18
+ /**
19
+ * A repeat instance may only be a child of a {@link RepeatRangeNode}.
20
+ *
21
+ * Note: the web-forms engine's representation of this structure differs from
22
+ * the underlying XForms specification's primary instance structure.
23
+ *
24
+ * @see {@link RepeatRangeNode} for additional detail.
25
+ */
26
+ readonly parent: RepeatRangeNode;
27
+ readonly currentState: RepeatInstanceNodeState;
28
+ }
@@ -0,0 +1,94 @@
1
+ import type { RepeatSequenceDefinition } from '../model/RepeatSequenceDefinition.ts';
2
+ import type { BaseNode, BaseNodeState } from './BaseNode.ts';
3
+ import type { RepeatInstanceNode } from './RepeatInstanceNode.ts';
4
+ import type { RootNode } from './RootNode.ts';
5
+ import type { TextRange } from './TextRange.ts';
6
+ import type { GeneralParentNode } from './hierarchy.ts';
7
+ export interface RepeatRangeNodeState extends BaseNodeState {
8
+ get hint(): null;
9
+ get label(): TextRange<'label'> | null;
10
+ /**
11
+ * A repeat range's children may only be {@link RepeatInstanceNode}s.
12
+ *
13
+ * Note: the web-forms engine's representation of this structure differs from
14
+ * the underlying XForms specification's primary instance structure.
15
+ *
16
+ * @see {@link RepeatRangeNode} for additional detail.
17
+ */
18
+ get children(): readonly RepeatInstanceNode[];
19
+ get valueOptions(): null;
20
+ get value(): null;
21
+ }
22
+ /**
23
+ * Represents a contiguous set of zero or more {@link RepeatInstanceNode}s
24
+ * (accessed by its
25
+ * {@link RepeatRangeNodeState.children | `currentState.children`}).
26
+ *
27
+ * This structure is modeled as a node, like any other, in the web-forms engine
28
+ * representation, which notably differs from the corresponding structure in the
29
+ * underlying ODK XForms specification's primary instance state.
30
+ *
31
+ * _Conceptually_, it more closely corresponds to the concept of a set of
32
+ * repeats as defined by a `<repeat>` element in the XForms body. Whereas its
33
+ * {@link RepeatInstanceNode} children, if any, correspond directly to the
34
+ * XForms primary instance's state.
35
+ *
36
+ * More precisely, clients should be advised that the presentation aspect of a
37
+ * `RepeatRangeNode` corresponds to a pair of
38
+ * {@link https://getodk.github.io/xforms-spec/#body-elements | `<group>` and `<repeat>` body elements}
39
+ * referencing the same nodeset. (Forms which define a repeat without a
40
+ * corresponding group are semantically equivalent to those with this
41
+ * group/repeat pair. The web-forms engine model simplifies this by producing
42
+ * the same runtime structure for either case.)
43
+ *
44
+ * @example
45
+ *
46
+ * To illustrate the structural divergence from the underlying XForms concepts,
47
+ * consider this (abridged) XForm definition:
48
+ *
49
+ * ```xml
50
+ * <!-- /h:html/h:head/model -->
51
+ * <instance>
52
+ * <data>
53
+ * <rep />
54
+ * </data>
55
+ * </instance>
56
+ * <!-- /h:html/h:body -->
57
+ * <repeat nodeset="/data/rep" />
58
+ * ```
59
+ *
60
+ * The engine's representation maps to that structure roughly like:
61
+ *
62
+ * ```xml
63
+ * <!-- /h:html/h:head/model -->
64
+ * <instance>
65
+ * <data>
66
+ * <!-- RepeatRangeNode(/data/rep).currentState.children: [ -->
67
+ * <!-- RepeatInstanceNode(/data/rep[1]): --> <rep />
68
+ * <!-- RepeatInstanceNode(/data/rep[2]): --> <rep />
69
+ * <!-- ... -->
70
+ * <!-- ] -->
71
+ * </data>
72
+ * </instance>
73
+ * <!-- /h:html/h:body -->
74
+ * <!-- RepeatRangeNode(/data/rep).definition: { ... -->
75
+ * <group ref="/data/rep">
76
+ * <repeat nodeset="/data/rep" />
77
+ * </group>
78
+ * <!-- } -->
79
+ * ```
80
+ *
81
+ * Importantly, if the state of a given repeat range has no instances, no aspect
82
+ * of these repeats will be present in the underlying XForms primary instance
83
+ * state, but the web-forms engine's representations **retains a reference** to
84
+ * its {@link RepeatRangeNode}.
85
+ */
86
+ export interface RepeatRangeNode extends BaseNode {
87
+ readonly nodeType: 'repeat-range';
88
+ readonly definition: RepeatSequenceDefinition;
89
+ readonly root: RootNode;
90
+ readonly parent: GeneralParentNode;
91
+ readonly currentState: RepeatRangeNodeState;
92
+ addInstances(afterIndex?: number, count?: number): RootNode;
93
+ removeInstances(startIndex: number, count?: number): RootNode;
94
+ }
@@ -0,0 +1,31 @@
1
+ import type { RootDefinition } from '../model/RootDefinition.ts';
2
+ import type { BaseNode, BaseNodeState } from './BaseNode.ts';
3
+ import type { ActiveLanguage, FormLanguage, FormLanguages } from './FormLanguage.ts';
4
+ import type { GeneralChildNode } from './hierarchy.ts';
5
+ export interface RootNodeState extends BaseNodeState {
6
+ /**
7
+ * This, along with {@link RootNode.languages} is the most significant break
8
+ in consistency across node types' state and static properties. Exposing it
9
+ across all node types seems like a point of potential confusion, so this
10
+ particular divergence seems like the most reasonable compromise.
11
+ */
12
+ get activeLanguage(): ActiveLanguage;
13
+ get label(): null;
14
+ get hint(): null;
15
+ get children(): readonly GeneralChildNode[];
16
+ get valueOptions(): null;
17
+ get value(): null;
18
+ }
19
+ export interface RootNode extends BaseNode {
20
+ readonly nodeType: 'root';
21
+ readonly definition: RootDefinition;
22
+ readonly root: RootNode;
23
+ readonly parent: null;
24
+ readonly currentState: RootNodeState;
25
+ /**
26
+ * @todo as with {@link RootNodeState.activeLanguage}, this is the most
27
+ * significant break in consistency across node types.
28
+ */
29
+ readonly languages: FormLanguages;
30
+ setLanguage(language: FormLanguage): RootNode;
31
+ }
@@ -0,0 +1,60 @@
1
+ import type { AnySelectDefinition } from '../body/control/select/SelectDefinition.ts';
2
+ import type { ValueNodeDefinition } from '../model/ValueNodeDefinition.ts';
3
+ import type { BaseNode, BaseNodeState } from './BaseNode.ts';
4
+ import type { RootNode } from './RootNode.ts';
5
+ import type { TextRange } from './TextRange.ts';
6
+ import type { GeneralParentNode } from './hierarchy.ts';
7
+ export interface SelectItem {
8
+ get value(): string;
9
+ get label(): TextRange<'label'> | null;
10
+ }
11
+ export interface SelectNodeState extends BaseNodeState {
12
+ get children(): null;
13
+ /**
14
+ * @todo should {@link BaseNodeState} include this??
15
+ */
16
+ get valueOptions(): readonly SelectItem[];
17
+ /**
18
+ * This value is treated as set-like by the engine, where each
19
+ * {@link SelectItem.value | item's `value`} may occur once (at most), and:
20
+ *
21
+ * - Fields defined with an XForms `<select>` may contain multiple items
22
+ * - Fields defined with an XForms `<select1>` will always produce one item
23
+ * (at most)
24
+ *
25
+ * @todo Maybe it makes more sense for the client interface to break these up!
26
+ * Should a `SelectNodeState` have this `value` type, whereas a hypothetical
27
+ * `Select1NodeState` would have `get value(): SelectItem | null`?
28
+ */
29
+ get value(): readonly SelectItem[];
30
+ }
31
+ export interface SelectDefinition extends ValueNodeDefinition {
32
+ readonly bodyElement: AnySelectDefinition;
33
+ }
34
+ export interface SelectNode extends BaseNode {
35
+ readonly nodeType: 'select';
36
+ readonly definition: SelectDefinition;
37
+ readonly root: RootNode;
38
+ readonly parent: GeneralParentNode;
39
+ readonly currentState: SelectNodeState;
40
+ /**
41
+ * For use by a client to update the selection of a select node where:
42
+ *
43
+ * - For fields defined with an XForms `<select>`, calling this method is
44
+ * additive, i.e. it will include the item in its
45
+ * {@link SelectNodeState.value}.
46
+ * - For fields defined with an XForms `<select1>`, calling this method will
47
+ * replace the selection (if any).
48
+ *
49
+ * @todo @see {@link StringNode.setValue} re: write restrictions
50
+ * @todo @see {@link SelectNodeState.value} re: breaking up the types
51
+ */
52
+ select(item: SelectItem): RootNode;
53
+ /**
54
+ * For use by a client to remove an item from the node's
55
+ * {@link SelectNodeState.value}.
56
+ *
57
+ * @todo @see {@link StringNode.setValue} re: write restrictions
58
+ */
59
+ deselect(item: SelectItem): RootNode;
60
+ }
@@ -0,0 +1,41 @@
1
+ import type { InputDefinition } from '../body/control/InputDefinition.ts';
2
+ import type { ValueNodeDefinition } from '../model/ValueNodeDefinition.ts';
3
+ import type { BaseNode, BaseNodeState } from './BaseNode.ts';
4
+ import type { RootNode } from './RootNode.ts';
5
+ import type { GeneralParentNode } from './hierarchy.ts';
6
+ export interface StringNodeState extends BaseNodeState {
7
+ get children(): null;
8
+ get valueOptions(): null;
9
+ /**
10
+ * Reflects the current value of a {@link StringNode}. This value may be
11
+ * populated when a form is loaded, and it may be updated by certain
12
+ * computations defined by the form. It may also be updated by a client, using
13
+ * the {@link StringNode.setValue} method.
14
+ */
15
+ get value(): string;
16
+ }
17
+ export interface StringDefinition extends ValueNodeDefinition {
18
+ readonly bodyElement: InputDefinition | null;
19
+ }
20
+ /**
21
+ * A node which can be assigned a string/text value. A string node **MAY**
22
+ * correspond to form field defined as an XForms `<input>`, which a user-facing
23
+ * client would likely present for a user to fill. It may not correspond to an
24
+ * `<input>`, or necessarily have any presentational implications for a client
25
+ * (for instance if the node is bound to an XForms `calculate` expression).
26
+ */
27
+ export interface StringNode extends BaseNode {
28
+ readonly nodeType: 'string';
29
+ readonly definition: StringDefinition;
30
+ readonly root: RootNode;
31
+ readonly parent: GeneralParentNode;
32
+ readonly currentState: StringNodeState;
33
+ /**
34
+ * For use by a client to update the value of a string node.
35
+ *
36
+ * @todo [how] should we express write restrictions to a client? E.g. what
37
+ * happens when a string node is readonly, and a client attempts to call this
38
+ * method?
39
+ */
40
+ setValue(value: string): RootNode;
41
+ }