@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,229 @@
|
|
|
1
|
+
import { getPropertyKeys } from '@getodk/common/lib/objects/structure.ts';
|
|
2
|
+
import type { Accessor, Signal } from 'solid-js';
|
|
3
|
+
import type { SimpleAtomicState } from '../types.ts';
|
|
4
|
+
import type { SpecifiedPropertyDescriptor } from './createSpecifiedPropertyDescriptor.ts';
|
|
5
|
+
import { createSpecifiedPropertyDescriptor } from './createSpecifiedPropertyDescriptor.ts';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Specifies a state object's property as mutable. Basic usage:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { createSignal } from 'solid-js';
|
|
12
|
+
*
|
|
13
|
+
* const count = createSignal(1);
|
|
14
|
+
*
|
|
15
|
+
* const state = createSpecifiedState({ count });
|
|
16
|
+
* // ^? { count: number }
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* While basic usage involves passing a Solid {@link Signal}, a
|
|
20
|
+
* {@link SimpleAtomicState} type is also supported, allowing a slightly more
|
|
21
|
+
* permissive setter type.
|
|
22
|
+
*
|
|
23
|
+
* @see {@link SimpleAtomicState} and its related types for more detail.
|
|
24
|
+
*
|
|
25
|
+
* A property specified with this type will be used to define a reactively
|
|
26
|
+
* mutable property on the resulting state object where:
|
|
27
|
+
*
|
|
28
|
+
* - Reading the property will read its value from the provided spec's getter
|
|
29
|
+
* function, establishing an internally reactive subscription.
|
|
30
|
+
*
|
|
31
|
+
* - Mutating the property will pass the assigned value to the provided spec's
|
|
32
|
+
* setter function, triggering any downstream computations—whether that
|
|
33
|
+
* computation is associated with the provided spec itself, or reads from the
|
|
34
|
+
* derived property.
|
|
35
|
+
*
|
|
36
|
+
* - All such mutations will be propagated to any derived (client) state object.
|
|
37
|
+
*/
|
|
38
|
+
// prettier-ignore
|
|
39
|
+
export type MutablePropertySpec<T> =
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
41
|
+
| SimpleAtomicState<T>
|
|
42
|
+
| Signal<T>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Specifies a state object's property as reactively computed, and read-only.
|
|
46
|
+
* Basic usage:
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { createMemo, createSignal } from 'solid-js';
|
|
50
|
+
*
|
|
51
|
+
* const [count, setCount] = createSignal(1);
|
|
52
|
+
* const doubleCount = createMemo(() => count() * 2);
|
|
53
|
+
*
|
|
54
|
+
* const state = createSpecifiedState({ doubleCount });
|
|
55
|
+
* // ^? { readonly doubleCount: number }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* Note: ideally, the produced type would better reflect the computed nature of
|
|
59
|
+
* the resulting property. Unfortunately this isn't currently supported by the
|
|
60
|
+
* TypeScript type system (by inferring the derived state type). This tradeoff
|
|
61
|
+
* is reasonable for internal engine use, but should not be used to derive types
|
|
62
|
+
* which will be directly consumed by clients.
|
|
63
|
+
*
|
|
64
|
+
* Any property specified by a thunk (zero-argument function) is supported. A
|
|
65
|
+
* property specified with this type will be used to define a reactively
|
|
66
|
+
* readable property on the resulting state object where:
|
|
67
|
+
*
|
|
68
|
+
* - Reading the property will read its value on each access, establishing an
|
|
69
|
+
* internally reactive subscription (presuming the input property spec is
|
|
70
|
+
* itself reactive).
|
|
71
|
+
*
|
|
72
|
+
* - Mutating the property will throw a {@link TypeError}, just as it would when
|
|
73
|
+
* attempting to mutate a normal `get` accessor.
|
|
74
|
+
*
|
|
75
|
+
* - Any change which would result in a reactive update in the property spec
|
|
76
|
+
* itself will be propagated to any derived (client) state object.
|
|
77
|
+
*/
|
|
78
|
+
export type ComputedPropertySpec<T> = Accessor<T>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @see {@link StaticPropertySpec}
|
|
82
|
+
*/
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any
|
|
84
|
+
type NonStaticValue = Function | Signal<any> | SimpleAtomicState<any>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Specifies a state object's property with a static value, immutable for the
|
|
88
|
+
* lifetime of that state object. Basic usage:
|
|
89
|
+
*
|
|
90
|
+
* ```ts
|
|
91
|
+
* const state = createSpecifiedState({ num: 10 });
|
|
92
|
+
* // ^? { readonly num: number }
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* Properties will be specified as static when they are none of:
|
|
96
|
+
*
|
|
97
|
+
* - {@link MutablePropertySpec}
|
|
98
|
+
* - {@link ComputedPropertySpec}
|
|
99
|
+
* - Any function with a non-zero arity (which are not supported for specifying
|
|
100
|
+
* a state object's properties at all)
|
|
101
|
+
*
|
|
102
|
+
* Any property so specified will produce a property where:
|
|
103
|
+
*
|
|
104
|
+
* - Reading the property will produce the provided static value.
|
|
105
|
+
*
|
|
106
|
+
* - Mutating the property will throw a {@link TypeError}, just as it would when
|
|
107
|
+
* attempting to mutate a normal `get` accessor (or a
|
|
108
|
+
* {@link PropertyDescriptor} with `writable: false`).
|
|
109
|
+
*
|
|
110
|
+
* - The property will reflect the same static value on any derived (client)
|
|
111
|
+
* state object.
|
|
112
|
+
*/
|
|
113
|
+
export type StaticPropertySpec<T> = Exclude<T, NonStaticValue>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Every state property may be specified with any of these types:
|
|
117
|
+
*
|
|
118
|
+
* - {@link MutablePropertySpec}
|
|
119
|
+
* - {@link ComputedPropertySpec}
|
|
120
|
+
* - {@link StaticPropertySpec}
|
|
121
|
+
*/
|
|
122
|
+
// prettier-ignore
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
124
|
+
export type StatePropertySpec<T = any> =
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
|
|
126
|
+
| MutablePropertySpec<T>
|
|
127
|
+
| ComputedPropertySpec<T>
|
|
128
|
+
| StaticPropertySpec<T>;
|
|
129
|
+
|
|
130
|
+
type ParametersOfArity<Arity extends number> = unknown[] & {
|
|
131
|
+
length: Arity;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
type FunctionOfArity<Arity extends number> = (...args: ParametersOfArity<Arity>) => unknown;
|
|
135
|
+
|
|
136
|
+
const isFunctionOfArity = <Arity extends number>(
|
|
137
|
+
arity: Arity,
|
|
138
|
+
value: unknown
|
|
139
|
+
): value is FunctionOfArity<Arity> => {
|
|
140
|
+
return typeof value === 'function' && value.length === arity;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const isMutablePropertySpec = <T>(
|
|
144
|
+
propertySpec: StatePropertySpec<T>
|
|
145
|
+
): propertySpec is MutablePropertySpec<T> => {
|
|
146
|
+
if (!Array.isArray(propertySpec) || propertySpec.length !== 2) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const [read, write] = propertySpec;
|
|
151
|
+
|
|
152
|
+
return isFunctionOfArity(0, read) && isFunctionOfArity(1, write);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const isComputedPropertySpec = <T>(
|
|
156
|
+
propertySpec: StatePropertySpec<T>
|
|
157
|
+
): propertySpec is ComputedPropertySpec<T> => {
|
|
158
|
+
return isFunctionOfArity(0, propertySpec);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const isStaticPropertySpec = <T>(
|
|
162
|
+
propertySpec: StatePropertySpec<T>
|
|
163
|
+
): propertySpec is StaticPropertySpec<T> => {
|
|
164
|
+
return !isMutablePropertySpec(propertySpec) && !isComputedPropertySpec(propertySpec);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export type StateSpec = Record<string, StatePropertySpec>;
|
|
168
|
+
|
|
169
|
+
// prettier-ignore
|
|
170
|
+
type SpecifiedStatePropertyValue<PropertySpec extends StatePropertySpec> =
|
|
171
|
+
PropertySpec extends StatePropertySpec<infer T>
|
|
172
|
+
? T
|
|
173
|
+
: never;
|
|
174
|
+
|
|
175
|
+
// prettier-ignore
|
|
176
|
+
type DerivedMutableKeys<Spec extends StateSpec> = {
|
|
177
|
+
[K in keyof Spec]:
|
|
178
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
179
|
+
Spec[K] extends MutablePropertySpec<any>
|
|
180
|
+
? K
|
|
181
|
+
: never;
|
|
182
|
+
}[keyof Spec];
|
|
183
|
+
|
|
184
|
+
type DerivedMutableState<Spec extends StateSpec> = {
|
|
185
|
+
-readonly [K in DerivedMutableKeys<Spec>]: SpecifiedStatePropertyValue<Spec[K]>;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
type DerivedReadableKeys<Spec extends StateSpec> = Exclude<keyof Spec, DerivedMutableKeys<Spec>>;
|
|
189
|
+
|
|
190
|
+
type DerivedReadableState<Spec extends StateSpec> = {
|
|
191
|
+
readonly [K in DerivedReadableKeys<Spec>]: SpecifiedStatePropertyValue<Spec[K]>;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// prettier-ignore
|
|
195
|
+
export type SpecifiedState<Spec extends StateSpec> =(
|
|
196
|
+
& DerivedMutableState<Spec>
|
|
197
|
+
& DerivedReadableState<Spec>
|
|
198
|
+
) extends infer DerivedState
|
|
199
|
+
? {
|
|
200
|
+
[K in keyof Spec]:
|
|
201
|
+
K extends keyof DerivedState
|
|
202
|
+
? DerivedState[K]
|
|
203
|
+
: never;
|
|
204
|
+
}
|
|
205
|
+
: never;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Produces an (internally) reactive object whose properties may be a mix of:
|
|
209
|
+
*
|
|
210
|
+
* - {@link MutablePropertySpec | mutable} (atomically reactive when mutated by
|
|
211
|
+
* engine internal logic; reactively propagated as read-only to clients)
|
|
212
|
+
* - {@link ComputedPropertySpec | computed} (read-only, reactive to changes in
|
|
213
|
+
* derived state; reactively propagated as read-only to clients)
|
|
214
|
+
* - {@link StaticPropertySpec | static} (read-only, immutable after creation;
|
|
215
|
+
* statically propagated as read-only to clients)
|
|
216
|
+
*/
|
|
217
|
+
export const createSpecifiedState = <Spec extends StateSpec>(spec: Spec): SpecifiedState<Spec> => {
|
|
218
|
+
const keys = getPropertyKeys(spec);
|
|
219
|
+
const descriptors = Object.fromEntries(
|
|
220
|
+
keys.map((key) => {
|
|
221
|
+
const propertySpec = spec[key];
|
|
222
|
+
const descriptor = createSpecifiedPropertyDescriptor(propertySpec);
|
|
223
|
+
|
|
224
|
+
return [key, descriptor];
|
|
225
|
+
})
|
|
226
|
+
) satisfies Record<string, SpecifiedPropertyDescriptor>;
|
|
227
|
+
|
|
228
|
+
return Object.create(null, descriptors) as SpecifiedState<Spec>;
|
|
229
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ShallowMutable } from '@getodk/common/types/helpers.js';
|
|
2
|
+
|
|
3
|
+
const ENGINE_REPRESENTATION = Symbol('ENGINE_REPRESENTATION');
|
|
4
|
+
type ENGINE_REPRESENTATION = typeof ENGINE_REPRESENTATION;
|
|
5
|
+
|
|
6
|
+
const INTERNAL_CLIENT_REPRESENTATION = Symbol('INTERNAL_CLIENT_REPRESENTATION');
|
|
7
|
+
type INTERNAL_CLIENT_REPRESENTATION = typeof INTERNAL_CLIENT_REPRESENTATION;
|
|
8
|
+
|
|
9
|
+
const READONLY_CLIENT_REPRESENTATION = Symbol('READONLY_CLIENT_REPRESENTATION');
|
|
10
|
+
type READONLY_CLIENT_REPRESENTATION = typeof READONLY_CLIENT_REPRESENTATION;
|
|
11
|
+
|
|
12
|
+
// prettier-ignore
|
|
13
|
+
type RepresentationType =
|
|
14
|
+
| ENGINE_REPRESENTATION
|
|
15
|
+
| INTERNAL_CLIENT_REPRESENTATION
|
|
16
|
+
| READONLY_CLIENT_REPRESENTATION;
|
|
17
|
+
|
|
18
|
+
// prettier-ignore
|
|
19
|
+
type TypedRepresentation<Type extends RepresentationType, T> =
|
|
20
|
+
& T
|
|
21
|
+
& { readonly [K in RepresentationType]?: K extends Type ? K : never };
|
|
22
|
+
|
|
23
|
+
// prettier-ignore
|
|
24
|
+
export type EngineRepresentation<T extends object> = TypedRepresentation<
|
|
25
|
+
ENGINE_REPRESENTATION,
|
|
26
|
+
ShallowMutable<T>
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
export const declareEngineRepresentation = <T extends object>(
|
|
30
|
+
stateObject: T
|
|
31
|
+
): EngineRepresentation<T> => {
|
|
32
|
+
return stateObject as EngineRepresentation<T>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// prettier-ignore
|
|
36
|
+
export type InternalClientRepresentation<T extends object> = TypedRepresentation<
|
|
37
|
+
INTERNAL_CLIENT_REPRESENTATION,
|
|
38
|
+
ShallowMutable<T>
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
export const declareInternalClientRepresentation = <T extends object>(
|
|
42
|
+
stateObject: T
|
|
43
|
+
): InternalClientRepresentation<T> => {
|
|
44
|
+
return stateObject as InternalClientRepresentation<T>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// prettier-ignore
|
|
48
|
+
export type ReadonlyClientRepresentation<T> = TypedRepresentation<
|
|
49
|
+
READONLY_CLIENT_REPRESENTATION,
|
|
50
|
+
T
|
|
51
|
+
>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Provides a static type mechanism to reduce the chance of mistakenly assigning
|
|
55
|
+
* one state representation to another (e.g. `engineState = clientState` or
|
|
56
|
+
* `doSomethingWithCurrentState(engineState)`). Each representation is either
|
|
57
|
+
* fully or partially assignable to the other, but this bit of indirection should
|
|
58
|
+
* prevent that (unless one of the types is widened to {@link T}).
|
|
59
|
+
*/
|
|
60
|
+
export const declareReadonlyClientRepresentation = <T extends object>(
|
|
61
|
+
stateObject: Readonly<T>
|
|
62
|
+
): ReadonlyClientRepresentation<T> => {
|
|
63
|
+
return stateObject as ReadonlyClientRepresentation<T>;
|
|
64
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { Owner } from 'solid-js';
|
|
2
|
+
import { runWithOwner as baseRunWithOwner, createRoot, getOwner } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
type ReactiveScopeTask<T> = (scope: ReactiveScope) => T;
|
|
5
|
+
|
|
6
|
+
type RunReactiveScopeTask = <T>(task: ReactiveScopeTask<T>) => T;
|
|
7
|
+
|
|
8
|
+
export interface ReactiveScope {
|
|
9
|
+
readonly owner: Owner;
|
|
10
|
+
readonly dispose: VoidFunction;
|
|
11
|
+
readonly runTask: RunReactiveScopeTask;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface CreateReactiveScopeOptions {
|
|
15
|
+
readonly owner?: Owner | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Default types for these pertinent reactive scope APIs as produced by Solid.
|
|
20
|
+
* These types are correct, but only so restrictive to account for their
|
|
21
|
+
* behavior outside of a reactive root.
|
|
22
|
+
*/
|
|
23
|
+
interface UnknownReactiveScopeAPI {
|
|
24
|
+
/**
|
|
25
|
+
* The type returned by Solid's {@link getOwner}. It will always produce
|
|
26
|
+
* `Owner` when called in a reactive scope (created by {@link createRoot}).
|
|
27
|
+
*/
|
|
28
|
+
readonly owner: Owner | null;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The unmodified type of Solid's {@link baseRunWithOwner | runWithOwner}.
|
|
32
|
+
* It will return {@link T} when {@link owner} is not `null`.
|
|
33
|
+
*/
|
|
34
|
+
readonly runWithOwner: <T>(owner: Owner | null, fn: () => T) => T | undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Refined types for the same reactive APIs as {@link UnknownReactiveScopeAPI},
|
|
39
|
+
* where their less restricted behavior is known when run within a reactive
|
|
40
|
+
* scope (via {@link createRoot}).
|
|
41
|
+
*/
|
|
42
|
+
interface ValidatedReactiveScopeAPI {
|
|
43
|
+
readonly owner: Owner;
|
|
44
|
+
readonly runWithOwner: <T>(owner: Owner, fn: () => T) => T;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @see {@link UnknownReactiveScopeAPI} and {@link ValidatedReactiveScopeAPI}
|
|
49
|
+
*/
|
|
50
|
+
const validateReactiveScopeAPI = (api: UnknownReactiveScopeAPI): ValidatedReactiveScopeAPI => {
|
|
51
|
+
if (api.owner == null) {
|
|
52
|
+
throw new Error('Must be run in a reactive scope');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return api as ValidatedReactiveScopeAPI;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a reactive scope for internal engine use. This currently uses Solid's
|
|
60
|
+
* implementation of reactivity, and makes no attempt to obscure that. As such,
|
|
61
|
+
* all of the terms and types exposed are intentionally direct references to
|
|
62
|
+
* their concepts in Solid.
|
|
63
|
+
*
|
|
64
|
+
* This reactive scope is suitable for isolating reactivity between tests. It is
|
|
65
|
+
* also suitable for scoping reactivity for nodes in engine/client state, as
|
|
66
|
+
* well as creating nested scopes for their descendants.
|
|
67
|
+
*/
|
|
68
|
+
export const createReactiveScope = (options: CreateReactiveScopeOptions = {}): ReactiveScope => {
|
|
69
|
+
return createRoot((baseDispose) => {
|
|
70
|
+
let isDisposed = false;
|
|
71
|
+
|
|
72
|
+
const dispose: VoidFunction = () => {
|
|
73
|
+
if (isDisposed) {
|
|
74
|
+
throw new Error('Cannot dispose reactive scope multiple times');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
baseDispose();
|
|
78
|
+
isDisposed = true;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const { owner, runWithOwner } = validateReactiveScopeAPI({
|
|
82
|
+
owner: getOwner(),
|
|
83
|
+
runWithOwner: baseRunWithOwner,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const runTask = <T>(task: ReactiveScopeTask<T>): T => {
|
|
87
|
+
if (isDisposed) {
|
|
88
|
+
throw new Error('Cannot run reactive task in reactive scope after it has been disposed');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return runWithOwner(owner, () => {
|
|
92
|
+
return task({
|
|
93
|
+
dispose,
|
|
94
|
+
owner,
|
|
95
|
+
runTask,
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
dispose,
|
|
102
|
+
owner,
|
|
103
|
+
runTask,
|
|
104
|
+
};
|
|
105
|
+
}, options.owner ?? getOwner());
|
|
106
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Accessor } from 'solid-js';
|
|
2
|
+
import type { EvaluationContext } from '../../../instance/internal-api/EvaluationContext.ts';
|
|
3
|
+
import { TextRange } from '../../../instance/text/TextRange.ts';
|
|
4
|
+
import type { ValueNodeDefinition } from '../../../model/ValueNodeDefinition.ts';
|
|
5
|
+
import { createTextRange } from './createTextRange.ts';
|
|
6
|
+
|
|
7
|
+
export const createFieldHint = (
|
|
8
|
+
context: EvaluationContext,
|
|
9
|
+
definition: ValueNodeDefinition
|
|
10
|
+
): Accessor<TextRange<'hint'> | null> => {
|
|
11
|
+
const hintDefinition = definition.bodyElement?.hint ?? null;
|
|
12
|
+
|
|
13
|
+
return createTextRange(context, 'hint', hintDefinition, {
|
|
14
|
+
fallbackValue: null,
|
|
15
|
+
});
|
|
16
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Accessor } from 'solid-js';
|
|
2
|
+
import type { EvaluationContext } from '../../../instance/internal-api/EvaluationContext.ts';
|
|
3
|
+
import { TextRange } from '../../../instance/text/TextRange.ts';
|
|
4
|
+
import type { AnyNodeDefinition } from '../../../model/NodeDefinition.ts';
|
|
5
|
+
import { createTextRange } from './createTextRange.ts';
|
|
6
|
+
|
|
7
|
+
export const createNodeLabel = (
|
|
8
|
+
context: EvaluationContext,
|
|
9
|
+
definition: AnyNodeDefinition
|
|
10
|
+
): Accessor<TextRange<'label'> | null> => {
|
|
11
|
+
const labelDefinition = definition.bodyElement?.label ?? null;
|
|
12
|
+
|
|
13
|
+
return createTextRange(context, 'label', labelDefinition, {
|
|
14
|
+
fallbackValue: null,
|
|
15
|
+
});
|
|
16
|
+
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { CollectionValues } from '@getodk/common/types/collections/CollectionValues.ts';
|
|
2
|
+
import { createMemo, type Accessor } from 'solid-js';
|
|
3
|
+
import type {
|
|
4
|
+
TextElementChild,
|
|
5
|
+
TextElementDefinition,
|
|
6
|
+
} from '../../../body/text/TextElementDefinition.ts';
|
|
7
|
+
import type { TextElementReferencePart } from '../../../body/text/TextElementReferencePart.ts';
|
|
8
|
+
import type { TextChunkSource } from '../../../client/TextRange.ts';
|
|
9
|
+
import type { EvaluationContext } from '../../../instance/internal-api/EvaluationContext.ts';
|
|
10
|
+
import { TextChunk } from '../../../instance/text/TextChunk.ts';
|
|
11
|
+
import { TextRange, type TextRole } from '../../../instance/text/TextRange.ts';
|
|
12
|
+
import { createComputedExpression } from '../createComputedExpression.ts';
|
|
13
|
+
|
|
14
|
+
// prettier-ignore
|
|
15
|
+
type TextSources =
|
|
16
|
+
| readonly [TextElementReferencePart]
|
|
17
|
+
| readonly TextElementChild[];
|
|
18
|
+
|
|
19
|
+
type TextSource = CollectionValues<TextSources>;
|
|
20
|
+
|
|
21
|
+
interface TextChunkComputation {
|
|
22
|
+
readonly source: TextChunkSource;
|
|
23
|
+
readonly getText: Accessor<string>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const createComputedTextChunk = (
|
|
27
|
+
context: EvaluationContext,
|
|
28
|
+
textSource: TextSource
|
|
29
|
+
): TextChunkComputation => {
|
|
30
|
+
const { type } = textSource;
|
|
31
|
+
|
|
32
|
+
if (type === 'static') {
|
|
33
|
+
const { stringValue } = textSource;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
source: type,
|
|
37
|
+
getText: () => stringValue,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return context.scope.runTask(() => {
|
|
42
|
+
const source: TextChunkSource = type === 'reference' ? 'itext' : type;
|
|
43
|
+
const getText = createComputedExpression(context, textSource);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
source,
|
|
47
|
+
getText,
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const createTextChunks = (
|
|
53
|
+
context: EvaluationContext,
|
|
54
|
+
textSources: TextSources
|
|
55
|
+
): Accessor<readonly TextChunk[]> => {
|
|
56
|
+
return context.scope.runTask(() => {
|
|
57
|
+
const { root } = context;
|
|
58
|
+
const chunkComputations = textSources.map((textSource) => {
|
|
59
|
+
return createComputedTextChunk(context, textSource);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return createMemo(() => {
|
|
63
|
+
return chunkComputations.map(({ source, getText }) => {
|
|
64
|
+
return new TextChunk(root, source, getText());
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
interface CreateTextRangeOptions<FallbackValue extends string | null> {
|
|
71
|
+
readonly fallbackValue?: FallbackValue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// prettier-ignore
|
|
75
|
+
type ComputedTextRange<
|
|
76
|
+
Role extends TextRole,
|
|
77
|
+
Definition extends TextElementDefinition<Role> | null,
|
|
78
|
+
FallbackValue extends string | null
|
|
79
|
+
> = Accessor<
|
|
80
|
+
Definition extends null
|
|
81
|
+
? FallbackValue extends null
|
|
82
|
+
? TextRange<Role> | null
|
|
83
|
+
: TextRange<Role>
|
|
84
|
+
: TextRange<Role>
|
|
85
|
+
>;
|
|
86
|
+
|
|
87
|
+
// prettier-ignore
|
|
88
|
+
type FallbackTextRange<
|
|
89
|
+
Role extends TextRole,
|
|
90
|
+
FallbackValue extends string | null
|
|
91
|
+
> =
|
|
92
|
+
FallbackValue extends null
|
|
93
|
+
? TextRange<Role> | null
|
|
94
|
+
: TextRange<Role>;
|
|
95
|
+
|
|
96
|
+
const createFallbackTextRange = <Role extends TextRole, FallbackValue extends string | null>(
|
|
97
|
+
context: EvaluationContext,
|
|
98
|
+
role: Role,
|
|
99
|
+
fallbackValue: FallbackValue
|
|
100
|
+
): FallbackTextRange<Role, FallbackValue> => {
|
|
101
|
+
if (fallbackValue == null) {
|
|
102
|
+
return null as FallbackTextRange<Role, FallbackValue>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const staticChunk = new TextChunk(context.root, 'static', fallbackValue);
|
|
106
|
+
|
|
107
|
+
return new TextRange(role, [staticChunk]);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Creates a text range (e.g. label or hint) from the provided definition,
|
|
112
|
+
* reactive to:
|
|
113
|
+
*
|
|
114
|
+
* - The form's current language (e.g. `<label ref="jr:itext('text-id')" />`)
|
|
115
|
+
* - Direct `<output>` references within the label's children
|
|
116
|
+
*
|
|
117
|
+
* @todo This does not yet handle itext translations **with** outputs!
|
|
118
|
+
*/
|
|
119
|
+
export const createTextRange = <
|
|
120
|
+
Role extends TextRole,
|
|
121
|
+
Definition extends TextElementDefinition<Role> | null,
|
|
122
|
+
FallbackValue extends string | null = null,
|
|
123
|
+
>(
|
|
124
|
+
context: EvaluationContext,
|
|
125
|
+
role: Role,
|
|
126
|
+
definition: Definition,
|
|
127
|
+
options?: CreateTextRangeOptions<FallbackValue>
|
|
128
|
+
): ComputedTextRange<Role, Definition, FallbackValue> => {
|
|
129
|
+
return context.scope.runTask(() => {
|
|
130
|
+
if (definition == null) {
|
|
131
|
+
const textRange = createFallbackTextRange(
|
|
132
|
+
context,
|
|
133
|
+
role,
|
|
134
|
+
options?.fallbackValue ?? (null as FallbackValue)
|
|
135
|
+
);
|
|
136
|
+
const getTextRange = () => textRange;
|
|
137
|
+
|
|
138
|
+
return getTextRange as ComputedTextRange<Role, Definition, FallbackValue>;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const { children, referenceExpression } = definition;
|
|
142
|
+
|
|
143
|
+
let getTextChunks: Accessor<readonly TextChunk[]>;
|
|
144
|
+
|
|
145
|
+
if (referenceExpression == null) {
|
|
146
|
+
getTextChunks = createTextChunks(context, children);
|
|
147
|
+
} else {
|
|
148
|
+
getTextChunks = createTextChunks(context, [referenceExpression]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return createMemo(() => {
|
|
152
|
+
return new TextRange(role, getTextChunks());
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Accessor, Setter, Signal } from 'solid-js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A write interface to reactive atomic state. This type is intended to be used
|
|
5
|
+
* as a relaxed version of {@link Setter}, where its callback form isn't
|
|
6
|
+
* required for conforming implementations.
|
|
7
|
+
*/
|
|
8
|
+
export type SimpleAtomicStateSetter<T> = (newValue: T) => T;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A read/write interface to reactive atomic state. This type is intended to be
|
|
12
|
+
* used as a relaxed version of {@link Signal}, with a
|
|
13
|
+
* {@link SimpleAtomicStateSetter | simpler setter type}.
|
|
14
|
+
*/
|
|
15
|
+
// prettier-ignore
|
|
16
|
+
export type SimpleAtomicState<T> = readonly [
|
|
17
|
+
get: Accessor<T>,
|
|
18
|
+
set: SimpleAtomicStateSetter<T>,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export type AtomicStateSetter<T> = Setter<T> | SimpleAtomicStateSetter<T>;
|
|
22
|
+
|
|
23
|
+
// prettier-ignore
|
|
24
|
+
export type AtomicState<T> = readonly [
|
|
25
|
+
get: Accessor<T>,
|
|
26
|
+
set: AtomicStateSetter<T>,
|
|
27
|
+
];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createUniqueId as baseCreateUniqueId } from 'solid-js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* **WARNING:** Uniqueness is not guaranteed across multiple sessions. Do not
|
|
5
|
+
* use IDs produced by this function in persistence scenarios, or where external
|
|
6
|
+
* interfaces expect stronger uniqueness guarantees.
|
|
7
|
+
*/
|
|
8
|
+
export type CreateUniqueId = () => string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Where available, uses the
|
|
12
|
+
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API | Web Crypto API}'s
|
|
13
|
+
* {@link crypto.randomUUID | `randomUUID`}.
|
|
14
|
+
*
|
|
15
|
+
* Notes on supported environments:
|
|
16
|
+
*
|
|
17
|
+
* - Browsers: only available in a
|
|
18
|
+
* {@link https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts | secure context}
|
|
19
|
+
* - Node: >= v19.0.0
|
|
20
|
+
* - Deno: >= 1.11
|
|
21
|
+
* - Bun: fully supported, unknown minimum version
|
|
22
|
+
*
|
|
23
|
+
* Where `randomUUID` is not supported, falls back to an internal implementation
|
|
24
|
+
* with weaker uniqueness constraints.
|
|
25
|
+
*
|
|
26
|
+
* **WARNING:** Uniqueness is not guaranteed across multiple sessions. Do not
|
|
27
|
+
* use IDs produced by this function in persistence scenarios, or where external
|
|
28
|
+
* interfaces expect stronger uniqueness guarantees.
|
|
29
|
+
*/
|
|
30
|
+
export const createUniqueId: CreateUniqueId = (() => {
|
|
31
|
+
const { crypto } = globalThis;
|
|
32
|
+
|
|
33
|
+
return crypto?.randomUUID?.bind(crypto) ?? baseCreateUniqueId;
|
|
34
|
+
})();
|