@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,100 @@
1
+ import type { DefineMutableObject } from '../../test/helpers/reactive/internal.ts';
2
+
3
+ // This type is intended to satisfy two goals, each corresponding to the order
4
+ // of the call signatures within the type:
5
+ //
6
+ // 1. Ensure that **general** compatible interfaces are assignable.
7
+ // 2. Ensure that **specific** call sites correctly infer their return type.
8
+ //
9
+ // It does **not** currently satisfy another goal it was originally intended to
10
+ // address: ensure that attempts to assign a function fail when the function
11
+ // **does not** accept an object. The remnants of this attempt are intentionally
12
+ // left here in case we want to try simplifying and clarifying these types
13
+ // further, in a future effort.
14
+ //
15
+ // It also does **not** currently make a difference to:
16
+ //
17
+ // - specify `in out` as is currently specified
18
+ // - specify distinct `in` and `in out` type parameters
19
+ //
20
+ // These TypeScript keywords are intended to allow greater control over the
21
+ // variance of type parameters in function argument/return type positions. It
22
+ // was thought that they might aid in relaxing assignability for Vue's `reactive`,
23
+ // but that didn't pan out (within the timebox allotted thus far). This, too, is
24
+ // left as a remnant in case we want to revisit this in the future.
25
+ interface BaseOpaqueReactiveObjectFactory<in out Input extends object = object> {
26
+ <T extends Input>(object: T): T;
27
+ <T extends object>(object: T): T;
28
+ }
29
+
30
+ /**
31
+ * A client-provided reactivity factory. We assume little about the mechanism
32
+ * of reactivity a client provides (and a client may opt to provide no reactive
33
+ * mechanism at all, if it will consume state changes by other means). From an
34
+ * API perspective, the expectation is:
35
+ *
36
+ * - The factory accepts a single argument, which **MUST** be an object.
37
+ * Reactive primitive values are unsupported by this interface.
38
+ * - The factory **MUST** return an object of the same shape (i.e. it must
39
+ * have the same property key/value pairs).
40
+ * - The factory's return object **MUST** be mutable by the web-forms engine.
41
+ * - The web-forms engine **WILL** propagate changes to state as they occur,
42
+ * by mutating properties of the object corresponding to those aspects of
43
+ * state.
44
+ * - The client **MAY** read any property of the factory's returned object.
45
+ * - Because the client's reactivity mechanism (if any) is unknown, it is
46
+ * **ASSUMED** that the engine's mutations are observable by the client,
47
+ * and that the client has a defined means to subscribe to those mutations.
48
+ * It is **IMPLIED** (but **NOT REQUIRED**) that the typical reactive client
49
+ * will subscribe to mutations by reading the pertinent properties of the
50
+ * reactive object in a reactive context appropriate for that client.
51
+ * - In common usage, the engine **MAY** convey computed getter property types
52
+ * back to the client, to indicate that certain aspects the engine mutates
53
+ * are read-only to the client (even if they are mutable by the engine).
54
+ *
55
+ * Real world examples of reactive implementations include:
56
+ *
57
+ * - {@link https://vuejs.org/api/reactivity-core.html#reactive | `reactive` (Vue)}
58
+ * - {@link https://docs.solidjs.com/reference/store-utilities/create-mutable | `createMutable` (Solid)}
59
+ * - {@link DefineMutableObject | our internal `mutable` test helper}
60
+ *
61
+ * Each of these implementations are targeted as part of our effort to ensure
62
+ * the `xforms-engine` is agnostic to a client's framework and/or implementation
63
+ * of state.
64
+ *
65
+ * @example
66
+ *
67
+ * ```ts
68
+ * declare const clientFactory: OpaqueReactiveObjectFactory;
69
+ *
70
+ * interface ClientFoo {
71
+ * get bar(): string;
72
+ * }
73
+ *
74
+ * interface MutableFoo {
75
+ * bar: string;
76
+ * }
77
+ *
78
+ * // State internally updated by engine
79
+ * let mutableFoo = clientFactory<MutableFoo>({ bar: 'bat' });
80
+ *
81
+ * // Same state, consumed by the client as computed but read-only
82
+ * let clientFoo: ClientFoo = engineFoo;
83
+ *
84
+ * // Client: subscribe to mutations by the engine
85
+ * reactiveContext(() => {
86
+ * // Implied client subscription to `clientFoo.bar` on property access
87
+ * doSomethingWith(clientFoo.bar);
88
+ * });
89
+ *
90
+ * // Engine: mutate values to convey state changes to subscribed client
91
+ * engineFoo.bar = 'quux';
92
+ * ```
93
+ */
94
+ // prettier-ignore
95
+ export type OpaqueReactiveObjectFactory<Input extends object = object> =
96
+ // Possibly a TypeScript bug? The order of this intersection matters! Changing
97
+ // the order causes types in `createClientState.ts` to fail inexplicably.
98
+ & BaseOpaqueReactiveObjectFactory<Input>
99
+ & ((object: object) => unknown)
100
+ ;
@@ -0,0 +1,39 @@
1
+ # @getodk/xforms-engine: Client interface
2
+
3
+ The modules in this directory define the explicit interface between:
4
+
5
+ - an ODK Web Forms client (typically, but not necessarily, providing a user interface), henceforth "client"; and,
6
+ - `@getodk/xforms-engine`; henceforth "the engine"
7
+
8
+ The interface is defined as TypeScript type definitions, with a work-in-progress effort to provide browsable documentation of the same.
9
+
10
+ ## Purpose
11
+
12
+ The interface is designed to:
13
+
14
+ - Provide a means for clients to initiate an [ODK XForm](https://getodk.github.io/xforms-spec/)
15
+ - Convey the structure of the form as a tree, _roughly analogous_ in structure to its [primary instance](https://getodk.github.io/xforms-spec/#primary-instance) model
16
+ - Convey definitional aspects of for each of the form's nodes, from which clients may implement particular modes of presentation and user interaction
17
+ - Convey the current state, at any given time, of each node in that tree
18
+ - Provide explicit mechanisms for clients to manipulate pertinent aspects of that state
19
+ - Facilitate propagation of state updates to clients as and where they occur throughout that tree, by means of a client-provided generic state factory[^1]
20
+ - Facilitate loading any of a form's externally referenced resources, by means of a client-provided generic resource accessor[^2]
21
+
22
+ [^1]: This factory **may be** reactive, and for many clients that is the anticipated usage. But no assumptions are made by the engine about reactivity or any other implementation details beyond the factory's type definition.
23
+ [^2]: This accessor **may** access networked resources, and for many clients that is the anticipated usage. But no assumptions are made by the engine about the provenance of any resources it requests beyond the accessor's type definition.
24
+
25
+ ### Non-goals
26
+
27
+ The interface is explicitly **not designed** to proscribe any particular mode of presentation or user interaction. Furthermore, it does not specify or mandate:
28
+
29
+ - Any particular reactive behavior, or implementation of reactivity generally
30
+ - Access to any network or other resource store per se, beyond the means to load and execute the engine itself in a compatible runtime environment
31
+
32
+ ## Notes on interface contract and stability
33
+
34
+ > [!IMPORTANT]
35
+ > The interface in this directory represents the **complete contract** between a client and the engine. The engine _may_ expose an implementation which exceeds this explicit contract; clients are **strongly discouraged** from depending on details not exposed by the interface itself, as they are considered implementation details subject to change at any time.
36
+
37
+ The general design and approach to this interface has gone through several iterations. The types defined _within this directory_ are expected to be fairly stable, apart from additive changes to support coming feature work.
38
+
39
+ Any types referenced _outside this directory_ should be treated as at least moderately unstable; these types are generally concerned with static aspects of a parsed form (typically referenced as a `definition`). We **will** be revisiting and refining these types as early client work proceeds, and are quite likely to introduce more limited client-facing types much like those within this directory to take their place.
@@ -0,0 +1,41 @@
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
+
8
+ export interface RepeatInstanceNodeState extends BaseNodeState {
9
+ // TODO(?): Previous iteration included an `index` getter here. I don't see it
10
+ // accessed by the current (Solid) client, and I don't know that it really has
11
+ // any use to a client that wouldn't be satisfied by accessing the same index
12
+ // while iterating the parent range's children.
13
+
14
+ get hint(): null;
15
+ get children(): readonly GeneralChildNode[];
16
+ get valueOptions(): null;
17
+ get value(): null;
18
+ }
19
+
20
+ // prettier-ignore
21
+ export type RepeatDefinition =
22
+ | RepeatInstanceDefinition
23
+ | RepeatTemplateDefinition;
24
+
25
+ export interface RepeatInstanceNode extends BaseNode {
26
+ readonly nodeType: 'repeat-instance';
27
+ readonly definition: RepeatDefinition;
28
+ readonly root: RootNode;
29
+
30
+ /**
31
+ * A repeat instance may only be a child of a {@link RepeatRangeNode}.
32
+ *
33
+ * Note: the web-forms engine's representation of this structure differs from
34
+ * the underlying XForms specification's primary instance structure.
35
+ *
36
+ * @see {@link RepeatRangeNode} for additional detail.
37
+ */
38
+ readonly parent: RepeatRangeNode;
39
+
40
+ readonly currentState: RepeatInstanceNodeState;
41
+ }
@@ -0,0 +1,100 @@
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
+
8
+ export interface RepeatRangeNodeState extends BaseNodeState {
9
+ get hint(): null;
10
+ get label(): TextRange<'label'> | null;
11
+
12
+ /**
13
+ * A repeat range's children may only be {@link RepeatInstanceNode}s.
14
+ *
15
+ * Note: the web-forms engine's representation of this structure differs from
16
+ * the underlying XForms specification's primary instance structure.
17
+ *
18
+ * @see {@link RepeatRangeNode} for additional detail.
19
+ */
20
+ get children(): readonly RepeatInstanceNode[];
21
+
22
+ get valueOptions(): null;
23
+ get value(): null;
24
+ }
25
+
26
+ /**
27
+ * Represents a contiguous set of zero or more {@link RepeatInstanceNode}s
28
+ * (accessed by its
29
+ * {@link RepeatRangeNodeState.children | `currentState.children`}).
30
+ *
31
+ * This structure is modeled as a node, like any other, in the web-forms engine
32
+ * representation, which notably differs from the corresponding structure in the
33
+ * underlying ODK XForms specification's primary instance state.
34
+ *
35
+ * _Conceptually_, it more closely corresponds to the concept of a set of
36
+ * repeats as defined by a `<repeat>` element in the XForms body. Whereas its
37
+ * {@link RepeatInstanceNode} children, if any, correspond directly to the
38
+ * XForms primary instance's state.
39
+ *
40
+ * More precisely, clients should be advised that the presentation aspect of a
41
+ * `RepeatRangeNode` corresponds to a pair of
42
+ * {@link https://getodk.github.io/xforms-spec/#body-elements | `<group>` and `<repeat>` body elements}
43
+ * referencing the same nodeset. (Forms which define a repeat without a
44
+ * corresponding group are semantically equivalent to those with this
45
+ * group/repeat pair. The web-forms engine model simplifies this by producing
46
+ * the same runtime structure for either case.)
47
+ *
48
+ * @example
49
+ *
50
+ * To illustrate the structural divergence from the underlying XForms concepts,
51
+ * consider this (abridged) XForm definition:
52
+ *
53
+ * ```xml
54
+ * <!-- /h:html/h:head/model -->
55
+ * <instance>
56
+ * <data>
57
+ * <rep />
58
+ * </data>
59
+ * </instance>
60
+ * <!-- /h:html/h:body -->
61
+ * <repeat nodeset="/data/rep" />
62
+ * ```
63
+ *
64
+ * The engine's representation maps to that structure roughly like:
65
+ *
66
+ * ```xml
67
+ * <!-- /h:html/h:head/model -->
68
+ * <instance>
69
+ * <data>
70
+ * <!-- RepeatRangeNode(/data/rep).currentState.children: [ -->
71
+ * <!-- RepeatInstanceNode(/data/rep[1]): --> <rep />
72
+ * <!-- RepeatInstanceNode(/data/rep[2]): --> <rep />
73
+ * <!-- ... -->
74
+ * <!-- ] -->
75
+ * </data>
76
+ * </instance>
77
+ * <!-- /h:html/h:body -->
78
+ * <!-- RepeatRangeNode(/data/rep).definition: { ... -->
79
+ * <group ref="/data/rep">
80
+ * <repeat nodeset="/data/rep" />
81
+ * </group>
82
+ * <!-- } -->
83
+ * ```
84
+ *
85
+ * Importantly, if the state of a given repeat range has no instances, no aspect
86
+ * of these repeats will be present in the underlying XForms primary instance
87
+ * state, but the web-forms engine's representations **retains a reference** to
88
+ * its {@link RepeatRangeNode}.
89
+ */
90
+ export interface RepeatRangeNode extends BaseNode {
91
+ readonly nodeType: 'repeat-range';
92
+ readonly definition: RepeatSequenceDefinition;
93
+ readonly root: RootNode;
94
+ readonly parent: GeneralParentNode;
95
+ readonly currentState: RepeatRangeNodeState;
96
+
97
+ addInstances(afterIndex?: number, count?: number): RootNode;
98
+
99
+ removeInstances(startIndex: number, count?: number): RootNode;
100
+ }
@@ -0,0 +1,36 @@
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
+
6
+ export interface RootNodeState extends BaseNodeState {
7
+ /**
8
+ * This, along with {@link RootNode.languages} is the most significant break
9
+ in consistency across node types' state and static properties. Exposing it
10
+ across all node types seems like a point of potential confusion, so this
11
+ particular divergence seems like the most reasonable compromise.
12
+ */
13
+ get activeLanguage(): ActiveLanguage;
14
+
15
+ get label(): null;
16
+ get hint(): null;
17
+ get children(): readonly GeneralChildNode[];
18
+ get valueOptions(): null;
19
+ get value(): null;
20
+ }
21
+
22
+ export interface RootNode extends BaseNode {
23
+ readonly nodeType: 'root';
24
+ readonly definition: RootDefinition;
25
+ readonly root: RootNode;
26
+ readonly parent: null;
27
+ readonly currentState: RootNodeState;
28
+
29
+ /**
30
+ * @todo as with {@link RootNodeState.activeLanguage}, this is the most
31
+ * significant break in consistency across node types.
32
+ */
33
+ readonly languages: FormLanguages;
34
+
35
+ setLanguage(language: FormLanguage): RootNode;
36
+ }
@@ -0,0 +1,69 @@
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 { StringNode } from './StringNode.ts';
6
+ import type { TextRange } from './TextRange.ts';
7
+ import type { GeneralParentNode } from './hierarchy.ts';
8
+
9
+ export interface SelectItem {
10
+ get value(): string;
11
+ get label(): TextRange<'label'> | null;
12
+ }
13
+
14
+ export interface SelectNodeState extends BaseNodeState {
15
+ get children(): null;
16
+
17
+ /**
18
+ * @todo should {@link BaseNodeState} include this??
19
+ */
20
+ get valueOptions(): readonly SelectItem[];
21
+
22
+ /**
23
+ * This value is treated as set-like by the engine, where each
24
+ * {@link SelectItem.value | item's `value`} may occur once (at most), and:
25
+ *
26
+ * - Fields defined with an XForms `<select>` may contain multiple items
27
+ * - Fields defined with an XForms `<select1>` will always produce one item
28
+ * (at most)
29
+ *
30
+ * @todo Maybe it makes more sense for the client interface to break these up!
31
+ * Should a `SelectNodeState` have this `value` type, whereas a hypothetical
32
+ * `Select1NodeState` would have `get value(): SelectItem | null`?
33
+ */
34
+ get value(): readonly SelectItem[];
35
+ }
36
+
37
+ export interface SelectDefinition extends ValueNodeDefinition {
38
+ readonly bodyElement: AnySelectDefinition;
39
+ }
40
+
41
+ export interface SelectNode extends BaseNode {
42
+ readonly nodeType: 'select';
43
+ readonly definition: SelectDefinition;
44
+ readonly root: RootNode;
45
+ readonly parent: GeneralParentNode;
46
+ readonly currentState: SelectNodeState;
47
+
48
+ /**
49
+ * For use by a client to update the selection of a select node where:
50
+ *
51
+ * - For fields defined with an XForms `<select>`, calling this method is
52
+ * additive, i.e. it will include the item in its
53
+ * {@link SelectNodeState.value}.
54
+ * - For fields defined with an XForms `<select1>`, calling this method will
55
+ * replace the selection (if any).
56
+ *
57
+ * @todo @see {@link StringNode.setValue} re: write restrictions
58
+ * @todo @see {@link SelectNodeState.value} re: breaking up the types
59
+ */
60
+ select(item: SelectItem): RootNode;
61
+
62
+ /**
63
+ * For use by a client to remove an item from the node's
64
+ * {@link SelectNodeState.value}.
65
+ *
66
+ * @todo @see {@link StringNode.setValue} re: write restrictions
67
+ */
68
+ deselect(item: SelectItem): RootNode;
69
+ }
@@ -0,0 +1,46 @@
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
+
7
+ export interface StringNodeState extends BaseNodeState {
8
+ get children(): null;
9
+ get valueOptions(): null;
10
+
11
+ /**
12
+ * Reflects the current value of a {@link StringNode}. This value may be
13
+ * populated when a form is loaded, and it may be updated by certain
14
+ * computations defined by the form. It may also be updated by a client, using
15
+ * the {@link StringNode.setValue} method.
16
+ */
17
+ get value(): string;
18
+ }
19
+
20
+ export interface StringDefinition extends ValueNodeDefinition {
21
+ readonly bodyElement: InputDefinition | null;
22
+ }
23
+
24
+ /**
25
+ * A node which can be assigned a string/text value. A string node **MAY**
26
+ * correspond to form field defined as an XForms `<input>`, which a user-facing
27
+ * client would likely present for a user to fill. It may not correspond to an
28
+ * `<input>`, or necessarily have any presentational implications for a client
29
+ * (for instance if the node is bound to an XForms `calculate` expression).
30
+ */
31
+ export interface StringNode extends BaseNode {
32
+ readonly nodeType: 'string';
33
+ readonly definition: StringDefinition;
34
+ readonly root: RootNode;
35
+ readonly parent: GeneralParentNode;
36
+ readonly currentState: StringNodeState;
37
+
38
+ /**
39
+ * For use by a client to update the value of a string node.
40
+ *
41
+ * @todo [how] should we express write restrictions to a client? E.g. what
42
+ * happens when a string node is readonly, and a client attempts to call this
43
+ * method?
44
+ */
45
+ setValue(value: string): RootNode;
46
+ }
@@ -0,0 +1,57 @@
1
+ import type { SubtreeDefinition as BaseSubtreeDefinition } from '../model/SubtreeDefinition.ts';
2
+ import type { BaseNode, BaseNodeState } from './BaseNode.ts';
3
+ import type { RootNode } from './RootNode.ts';
4
+ import type { GeneralChildNode, GeneralParentNode } from './hierarchy.ts';
5
+
6
+ export interface SubtreeNodeState extends BaseNodeState {
7
+ get label(): null;
8
+ get hint(): null;
9
+ get children(): readonly GeneralChildNode[];
10
+ get valueOptions(): null;
11
+ get value(): null;
12
+ }
13
+
14
+ // TODO: obviously there is a naming inconsistency emerging here.
15
+ export interface SubtreeDefinition extends BaseSubtreeDefinition {
16
+ readonly bodyElement: null;
17
+ }
18
+
19
+ /**
20
+ * A non-root node which has children, but **no** corresponding XForms
21
+ * `<group>`. A subtree node does not have any direct implications for
22
+ * presentation to users, but its descendants may specify presentational details
23
+ * in their own {@link BaseNode.definition | definition}s.
24
+ *
25
+ * @example
26
+ *
27
+ * A common `SubtreeNode` case will be a form's `<meta>` element:
28
+ *
29
+ * ```xml
30
+ * <!-- /h:html/h:head/model -->
31
+ * <instance>
32
+ * <data>
33
+ * <some-group>
34
+ * <some-field />
35
+ * </some-group>
36
+ * <!-- Note that `meta` does not have a corresponding group body element -->
37
+ * <!-- SubtreeNode(/data/meta): { ... -->
38
+ * <meta>
39
+ * <instanceID/>
40
+ * </meta>
41
+ * <!-- } -->
42
+ * </data>
43
+ * </instance>
44
+ * <!-- /h:html/h:body -->
45
+ * <group ref="/data/some-group">
46
+ * <input ref="/data/some-group/some-field" />
47
+ * </group>
48
+ * ```
49
+ */
50
+ // TODO: directly test presentation of non-group subtree children/descendants
51
+ export interface SubtreeNode extends BaseNode {
52
+ readonly nodeType: 'subtree';
53
+ readonly definition: SubtreeDefinition;
54
+ readonly root: RootNode;
55
+ readonly parent: GeneralParentNode;
56
+ readonly currentState: SubtreeNodeState;
57
+ }
@@ -0,0 +1,63 @@
1
+ import type { ActiveLanguage } from './FormLanguage.ts';
2
+ import type { RootNodeState } from './RootNode.ts';
3
+
4
+ export type TextChunkSource = 'itext' | 'output' | 'static';
5
+
6
+ /**
7
+ * @todo This (and everything else to do with {@link TextRange}s is for
8
+ * illustration purposes, as a starting point where any particular detail is of
9
+ * unknown utility. We can iterate on all aspects of text ranges in actual
10
+ * clients and refine from there.
11
+ *
12
+ * @see {@link TextRange}
13
+ */
14
+ export interface TextChunk {
15
+ readonly source: TextChunkSource;
16
+
17
+ /**
18
+ * @see {@link ActiveLanguage} for additional commentary
19
+ */
20
+ get language(): ActiveLanguage;
21
+
22
+ get asString(): string;
23
+ get formatted(): unknown;
24
+ }
25
+
26
+ /**
27
+ * Represents aspects of a form which produce text, which _might_ be:
28
+ *
29
+ * - Computed from multiple sources
30
+ * - Capable of conveying certain formatting (which may be presentational and/or
31
+ * structural)
32
+ *
33
+ * Computed text values may be updated by:
34
+ *
35
+ * - Changing a {@link RootNodeState.activeLanguage | form's active language}
36
+ * - Changes to any state referenced by an
37
+ * {@link https://getodk.github.io/xforms-spec/#body-elements | output},
38
+ * within any text-presenting aspect of a form (e.g. labels and hints, as well
39
+ * as itext translations referenced by those)
40
+ *
41
+ * As a client interface, the intent is to convey that this text may be dynamic
42
+ * (and thus potentially reactive for clients supplying a
43
+ * {@link OpaqueReactiveObjectFactory}), and may produce multiple spans of text
44
+ * (or none at all) depending on the structure and state of the form.
45
+ *
46
+ * @todo This interface should be considered **incomplete and in flux**, and
47
+ * subject to change as we evaluate client needs and engine responsibilities. In
48
+ * particular, we've deferred a notion of an interface for formatting aspects,
49
+ * while leaving open the possibility that it may come in future iterations.
50
+ *
51
+ * {@link role} is intended to convey that individual text ranges may be
52
+ * reasoned about differently by clients depending on their role (for instance,
53
+ * a text range's role may correspond to the "short" or "guidance" `form` of a
54
+ * {@link https://getodk.github.io/xforms-spec/#languages | translation}).
55
+ */
56
+ export interface TextRange<Role extends string | null = null> {
57
+ readonly role: Role;
58
+
59
+ [Symbol.iterator](): Iterable<TextChunk>;
60
+
61
+ get asString(): string;
62
+ get formatted(): unknown;
63
+ }
@@ -0,0 +1,63 @@
1
+ import type { ExpandUnion } from '@getodk/common/types/helpers.d.ts';
2
+ import type { GroupNode } from './GroupNode.ts';
3
+ import type { RepeatInstanceNode } from './RepeatInstanceNode.ts';
4
+ import type { RepeatRangeNode } from './RepeatRangeNode.ts';
5
+ import type { RootNode } from './RootNode.ts';
6
+ import type { SelectNode } from './SelectNode.ts';
7
+ import type { StringNode } from './StringNode.ts';
8
+ import type { SubtreeNode } from './SubtreeNode.ts';
9
+
10
+ // prettier-ignore
11
+ export type AnyLeafNode =
12
+ | SelectNode
13
+ | StringNode;
14
+
15
+ /**
16
+ * Any of the concrete node types which may be a parent of any other node.
17
+ *
18
+ * This is an intermediate type, which shouldn't be referenced by other concrete
19
+ * node types directly. Instead:
20
+ *
21
+ * - Repeat instances should (continue to) specify {@link RepeatRangeNode}.
22
+ * - All other child nodes should specify {@link GeneralParentNode}.
23
+ */
24
+ export type AnyParentNode =
25
+ | RootNode // eslint-disable-line @typescript-eslint/sort-type-constituents
26
+ | SubtreeNode
27
+ | GroupNode
28
+ | RepeatRangeNode
29
+ | RepeatInstanceNode;
30
+
31
+ /**
32
+ * Any of the concrete node types a client may get from the engine's form
33
+ * representation. This union should be updated when any new concrete node type
34
+ * is added (or when any of the current members is changed/removed).
35
+ *
36
+ * @see {@link GeneralParentNode}, which is derived from this type
37
+ * @see {@link GeneralChildNode}, which is derived from this type
38
+ */
39
+ export type AnyNode = ExpandUnion<AnyLeafNode | AnyParentNode>;
40
+
41
+ /**
42
+ * Any of the concrete node types which may be a parent of non-repeat instance
43
+ * child nodes.
44
+ */
45
+ export type GeneralParentNode = Exclude<AnyParentNode, RepeatRangeNode>;
46
+
47
+ /**
48
+ * Any of the concrete node types which may be a child of any other node.
49
+ *
50
+ * This is an intermediate type, which shouldn't be referenced by other concrete
51
+ * node types directly. Instead:
52
+ *
53
+ * - Repeat ranges should (continue to) specify {@link RepeatInstanceNode}.
54
+ * - All other parent nodes should specify {@link GeneralChildNode}.
55
+ */
56
+ // prettier-ignore
57
+ export type AnyChildNode = Exclude<AnyNode, RootNode>;
58
+
59
+ /**
60
+ * Any of the concrete node types which may be a child of non-repeat range
61
+ * parent nodes.
62
+ */
63
+ export type GeneralChildNode = Exclude<AnyChildNode, RepeatInstanceNode>;
@@ -0,0 +1,29 @@
1
+ import type { EngineConfig } from './EngineConfig.ts';
2
+ import type { RootNode } from './RootNode.ts';
3
+
4
+ export type FormResource = Blob | URL | string;
5
+
6
+ export interface InitializeFormOptions {
7
+ readonly config: EngineConfig;
8
+
9
+ // E.g. opening a submitted form instance for edit. TDOO: the `FormResource`
10
+ // name is shared here for both init input and initial state, but the shape of
11
+ // those resources would differ. Probably a more generic name is appropriate.
12
+ readonly initialState?: FormResource;
13
+ }
14
+
15
+ /**
16
+ * Creates an instance of a form to be filled (or edited) by a client.
17
+ */
18
+ // TODO: initialization is represented as asynchronous here, so that any
19
+ // requisite resources can be retrieved before passing control to a client. This
20
+ // is an obvious first step, but we can consider a more complex (if optional)
21
+ // flow, e.g.:
22
+ //
23
+ // - Where it can be determined upfront that the form has no need to perform IO
24
+ // - Where the IO may have already been performed (e.g. offline, other potential
25
+ // caching cases where appropriate)
26
+ export type InitializeForm = (
27
+ input: FormResource,
28
+ options?: InitializeFormOptions
29
+ ) => Promise<RootNode>;
@@ -0,0 +1,10 @@
1
+ // prettier-ignore
2
+ export type InstanceNodeType =
3
+ // eslint-disable-next-line @typescript-eslint/sort-type-constituents
4
+ | 'root'
5
+ | 'repeat-range'
6
+ | 'repeat-instance'
7
+ | 'group'
8
+ | 'subtree'
9
+ | 'select'
10
+ | 'string';