@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.
- package/README.md +44 -0
- package/dist/.vite/manifest.json +7 -0
- package/dist/XFormDOM.d.ts +31 -0
- package/dist/XFormDataType.d.ts +26 -0
- package/dist/XFormDefinition.d.ts +14 -0
- package/dist/body/BodyDefinition.d.ts +52 -0
- package/dist/body/BodyElementDefinition.d.ts +32 -0
- package/dist/body/RepeatDefinition.d.ts +15 -0
- package/dist/body/UnsupportedBodyElementDefinition.d.ts +10 -0
- package/dist/body/control/ControlDefinition.d.ts +16 -0
- package/dist/body/control/InputDefinition.d.ts +5 -0
- package/dist/body/control/select/ItemDefinition.d.ts +13 -0
- package/dist/body/control/select/ItemsetDefinition.d.ts +16 -0
- package/dist/body/control/select/ItemsetNodesetContext.d.ts +11 -0
- package/dist/body/control/select/ItemsetNodesetExpression.d.ts +5 -0
- package/dist/body/control/select/ItemsetValueExpression.d.ts +6 -0
- package/dist/body/control/select/SelectDefinition.d.ts +23 -0
- package/dist/body/group/BaseGroupDefinition.d.ts +46 -0
- package/dist/body/group/LogicalGroupDefinition.d.ts +6 -0
- package/dist/body/group/PresentationGroupDefinition.d.ts +11 -0
- package/dist/body/group/RepeatGroupDefinition.d.ts +12 -0
- package/dist/body/group/StructuralGroupDefinition.d.ts +6 -0
- package/dist/body/text/HintDefinition.d.ts +11 -0
- package/dist/body/text/LabelDefinition.d.ts +20 -0
- package/dist/body/text/TextElementDefinition.d.ts +32 -0
- package/dist/body/text/TextElementOutputPart.d.ts +12 -0
- package/dist/body/text/TextElementPart.d.ts +12 -0
- package/dist/body/text/TextElementReferencePart.d.ts +6 -0
- package/dist/body/text/TextElementStaticPart.d.ts +6 -0
- package/dist/client/BaseNode.d.ts +138 -0
- package/dist/client/EngineConfig.d.ts +78 -0
- package/dist/client/FormLanguage.d.ts +63 -0
- package/dist/client/GroupNode.d.ts +24 -0
- package/dist/client/OpaqueReactiveObjectFactory.d.ts +70 -0
- package/dist/client/RepeatInstanceNode.d.ts +28 -0
- package/dist/client/RepeatRangeNode.d.ts +94 -0
- package/dist/client/RootNode.d.ts +31 -0
- package/dist/client/SelectNode.d.ts +60 -0
- package/dist/client/StringNode.d.ts +41 -0
- package/dist/client/SubtreeNode.d.ts +52 -0
- package/dist/client/TextRange.d.ts +55 -0
- package/dist/client/hierarchy.d.ts +48 -0
- package/dist/client/index.d.ts +11 -0
- package/dist/client/node-types.d.ts +1 -0
- package/dist/expression/DependencyContext.d.ts +12 -0
- package/dist/expression/DependentExpression.d.ts +43 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +37622 -0
- package/dist/index.js.map +1 -0
- package/dist/instance/Group.d.ts +31 -0
- package/dist/instance/RepeatInstance.d.ts +60 -0
- package/dist/instance/RepeatRange.d.ts +81 -0
- package/dist/instance/Root.d.ts +70 -0
- package/dist/instance/SelectField.d.ts +45 -0
- package/dist/instance/StringField.d.ts +39 -0
- package/dist/instance/Subtree.d.ts +30 -0
- package/dist/instance/abstract/DescendantNode.d.ts +76 -0
- package/dist/instance/abstract/InstanceNode.d.ts +107 -0
- package/dist/instance/children.d.ts +2 -0
- package/dist/instance/hierarchy.d.ts +12 -0
- package/dist/instance/identity.d.ts +7 -0
- package/dist/instance/index.d.ts +8 -0
- package/dist/instance/internal-api/EvaluationContext.d.ts +34 -0
- package/dist/instance/internal-api/InstanceConfig.d.ts +8 -0
- package/dist/instance/internal-api/SubscribableDependency.d.ts +59 -0
- package/dist/instance/internal-api/TranslationContext.d.ts +4 -0
- package/dist/instance/internal-api/ValueContext.d.ts +22 -0
- package/dist/instance/resource.d.ts +10 -0
- package/dist/instance/text/FormattedTextStub.d.ts +1 -0
- package/dist/instance/text/TextChunk.d.ts +11 -0
- package/dist/instance/text/TextRange.d.ts +10 -0
- package/dist/lib/dom/query.d.ts +20 -0
- package/dist/lib/reactivity/createChildrenState.d.ts +36 -0
- package/dist/lib/reactivity/createComputedExpression.d.ts +12 -0
- package/dist/lib/reactivity/createSelectItems.d.ts +16 -0
- package/dist/lib/reactivity/createValueState.d.ts +44 -0
- package/dist/lib/reactivity/materializeCurrentStateChildren.d.ts +18 -0
- package/dist/lib/reactivity/node-state/createClientState.d.ts +9 -0
- package/dist/lib/reactivity/node-state/createCurrentState.d.ts +6 -0
- package/dist/lib/reactivity/node-state/createEngineState.d.ts +5 -0
- package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +22 -0
- package/dist/lib/reactivity/node-state/createSpecifiedPropertyDescriptor.d.ts +6 -0
- package/dist/lib/reactivity/node-state/createSpecifiedState.d.ts +139 -0
- package/dist/lib/reactivity/node-state/representations.d.ts +25 -0
- package/dist/lib/reactivity/scope.d.ts +23 -0
- package/dist/lib/reactivity/text/createFieldHint.d.ts +5 -0
- package/dist/lib/reactivity/text/createNodeLabel.d.ts +5 -0
- package/dist/lib/reactivity/text/createTextRange.d.ts +19 -0
- package/dist/lib/reactivity/types.d.ts +21 -0
- package/dist/lib/unique-id.d.ts +27 -0
- package/dist/lib/xpath/analysis.d.ts +22 -0
- package/dist/model/BindComputation.d.ts +30 -0
- package/dist/model/BindDefinition.d.ts +31 -0
- package/dist/model/BindElement.d.ts +6 -0
- package/dist/model/DescendentNodeDefinition.d.ts +25 -0
- package/dist/model/ModelBindMap.d.ts +15 -0
- package/dist/model/ModelDefinition.d.ts +10 -0
- package/dist/model/NodeDefinition.d.ts +74 -0
- package/dist/model/RepeatInstanceDefinition.d.ts +15 -0
- package/dist/model/RepeatSequenceDefinition.d.ts +19 -0
- package/dist/model/RepeatTemplateDefinition.d.ts +29 -0
- package/dist/model/RootDefinition.d.ts +24 -0
- package/dist/model/SubtreeDefinition.d.ts +14 -0
- package/dist/model/ValueNodeDefinition.d.ts +15 -0
- package/dist/solid.js +37273 -0
- package/dist/solid.js.map +1 -0
- package/package.json +87 -0
- package/src/XFormDOM.ts +224 -0
- package/src/XFormDataType.ts +64 -0
- package/src/XFormDefinition.ts +40 -0
- package/src/body/BodyDefinition.ts +202 -0
- package/src/body/BodyElementDefinition.ts +62 -0
- package/src/body/RepeatDefinition.ts +54 -0
- package/src/body/UnsupportedBodyElementDefinition.ts +17 -0
- package/src/body/control/ControlDefinition.ts +42 -0
- package/src/body/control/InputDefinition.ts +9 -0
- package/src/body/control/select/ItemDefinition.ts +31 -0
- package/src/body/control/select/ItemsetDefinition.ts +36 -0
- package/src/body/control/select/ItemsetNodesetContext.ts +26 -0
- package/src/body/control/select/ItemsetNodesetExpression.ts +8 -0
- package/src/body/control/select/ItemsetValueExpression.ts +11 -0
- package/src/body/control/select/SelectDefinition.ts +74 -0
- package/src/body/group/BaseGroupDefinition.ts +137 -0
- package/src/body/group/LogicalGroupDefinition.ts +11 -0
- package/src/body/group/PresentationGroupDefinition.ts +28 -0
- package/src/body/group/RepeatGroupDefinition.ts +91 -0
- package/src/body/group/StructuralGroupDefinition.ts +11 -0
- package/src/body/text/HintDefinition.ts +26 -0
- package/src/body/text/LabelDefinition.ts +54 -0
- package/src/body/text/TextElementDefinition.ts +97 -0
- package/src/body/text/TextElementOutputPart.ts +27 -0
- package/src/body/text/TextElementPart.ts +31 -0
- package/src/body/text/TextElementReferencePart.ts +21 -0
- package/src/body/text/TextElementStaticPart.ts +26 -0
- package/src/client/BaseNode.ts +180 -0
- package/src/client/EngineConfig.ts +83 -0
- package/src/client/FormLanguage.ts +77 -0
- package/src/client/GroupNode.ts +33 -0
- package/src/client/OpaqueReactiveObjectFactory.ts +100 -0
- package/src/client/README.md +39 -0
- package/src/client/RepeatInstanceNode.ts +41 -0
- package/src/client/RepeatRangeNode.ts +100 -0
- package/src/client/RootNode.ts +36 -0
- package/src/client/SelectNode.ts +69 -0
- package/src/client/StringNode.ts +46 -0
- package/src/client/SubtreeNode.ts +57 -0
- package/src/client/TextRange.ts +63 -0
- package/src/client/hierarchy.ts +63 -0
- package/src/client/index.ts +29 -0
- package/src/client/node-types.ts +10 -0
- package/src/expression/DependencyContext.ts +53 -0
- package/src/expression/DependentExpression.ts +102 -0
- package/src/index.ts +35 -0
- package/src/instance/Group.ts +82 -0
- package/src/instance/RepeatInstance.ts +164 -0
- package/src/instance/RepeatRange.ts +214 -0
- package/src/instance/Root.ts +264 -0
- package/src/instance/SelectField.ts +204 -0
- package/src/instance/StringField.ts +93 -0
- package/src/instance/Subtree.ts +79 -0
- package/src/instance/abstract/DescendantNode.ts +182 -0
- package/src/instance/abstract/InstanceNode.ts +257 -0
- package/src/instance/children.ts +52 -0
- package/src/instance/hierarchy.ts +54 -0
- package/src/instance/identity.ts +11 -0
- package/src/instance/index.ts +37 -0
- package/src/instance/internal-api/EvaluationContext.ts +41 -0
- package/src/instance/internal-api/InstanceConfig.ts +9 -0
- package/src/instance/internal-api/SubscribableDependency.ts +61 -0
- package/src/instance/internal-api/TranslationContext.ts +5 -0
- package/src/instance/internal-api/ValueContext.ts +27 -0
- package/src/instance/resource.ts +75 -0
- package/src/instance/text/FormattedTextStub.ts +8 -0
- package/src/instance/text/TextChunk.ts +20 -0
- package/src/instance/text/TextRange.ts +23 -0
- package/src/lib/dom/query.ts +49 -0
- package/src/lib/reactivity/createChildrenState.ts +60 -0
- package/src/lib/reactivity/createComputedExpression.ts +114 -0
- package/src/lib/reactivity/createSelectItems.ts +163 -0
- package/src/lib/reactivity/createValueState.ts +258 -0
- package/src/lib/reactivity/materializeCurrentStateChildren.ts +121 -0
- package/src/lib/reactivity/node-state/createClientState.ts +51 -0
- package/src/lib/reactivity/node-state/createCurrentState.ts +27 -0
- package/src/lib/reactivity/node-state/createEngineState.ts +18 -0
- package/src/lib/reactivity/node-state/createSharedNodeState.ts +79 -0
- package/src/lib/reactivity/node-state/createSpecifiedPropertyDescriptor.ts +85 -0
- package/src/lib/reactivity/node-state/createSpecifiedState.ts +229 -0
- package/src/lib/reactivity/node-state/representations.ts +64 -0
- package/src/lib/reactivity/scope.ts +106 -0
- package/src/lib/reactivity/text/createFieldHint.ts +16 -0
- package/src/lib/reactivity/text/createNodeLabel.ts +16 -0
- package/src/lib/reactivity/text/createTextRange.ts +155 -0
- package/src/lib/reactivity/types.ts +27 -0
- package/src/lib/unique-id.ts +34 -0
- package/src/lib/xpath/analysis.ts +241 -0
- package/src/model/BindComputation.ts +88 -0
- package/src/model/BindDefinition.ts +104 -0
- package/src/model/BindElement.ts +8 -0
- package/src/model/DescendentNodeDefinition.ts +56 -0
- package/src/model/ModelBindMap.ts +71 -0
- package/src/model/ModelDefinition.ts +19 -0
- package/src/model/NodeDefinition.ts +146 -0
- package/src/model/RepeatInstanceDefinition.ts +39 -0
- package/src/model/RepeatSequenceDefinition.ts +53 -0
- package/src/model/RepeatTemplateDefinition.ts +150 -0
- package/src/model/RootDefinition.ts +121 -0
- package/src/model/SubtreeDefinition.ts +50 -0
- package/src/model/ValueNodeDefinition.ts +39 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { XFormsXPathEvaluator } from '@getodk/xpath';
|
|
2
|
+
import type { Accessor } from 'solid-js';
|
|
3
|
+
import { createMemo } from 'solid-js';
|
|
4
|
+
import type { BaseNode } from '../../client/BaseNode.ts';
|
|
5
|
+
import { createComputedExpression } from '../../lib/reactivity/createComputedExpression.ts';
|
|
6
|
+
import type { ReactiveScope } from '../../lib/reactivity/scope.ts';
|
|
7
|
+
import type { AnyDescendantNodeDefinition } from '../../model/DescendentNodeDefinition.ts';
|
|
8
|
+
import type { AnyNodeDefinition } from '../../model/NodeDefinition.ts';
|
|
9
|
+
import type { RepeatInstanceDefinition } from '../../model/RepeatInstanceDefinition.ts';
|
|
10
|
+
import type { ValueNodeDefinition } from '../../model/ValueNodeDefinition.ts';
|
|
11
|
+
import type { RepeatInstance } from '../RepeatInstance.ts';
|
|
12
|
+
import type { RepeatRange } from '../RepeatRange.ts';
|
|
13
|
+
import type { Root } from '../Root.ts';
|
|
14
|
+
import type { AnyChildNode, GeneralParentNode } from '../hierarchy.ts';
|
|
15
|
+
import type { EvaluationContext } from '../internal-api/EvaluationContext.ts';
|
|
16
|
+
import type { SubscribableDependency } from '../internal-api/SubscribableDependency.ts';
|
|
17
|
+
import type { InstanceNodeStateSpec } from './InstanceNode.ts';
|
|
18
|
+
import { InstanceNode } from './InstanceNode.ts';
|
|
19
|
+
|
|
20
|
+
export interface DescendantNodeSharedStateSpec {
|
|
21
|
+
readonly reference: Accessor<string>;
|
|
22
|
+
readonly readonly: Accessor<boolean>;
|
|
23
|
+
readonly relevant: Accessor<boolean>;
|
|
24
|
+
readonly required: Accessor<boolean>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// prettier-ignore
|
|
28
|
+
export type DescendantNodeStateSpec<Value = never> =
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
30
|
+
& InstanceNodeStateSpec<Value>
|
|
31
|
+
& DescendantNodeSharedStateSpec;
|
|
32
|
+
|
|
33
|
+
// prettier-ignore
|
|
34
|
+
export type DescendantNodeDefinition = Extract<
|
|
35
|
+
AnyNodeDefinition,
|
|
36
|
+
AnyDescendantNodeDefinition
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
// prettier-ignore
|
|
40
|
+
export type DescendantNodeParent<Definition extends DescendantNodeDefinition> =
|
|
41
|
+
Definition extends ValueNodeDefinition
|
|
42
|
+
? GeneralParentNode
|
|
43
|
+
: Definition extends RepeatInstanceDefinition
|
|
44
|
+
? RepeatRange
|
|
45
|
+
: GeneralParentNode;
|
|
46
|
+
|
|
47
|
+
export type AnyDescendantNode = DescendantNode<
|
|
48
|
+
DescendantNodeDefinition,
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
+
DescendantNodeStateSpec<any>,
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
any
|
|
53
|
+
>;
|
|
54
|
+
|
|
55
|
+
export abstract class DescendantNode<
|
|
56
|
+
Definition extends DescendantNodeDefinition,
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
Spec extends DescendantNodeStateSpec<any>,
|
|
59
|
+
Child extends AnyChildNode | null = null,
|
|
60
|
+
>
|
|
61
|
+
extends InstanceNode<Definition, Spec, Child>
|
|
62
|
+
implements BaseNode, EvaluationContext, SubscribableDependency
|
|
63
|
+
{
|
|
64
|
+
readonly root: Root;
|
|
65
|
+
readonly evaluator: XFormsXPathEvaluator;
|
|
66
|
+
readonly contextNode: Element;
|
|
67
|
+
|
|
68
|
+
constructor(
|
|
69
|
+
override readonly parent: DescendantNodeParent<Definition>,
|
|
70
|
+
override readonly definition: Definition
|
|
71
|
+
) {
|
|
72
|
+
super(parent.engineConfig, parent, definition);
|
|
73
|
+
|
|
74
|
+
const { evaluator, root } = parent;
|
|
75
|
+
|
|
76
|
+
this.root = root;
|
|
77
|
+
this.evaluator = evaluator;
|
|
78
|
+
this.contextNode = this.initializeContextNode(parent.contextNode, definition.nodeName);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected computeChildStepReference(parent: DescendantNodeParent<Definition>): string {
|
|
82
|
+
return `${parent.contextReference}/${this.definition.nodeName}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
protected abstract override computeReference(
|
|
86
|
+
parent: DescendantNodeParent<Definition>,
|
|
87
|
+
definition: Definition
|
|
88
|
+
): string;
|
|
89
|
+
|
|
90
|
+
protected buildSharedStateSpec(
|
|
91
|
+
parent: DescendantNodeParent<Definition>,
|
|
92
|
+
definition: Definition
|
|
93
|
+
): DescendantNodeSharedStateSpec {
|
|
94
|
+
return this.scope.runTask(() => {
|
|
95
|
+
const reference = createMemo(() => this.contextReference);
|
|
96
|
+
const { bind } = definition;
|
|
97
|
+
|
|
98
|
+
// TODO: we can likely short-circuit `readonly` computation when a node
|
|
99
|
+
// is non-relevant.
|
|
100
|
+
const selfReadonly = createComputedExpression(this, bind.readonly);
|
|
101
|
+
const readonly = createMemo(() => {
|
|
102
|
+
return parent.isReadonly || selfReadonly();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const selfRelevant = createComputedExpression(this, bind.relevant);
|
|
106
|
+
const relevant = createMemo(() => {
|
|
107
|
+
return parent.isRelevant && selfRelevant();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// TODO: we can likely short-circuit `required` computation when a node
|
|
111
|
+
// is non-relevant.
|
|
112
|
+
const required = createComputedExpression(this, bind.required);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
reference,
|
|
116
|
+
readonly,
|
|
117
|
+
relevant,
|
|
118
|
+
required,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
protected createContextNode(parentContextNode: Element, nodeName: string): Element {
|
|
124
|
+
return parentContextNode.ownerDocument.createElement(nodeName);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Currently expected to be overridden by...
|
|
129
|
+
*
|
|
130
|
+
* - Repeat range: returns its parent's context node, because it doesn't have
|
|
131
|
+
* a node in the primary instance tree.
|
|
132
|
+
*
|
|
133
|
+
* - Repeat instance: returns its created context node, but overrides handles
|
|
134
|
+
* appending behavior separately (for inserting at the end of its parent
|
|
135
|
+
* range, or even at an arbitrary index within the range, after instance
|
|
136
|
+
* creation is has completed).
|
|
137
|
+
*/
|
|
138
|
+
protected initializeContextNode(parentContextNode: Element, nodeName: string): Element {
|
|
139
|
+
const element = this.createContextNode(parentContextNode, nodeName);
|
|
140
|
+
|
|
141
|
+
parentContextNode.append(element);
|
|
142
|
+
|
|
143
|
+
return element;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @package
|
|
148
|
+
*
|
|
149
|
+
* Performs recursive removal, first of the node's descendants, then of the
|
|
150
|
+
* node itself. For all {@link DescendantNode}s, removal involves **at least**
|
|
151
|
+
* disposal of its {@link scope} ({@link ReactiveScope}).
|
|
152
|
+
*
|
|
153
|
+
* It is expected that the outermost node targeted for removal will always be
|
|
154
|
+
* a {@link RepeatInstance}. @see {@link RepeatInstance.remove} for additional
|
|
155
|
+
* details.
|
|
156
|
+
*
|
|
157
|
+
* It is also expected that upon that outermost node's removal, its parent
|
|
158
|
+
* {@link RepeatRange} will perform a reactive update to its children state so
|
|
159
|
+
* that:
|
|
160
|
+
*
|
|
161
|
+
* 1. Any downstream computations affected by the removal are updated.
|
|
162
|
+
* 2. The client invoking removal is also reactively updated (where
|
|
163
|
+
* applicable).
|
|
164
|
+
*
|
|
165
|
+
* @see {@link RepeatInstance.remove} and {@link RepeatRange.removeInstances}
|
|
166
|
+
* for additional details about their respective node-specific removal
|
|
167
|
+
* behaviors and ordering.
|
|
168
|
+
*
|
|
169
|
+
* @todo Possibly retain removed repeat instances in memory. This came up as a
|
|
170
|
+
* behavior of Collect/JavaRosa, and we should investigate the details and
|
|
171
|
+
* ramifications of that, and whether it's the desired behavior.
|
|
172
|
+
*/
|
|
173
|
+
remove(this: AnyChildNode): void {
|
|
174
|
+
this.scope.runTask(() => {
|
|
175
|
+
this.getChildren().forEach((child) => {
|
|
176
|
+
child.remove();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
this.scope.dispose();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import type { XFormsXPathEvaluator } from '@getodk/xpath';
|
|
2
|
+
import type { Accessor, Signal } from 'solid-js';
|
|
3
|
+
import { createSignal } from 'solid-js';
|
|
4
|
+
import type { BaseNode } from '../../client/BaseNode.ts';
|
|
5
|
+
import type { InstanceNodeType } from '../../client/node-types.ts';
|
|
6
|
+
import type { TextRange } from '../../index.ts';
|
|
7
|
+
import type { MaterializedChildren } from '../../lib/reactivity/materializeCurrentStateChildren.ts';
|
|
8
|
+
import type { CurrentState } from '../../lib/reactivity/node-state/createCurrentState.ts';
|
|
9
|
+
import type { EngineState } from '../../lib/reactivity/node-state/createEngineState.ts';
|
|
10
|
+
import type { SharedNodeState } from '../../lib/reactivity/node-state/createSharedNodeState.ts';
|
|
11
|
+
import type { ReactiveScope } from '../../lib/reactivity/scope.ts';
|
|
12
|
+
import { createReactiveScope } from '../../lib/reactivity/scope.ts';
|
|
13
|
+
import type { SimpleAtomicState } from '../../lib/reactivity/types.ts';
|
|
14
|
+
import type { AnyNodeDefinition } from '../../model/NodeDefinition.ts';
|
|
15
|
+
import type { Root } from '../Root.ts';
|
|
16
|
+
import type { AnyChildNode, AnyNode, AnyParentNode } from '../hierarchy.ts';
|
|
17
|
+
import type { NodeID } from '../identity.ts';
|
|
18
|
+
import { declareNodeID } from '../identity.ts';
|
|
19
|
+
import type { EvaluationContext } from '../internal-api/EvaluationContext.ts';
|
|
20
|
+
import type { InstanceConfig } from '../internal-api/InstanceConfig.ts';
|
|
21
|
+
import type { SubscribableDependency } from '../internal-api/SubscribableDependency.ts';
|
|
22
|
+
|
|
23
|
+
export interface InstanceNodeStateSpec<Value = never> {
|
|
24
|
+
readonly reference: Accessor<string> | string;
|
|
25
|
+
readonly readonly: Accessor<boolean> | boolean;
|
|
26
|
+
readonly relevant: Accessor<boolean> | boolean;
|
|
27
|
+
readonly required: Accessor<boolean> | boolean;
|
|
28
|
+
readonly label: Accessor<TextRange<'label'> | null> | null;
|
|
29
|
+
readonly hint: Accessor<TextRange<'hint'> | null> | null;
|
|
30
|
+
readonly children: Accessor<readonly NodeID[]> | null;
|
|
31
|
+
readonly valueOptions: Accessor<null> | Accessor<readonly unknown[]> | null;
|
|
32
|
+
readonly value: Signal<Value> | SimpleAtomicState<Value> | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type AnyInstanceNode = InstanceNode<
|
|
36
|
+
AnyNodeDefinition,
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
InstanceNodeStateSpec<any>,
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
any
|
|
41
|
+
>;
|
|
42
|
+
|
|
43
|
+
interface InitializedStateOptions<T, K extends keyof T> {
|
|
44
|
+
readonly uninitializedFallback: T[K];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* This type has the same effect as {@link MaterializedChildren}, but abstractly
|
|
49
|
+
* handles leaf node types as well.
|
|
50
|
+
*/
|
|
51
|
+
// prettier-ignore
|
|
52
|
+
export type InstanceNodeCurrentState<
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
Spec extends InstanceNodeStateSpec<any>,
|
|
55
|
+
Child
|
|
56
|
+
> =
|
|
57
|
+
& CurrentState<Omit<Spec, 'children'>>
|
|
58
|
+
& {
|
|
59
|
+
readonly children: [Child] extends [AnyChildNode]
|
|
60
|
+
? readonly Child[]
|
|
61
|
+
: null;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export abstract class InstanceNode<
|
|
65
|
+
Definition extends AnyNodeDefinition,
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
Spec extends InstanceNodeStateSpec<any>,
|
|
68
|
+
Child extends AnyChildNode | null = null,
|
|
69
|
+
>
|
|
70
|
+
implements BaseNode, EvaluationContext, SubscribableDependency
|
|
71
|
+
{
|
|
72
|
+
protected readonly isStateInitialized: Accessor<boolean>;
|
|
73
|
+
|
|
74
|
+
protected abstract readonly state: SharedNodeState<Spec>;
|
|
75
|
+
protected abstract readonly engineState: EngineState<Spec>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Provides a generalized mechanism for accessing a reactive state value
|
|
79
|
+
* during a node's construction, while {@link engineState} is still being
|
|
80
|
+
* defined and thus isn't assigned.
|
|
81
|
+
*
|
|
82
|
+
* The fallback value specified in {@link options} will be returned on access
|
|
83
|
+
* until {@link isStateInitialized} returns true. This ensures:
|
|
84
|
+
*
|
|
85
|
+
* - a value of the expected type will be available
|
|
86
|
+
* - any read access will become reactive to the actual state, once it has
|
|
87
|
+
* been initialized and {@link engineState} is assigned
|
|
88
|
+
*
|
|
89
|
+
* @todo This is one among several chicken/egg problems encountered trying to
|
|
90
|
+
* support state initialization in which some aspects of the state derive from
|
|
91
|
+
* other aspects of it. It would be nice to dispense with this entirely. But
|
|
92
|
+
* if it must persist, we should also consider replacing the method with a
|
|
93
|
+
* direct accessor once state initialization completes, so the initialized
|
|
94
|
+
* check is only called until it becomes impertinent.
|
|
95
|
+
*/
|
|
96
|
+
protected getInitializedState<K extends keyof EngineState<Spec>>(
|
|
97
|
+
key: K,
|
|
98
|
+
options: InitializedStateOptions<EngineState<Spec>, K>
|
|
99
|
+
): EngineState<Spec>[K] {
|
|
100
|
+
if (this.isStateInitialized()) {
|
|
101
|
+
return this.engineState[key];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return options.uninitializedFallback;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @package Exposed on every node type to facilitate inheritance, as well as
|
|
109
|
+
* conditional behavior for value nodes.
|
|
110
|
+
*/
|
|
111
|
+
get isReadonly(): boolean {
|
|
112
|
+
return (this as AnyInstanceNode).getInitializedState('readonly', {
|
|
113
|
+
uninitializedFallback: false,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @package Exposed on every node type to facilitate inheritance, as well as
|
|
119
|
+
* conditional behavior for value nodes.
|
|
120
|
+
*/
|
|
121
|
+
get isRelevant(): boolean {
|
|
122
|
+
return (this as AnyInstanceNode).getInitializedState('relevant', {
|
|
123
|
+
uninitializedFallback: true,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// BaseNode: identity
|
|
128
|
+
readonly nodeId: NodeID;
|
|
129
|
+
|
|
130
|
+
// BaseNode: node types and variants (e.g. for narrowing)
|
|
131
|
+
abstract readonly nodeType: InstanceNodeType;
|
|
132
|
+
|
|
133
|
+
abstract readonly currentState: InstanceNodeCurrentState<Spec, Child>;
|
|
134
|
+
|
|
135
|
+
// BaseNode: structural
|
|
136
|
+
abstract readonly root: Root;
|
|
137
|
+
|
|
138
|
+
// EvaluationContext: instance-global/shared
|
|
139
|
+
abstract readonly evaluator: XFormsXPathEvaluator;
|
|
140
|
+
|
|
141
|
+
// EvaluationContext *and* Subscribable: node-specific
|
|
142
|
+
readonly scope: ReactiveScope;
|
|
143
|
+
|
|
144
|
+
// EvaluationContext: node-specific
|
|
145
|
+
get contextReference(): string {
|
|
146
|
+
return this.computeReference(this.parent, this.definition);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
abstract readonly contextNode: Element;
|
|
150
|
+
|
|
151
|
+
constructor(
|
|
152
|
+
readonly engineConfig: InstanceConfig,
|
|
153
|
+
readonly parent: AnyParentNode | null,
|
|
154
|
+
readonly definition: Definition
|
|
155
|
+
) {
|
|
156
|
+
this.scope = createReactiveScope();
|
|
157
|
+
this.engineConfig = engineConfig;
|
|
158
|
+
this.nodeId = declareNodeID(engineConfig.createUniqueId());
|
|
159
|
+
this.definition = definition;
|
|
160
|
+
|
|
161
|
+
const checkStateInitialized = () => this.engineState != null;
|
|
162
|
+
const [isStateInitialized, setStateInitialized] = createSignal(checkStateInitialized());
|
|
163
|
+
|
|
164
|
+
this.isStateInitialized = isStateInitialized;
|
|
165
|
+
|
|
166
|
+
queueMicrotask(() => {
|
|
167
|
+
if (checkStateInitialized()) {
|
|
168
|
+
setStateInitialized(true);
|
|
169
|
+
} else {
|
|
170
|
+
throw new Error('Node state was never initialized');
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @package This presently serves a few internal use cases, where certain
|
|
177
|
+
* behaviors depend on arbitrary traversal from any point in the instance
|
|
178
|
+
* tree, without particular regard for the visited node type. It isn't
|
|
179
|
+
* intended for external traversal or any other means of consuming children by
|
|
180
|
+
* a client. This return type intentionally deviates from one structural
|
|
181
|
+
* expectation, requiring even leaf nodes to return an array (though for those
|
|
182
|
+
* nodes it will always be empty). This affords consistency and efficiency of
|
|
183
|
+
* interface for those internal uses.
|
|
184
|
+
*/
|
|
185
|
+
abstract getChildren(this: AnyInstanceNode): readonly AnyChildNode[];
|
|
186
|
+
|
|
187
|
+
protected abstract computeReference(
|
|
188
|
+
parent: AnyInstanceNode | null,
|
|
189
|
+
definition: Definition
|
|
190
|
+
): string;
|
|
191
|
+
|
|
192
|
+
getNodeByReference(
|
|
193
|
+
this: AnyNode,
|
|
194
|
+
visited: WeakSet<AnyNode>,
|
|
195
|
+
dependencyReference: string
|
|
196
|
+
): SubscribableDependency | null {
|
|
197
|
+
if (visited.has(this)) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
visited.add(this);
|
|
202
|
+
|
|
203
|
+
const { nodeset } = this.definition;
|
|
204
|
+
|
|
205
|
+
if (dependencyReference === nodeset) {
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (
|
|
210
|
+
dependencyReference.startsWith(`${nodeset}/`) ||
|
|
211
|
+
dependencyReference.startsWith(`${nodeset}[`)
|
|
212
|
+
) {
|
|
213
|
+
const children = this.getChildren();
|
|
214
|
+
|
|
215
|
+
if (children == null) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
for (const child of children) {
|
|
220
|
+
const dependency = child.getNodeByReference(visited, dependencyReference);
|
|
221
|
+
|
|
222
|
+
if (dependency != null) {
|
|
223
|
+
return dependency;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return this.parent?.getNodeByReference(visited, dependencyReference) ?? null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// EvaluationContext: node-relative
|
|
232
|
+
getSubscribableDependencyByReference(
|
|
233
|
+
this: AnyNode,
|
|
234
|
+
reference: string
|
|
235
|
+
): SubscribableDependency | null {
|
|
236
|
+
const visited = new WeakSet<SubscribableDependency>();
|
|
237
|
+
|
|
238
|
+
return this.getNodeByReference(visited, reference);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// SubscribableDependency
|
|
242
|
+
/**
|
|
243
|
+
* This is a default implementation suitable for most node types. The rest
|
|
244
|
+
* (currently: `Root`, `RepeatRange`, `RepeatInstance`) should likely extend
|
|
245
|
+
* this behavior, rather than simply overriding it.
|
|
246
|
+
*/
|
|
247
|
+
subscribe(): void {
|
|
248
|
+
const { engineState } = this;
|
|
249
|
+
|
|
250
|
+
if (engineState.relevant) {
|
|
251
|
+
engineState.reference;
|
|
252
|
+
engineState.relevant;
|
|
253
|
+
engineState.children;
|
|
254
|
+
engineState.value;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { UnreachableError } from '@getodk/common/lib/error/UnreachableError.ts';
|
|
2
|
+
import { SelectDefinition } from '../body/control/select/SelectDefinition.ts';
|
|
3
|
+
import type { GroupDefinition } from '../client/GroupNode.ts';
|
|
4
|
+
import type { SubtreeDefinition } from '../client/SubtreeNode.ts';
|
|
5
|
+
import type { SubtreeDefinition as ModelSubtreeDefinition } from '../model/SubtreeDefinition.ts';
|
|
6
|
+
import { Group } from './Group.ts';
|
|
7
|
+
import { RepeatRange } from './RepeatRange.ts';
|
|
8
|
+
import { SelectField, type SelectFieldDefinition } from './SelectField.ts';
|
|
9
|
+
import { StringField } from './StringField.ts';
|
|
10
|
+
import { Subtree } from './Subtree.ts';
|
|
11
|
+
import type { GeneralChildNode, GeneralParentNode } from './hierarchy.ts';
|
|
12
|
+
|
|
13
|
+
const isSubtreeDefinition = (
|
|
14
|
+
definition: ModelSubtreeDefinition
|
|
15
|
+
): definition is SubtreeDefinition => {
|
|
16
|
+
return definition.bodyElement == null;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const buildChildren = (parent: GeneralParentNode): GeneralChildNode[] => {
|
|
20
|
+
const { children } = parent.definition;
|
|
21
|
+
|
|
22
|
+
return children.map((child): GeneralChildNode => {
|
|
23
|
+
switch (child.type) {
|
|
24
|
+
case 'subtree': {
|
|
25
|
+
if (isSubtreeDefinition(child)) {
|
|
26
|
+
return new Subtree(parent, child);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// TODO: it'd be good to be able to do without this type assertion. The
|
|
30
|
+
// only distinction between the types is whether `bodyElement` is
|
|
31
|
+
// `null`, but for some reason that's insufficient to narrow the union.
|
|
32
|
+
return new Group(parent, child as GroupDefinition);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
case 'repeat-sequence': {
|
|
36
|
+
return new RepeatRange(parent, child);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
case 'value-node': {
|
|
40
|
+
if (child.bodyElement instanceof SelectDefinition) {
|
|
41
|
+
return new SelectField(parent, child as SelectFieldDefinition);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return new StringField(parent, child);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
default: {
|
|
48
|
+
throw new UnreachableError(child);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Group } from './Group.ts';
|
|
2
|
+
import type { RepeatInstance } from './RepeatInstance.ts';
|
|
3
|
+
import type { RepeatRange } from './RepeatRange.ts';
|
|
4
|
+
import type { Root } from './Root.ts';
|
|
5
|
+
import type { SelectField } from './SelectField.ts';
|
|
6
|
+
import type { StringField } from './StringField.ts';
|
|
7
|
+
import type { Subtree } from './Subtree.ts';
|
|
8
|
+
|
|
9
|
+
// prettier-ignore
|
|
10
|
+
export type AnyNode =
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
12
|
+
| Root
|
|
13
|
+
| Group
|
|
14
|
+
| Subtree
|
|
15
|
+
| RepeatRange
|
|
16
|
+
| RepeatInstance
|
|
17
|
+
| StringField
|
|
18
|
+
| SelectField;
|
|
19
|
+
|
|
20
|
+
// prettier-ignore
|
|
21
|
+
export type AnyParentNode =
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
23
|
+
| Root
|
|
24
|
+
| Group
|
|
25
|
+
| Subtree
|
|
26
|
+
| RepeatRange
|
|
27
|
+
| RepeatInstance;
|
|
28
|
+
|
|
29
|
+
// prettier-ignore
|
|
30
|
+
export type GeneralParentNode =
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
32
|
+
| Root
|
|
33
|
+
| Group
|
|
34
|
+
| Subtree
|
|
35
|
+
| RepeatInstance;
|
|
36
|
+
|
|
37
|
+
// prettier-ignore
|
|
38
|
+
export type AnyChildNode =
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
40
|
+
| Group
|
|
41
|
+
| Subtree
|
|
42
|
+
| RepeatRange
|
|
43
|
+
| RepeatInstance
|
|
44
|
+
| StringField
|
|
45
|
+
| SelectField;
|
|
46
|
+
|
|
47
|
+
// prettier-ignore
|
|
48
|
+
export type GeneralChildNode =
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
50
|
+
| Group
|
|
51
|
+
| Subtree
|
|
52
|
+
| RepeatRange
|
|
53
|
+
| StringField
|
|
54
|
+
| SelectField;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const NODE_ID_BRAND: unique symbol;
|
|
2
|
+
type NODE_ID_BRAND = typeof NODE_ID_BRAND;
|
|
3
|
+
|
|
4
|
+
export type NodeID = string & { readonly [NODE_ID_BRAND]: true };
|
|
5
|
+
|
|
6
|
+
// Just another added safeguard to ensure we're not mistakenly handling
|
|
7
|
+
// rando `NodeID` strings which aren't explicitly attached to the node
|
|
8
|
+
// types we expect.
|
|
9
|
+
export const declareNodeID = (id: string): NodeID => {
|
|
10
|
+
return id as NodeID;
|
|
11
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { identity } from '@getodk/common/lib/identity.ts';
|
|
2
|
+
import { XFormDefinition } from '../XFormDefinition.ts';
|
|
3
|
+
import type { RootNode } from '../client/RootNode.ts';
|
|
4
|
+
import type {
|
|
5
|
+
InitializeFormOptions as BaseInitializeFormOptions,
|
|
6
|
+
FormResource,
|
|
7
|
+
InitializeForm,
|
|
8
|
+
} from '../client/index.ts';
|
|
9
|
+
import { retrieveSourceXMLResource } from '../instance/resource.ts';
|
|
10
|
+
import { createUniqueId } from '../lib/unique-id.ts';
|
|
11
|
+
import { Root } from './Root.ts';
|
|
12
|
+
import type { InstanceConfig } from './internal-api/InstanceConfig.ts';
|
|
13
|
+
|
|
14
|
+
interface InitializeFormOptions extends BaseInitializeFormOptions {
|
|
15
|
+
readonly config: Partial<InstanceConfig>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const buildInstanceConfig = (options: Partial<InstanceConfig> = {}): InstanceConfig => {
|
|
19
|
+
return {
|
|
20
|
+
createUniqueId: options.createUniqueId ?? createUniqueId,
|
|
21
|
+
fetchResource: options.fetchResource ?? fetch,
|
|
22
|
+
stateFactory: options.stateFactory ?? identity,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const initializeForm = async (
|
|
27
|
+
input: FormResource,
|
|
28
|
+
options: Partial<InitializeFormOptions> = {}
|
|
29
|
+
): Promise<RootNode> => {
|
|
30
|
+
const engineConfig = buildInstanceConfig(options.config);
|
|
31
|
+
const sourceXML = await retrieveSourceXMLResource(input, engineConfig);
|
|
32
|
+
const form = new XFormDefinition(sourceXML);
|
|
33
|
+
|
|
34
|
+
return Root.initialize(form.xformDOM, form.model.root, engineConfig);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
initializeForm satisfies InitializeForm;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { XFormsXPathEvaluator } from '@getodk/xpath';
|
|
2
|
+
import type { DependentExpression } from '../../expression/DependentExpression.ts';
|
|
3
|
+
import type { ReactiveScope } from '../../lib/reactivity/scope.ts';
|
|
4
|
+
import type { SubscribableDependency } from './SubscribableDependency.ts';
|
|
5
|
+
import type { TranslationContext } from './TranslationContext.ts';
|
|
6
|
+
|
|
7
|
+
export interface EvaluationContextRoot extends SubscribableDependency, TranslationContext {}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Provides a common interface to establish context for XPath-based
|
|
11
|
+
* computations, i.e. to evaluate {@link DependentExpression}s where:
|
|
12
|
+
*
|
|
13
|
+
* - the expression may have dynamic dependency **references** (e.g. relative
|
|
14
|
+
* references resolve to repeat instances or their descendants)
|
|
15
|
+
* - the expression may reference dynamic dependency **values** (e.g. an
|
|
16
|
+
* expression referencing the value of another node)
|
|
17
|
+
* - the expression may be dependent on the form's currently active language
|
|
18
|
+
* (e.g. `jr:itext`)
|
|
19
|
+
* - any dynamic case is expected to be internally reactive
|
|
20
|
+
*/
|
|
21
|
+
export interface EvaluationContext {
|
|
22
|
+
readonly scope: ReactiveScope;
|
|
23
|
+
readonly evaluator: XFormsXPathEvaluator;
|
|
24
|
+
readonly root: EvaluationContextRoot;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Produces the current absolute reference to the {@link contextNode}, where
|
|
28
|
+
* the absolute `/` resolves to the active form state's primary instance root.
|
|
29
|
+
*/
|
|
30
|
+
get contextReference(): string;
|
|
31
|
+
|
|
32
|
+
readonly contextNode: Node;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolves a nodeset reference, possibly relative to the
|
|
36
|
+
* {@link EvaluationContext.contextNode}.
|
|
37
|
+
*/
|
|
38
|
+
readonly getSubscribableDependencyByReference: (
|
|
39
|
+
reference: string
|
|
40
|
+
) => SubscribableDependency | null;
|
|
41
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EngineConfig } from '../../client/EngineConfig.ts';
|
|
2
|
+
import type { CreateUniqueId } from '../../lib/unique-id.ts';
|
|
3
|
+
|
|
4
|
+
export interface InstanceConfig extends Required<EngineConfig> {
|
|
5
|
+
/**
|
|
6
|
+
* Uniqueness per form instance session (so e.g. persistence isn't necessary).
|
|
7
|
+
*/
|
|
8
|
+
readonly createUniqueId: CreateUniqueId;
|
|
9
|
+
}
|