@getodk/xforms-engine 0.16.0 → 0.17.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 (124) hide show
  1. package/dist/client/AttributeNode.d.ts +4 -3
  2. package/dist/client/InputNode.d.ts +8 -4
  3. package/dist/client/MarkdownNode.d.ts +3 -0
  4. package/dist/client/NoteNode.d.ts +6 -2
  5. package/dist/client/form/FormInstanceConfig.d.ts +4 -0
  6. package/dist/client/form/LoadFormResult.d.ts +5 -14
  7. package/dist/client/form/ResetFormInstance.d.ts +13 -0
  8. package/dist/entrypoints/FormResult/BaseFormResult.d.ts +1 -0
  9. package/dist/entrypoints/FormResult/BaseInstantiableFormResult.d.ts +2 -0
  10. package/dist/entrypoints/FormResult/FormFailureResult.d.ts +2 -0
  11. package/dist/entrypoints/createPotentiallyClientOwnedReactiveScope.d.ts +19 -0
  12. package/dist/index.js +22150 -25908
  13. package/dist/index.js.map +1 -1
  14. package/dist/instance/Attribute.d.ts +11 -23
  15. package/dist/instance/Group.d.ts +3 -0
  16. package/dist/instance/InputControl.d.ts +3 -0
  17. package/dist/instance/ModelValue.d.ts +4 -0
  18. package/dist/instance/Note.d.ts +4 -0
  19. package/dist/instance/PrimaryInstance.d.ts +7 -1
  20. package/dist/instance/RangeControl.d.ts +4 -0
  21. package/dist/instance/RankControl.d.ts +5 -1
  22. package/dist/instance/Root.d.ts +3 -0
  23. package/dist/instance/SelectControl.d.ts +5 -1
  24. package/dist/instance/TriggerControl.d.ts +4 -0
  25. package/dist/instance/UploadControl.d.ts +3 -0
  26. package/dist/instance/abstract/DescendantNode.d.ts +5 -4
  27. package/dist/instance/abstract/InstanceNode.d.ts +4 -3
  28. package/dist/instance/hierarchy.d.ts +2 -1
  29. package/dist/instance/internal-api/AttributeContext.d.ts +1 -0
  30. package/dist/instance/internal-api/InstanceConfig.d.ts +2 -1
  31. package/dist/instance/internal-api/InstanceValueContext.d.ts +1 -0
  32. package/dist/instance/markdown/MarkdownNode.d.ts +14 -9
  33. package/dist/instance/repeat/RepeatInstance.d.ts +2 -0
  34. package/dist/integration/xpath/adapter/XFormsXPathNode.d.ts +1 -1
  35. package/dist/integration/xpath/adapter/kind.d.ts +5 -3
  36. package/dist/integration/xpath/adapter/traversal.d.ts +3 -3
  37. package/dist/integration/xpath/static-dom/StaticAttribute.d.ts +1 -0
  38. package/dist/integration/xpath/static-dom/StaticDocument.d.ts +2 -0
  39. package/dist/lib/codecs/{Geopoint/Geopoint.d.ts → geolocation/Geolocation.d.ts} +11 -15
  40. package/dist/lib/codecs/geolocation/Geopoint.d.ts +7 -0
  41. package/dist/lib/codecs/geolocation/Geoshape.d.ts +7 -0
  42. package/dist/lib/codecs/geolocation/Geotrace.d.ts +7 -0
  43. package/dist/lib/codecs/geolocation/createGeolocationValueCodec.d.ts +3 -0
  44. package/dist/lib/codecs/getSharedValueCodec.d.ts +7 -5
  45. package/dist/lib/reactivity/text/createTextRange.d.ts +0 -2
  46. package/dist/parse/XFormDOM.d.ts +7 -1
  47. package/dist/parse/body/appearance/inputAppearanceParser.d.ts +1 -1
  48. package/dist/parse/model/ActionDefinition.d.ts +1 -1
  49. package/dist/parse/model/AttributeDefinition.d.ts +2 -0
  50. package/dist/parse/model/BindPreloadDefinition.d.ts +2 -1
  51. package/dist/parse/model/ModelActionMap.d.ts +3 -2
  52. package/dist/parse/model/ModelDefinition.d.ts +3 -5
  53. package/dist/parse/model/SecondaryInstance/sources/CSVExternalSecondaryInstance.d.ts +0 -17
  54. package/dist/parse/model/SecondaryInstance/sources/external-instance-csv-parser.d.ts +8 -0
  55. package/dist/parse/model/TranslationDefinitionMap.d.ts +4 -0
  56. package/dist/solid.js +21608 -25366
  57. package/dist/solid.js.map +1 -1
  58. package/package.json +2 -2
  59. package/src/client/AttributeNode.ts +4 -3
  60. package/src/client/InputNode.ts +11 -3
  61. package/src/client/MarkdownNode.ts +3 -0
  62. package/src/client/NoteNode.ts +9 -1
  63. package/src/client/form/FormInstanceConfig.ts +6 -0
  64. package/src/client/form/LoadFormResult.ts +5 -17
  65. package/src/client/form/ResetFormInstance.ts +17 -0
  66. package/src/entrypoints/FormInstance.ts +2 -0
  67. package/src/entrypoints/FormResult/BaseFormResult.ts +1 -0
  68. package/src/entrypoints/FormResult/BaseInstantiableFormResult.ts +10 -1
  69. package/src/entrypoints/FormResult/FormFailureResult.ts +3 -0
  70. package/src/entrypoints/createPotentiallyClientOwnedReactiveScope.ts +30 -0
  71. package/src/entrypoints/loadForm.ts +1 -31
  72. package/src/instance/Attribute.ts +38 -54
  73. package/src/instance/Group.ts +12 -4
  74. package/src/instance/InputControl.ts +15 -9
  75. package/src/instance/ModelValue.ts +13 -4
  76. package/src/instance/Note.ts +13 -4
  77. package/src/instance/PrimaryInstance.ts +29 -6
  78. package/src/instance/RangeControl.ts +13 -4
  79. package/src/instance/RankControl.ts +14 -5
  80. package/src/instance/Root.ts +12 -4
  81. package/src/instance/SelectControl.ts +14 -5
  82. package/src/instance/TriggerControl.ts +13 -4
  83. package/src/instance/UploadControl.ts +13 -3
  84. package/src/instance/abstract/DescendantNode.ts +4 -3
  85. package/src/instance/abstract/InstanceNode.ts +5 -3
  86. package/src/instance/attachments/buildAttributes.ts +26 -2
  87. package/src/instance/children/childrenInitOptions.ts +2 -1
  88. package/src/instance/hierarchy.ts +2 -0
  89. package/src/instance/internal-api/AttributeContext.ts +1 -0
  90. package/src/instance/internal-api/InstanceConfig.ts +3 -0
  91. package/src/instance/internal-api/InstanceValueContext.ts +1 -0
  92. package/src/instance/markdown/MarkdownNode.ts +19 -7
  93. package/src/instance/repeat/RepeatInstance.ts +11 -3
  94. package/src/instance/text/markdownFormat.ts +4 -3
  95. package/src/integration/xpath/adapter/XFormsXPathNode.ts +1 -0
  96. package/src/integration/xpath/adapter/engineDOMAdapter.ts +2 -2
  97. package/src/integration/xpath/adapter/kind.ts +6 -1
  98. package/src/integration/xpath/adapter/names.ts +1 -0
  99. package/src/integration/xpath/adapter/traversal.ts +5 -6
  100. package/src/integration/xpath/static-dom/StaticAttribute.ts +1 -0
  101. package/src/integration/xpath/static-dom/StaticDocument.ts +2 -0
  102. package/src/lib/codecs/{Geopoint/Geopoint.ts → geolocation/Geolocation.ts} +43 -24
  103. package/src/lib/codecs/geolocation/Geopoint.ts +15 -0
  104. package/src/lib/codecs/geolocation/Geoshape.ts +36 -0
  105. package/src/lib/codecs/geolocation/Geotrace.ts +36 -0
  106. package/src/lib/codecs/geolocation/createGeolocationValueCodec.ts +18 -0
  107. package/src/lib/codecs/getSharedValueCodec.ts +37 -11
  108. package/src/lib/reactivity/createInstanceValueState.ts +90 -34
  109. package/src/lib/reactivity/text/createTextRange.ts +71 -45
  110. package/src/parse/XFormDOM.ts +22 -2
  111. package/src/parse/model/ActionDefinition.ts +6 -6
  112. package/src/parse/model/AttributeDefinition.ts +7 -0
  113. package/src/parse/model/BindDefinition.ts +1 -1
  114. package/src/parse/model/BindPreloadDefinition.ts +21 -14
  115. package/src/parse/model/ModelActionMap.ts +30 -13
  116. package/src/parse/model/ModelDefinition.ts +5 -10
  117. package/src/parse/model/RootDefinition.ts +2 -1
  118. package/src/parse/model/SecondaryInstance/sources/CSVExternalSecondaryInstance.ts +2 -184
  119. package/src/parse/model/SecondaryInstance/sources/external-instance-csv-parser.ts +185 -0
  120. package/src/parse/model/TranslationDefinitionMap.ts +23 -0
  121. package/dist/lib/codecs/Geopoint/GeopointValueCodec.d.ts +0 -5
  122. package/dist/parse/model/generateItextChunks.d.ts +0 -5
  123. package/src/lib/codecs/Geopoint/GeopointValueCodec.ts +0 -20
  124. package/src/parse/model/generateItextChunks.ts +0 -61
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getodk/xforms-engine",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "XForms engine for ODK Web Forms",
6
6
  "type": "module",
@@ -63,7 +63,7 @@
63
63
  "devDependencies": {
64
64
  "@babel/core": "^7.28.5",
65
65
  "@getodk/tree-sitter-xpath": "0.2.2",
66
- "@getodk/xpath": "0.9.1",
66
+ "@getodk/xpath": "0.10.0",
67
67
  "@playwright/test": "^1.57.0",
68
68
  "@types/papaparse": "^5.5.0",
69
69
  "@vitest/browser": "^3.2.4",
@@ -1,9 +1,10 @@
1
1
  import type { Root } from '../instance/Root.ts';
2
2
  import type { AttributeDefinition } from '../parse/model/AttributeDefinition.ts';
3
+ import type { BaseNode, BaseNodeState } from './BaseNode.ts';
3
4
  import type { OpaqueReactiveObjectFactory } from './OpaqueReactiveObjectFactory.ts';
4
5
  import type { InstanceState } from './serialization/InstanceState.ts';
5
6
 
6
- export interface AttributeNodeState {
7
+ export interface AttributeNodeState extends BaseNodeState {
7
8
  get value(): string;
8
9
  get relevant(): boolean;
9
10
  }
@@ -11,7 +12,7 @@ export interface AttributeNodeState {
11
12
  /**
12
13
  * Base interface for common/shared aspects of attributes.
13
14
  */
14
- export interface AttributeNode {
15
+ export interface AttributeNode extends BaseNode {
15
16
  /**
16
17
  * Specifies the node's general type. This can be useful for narrowing types,
17
18
  * e.g. those of children.
@@ -31,7 +32,7 @@ export interface AttributeNode {
31
32
  */
32
33
  readonly root: Root;
33
34
 
34
- readonly owner: unknown;
35
+ readonly parent: unknown;
35
36
 
36
37
  /**
37
38
  * Each node provides a discrete object representing the stateful aspects of
@@ -49,8 +49,8 @@ interface InputNodeOptionsByValueType {
49
49
  readonly time: null;
50
50
  readonly dateTime: null;
51
51
  readonly geopoint: GeoInputNodeOptions;
52
- readonly geotrace: GeoInputNodeOptions;
53
- readonly geoshape: GeoInputNodeOptions;
52
+ readonly geotrace: null;
53
+ readonly geoshape: null;
54
54
  readonly binary: null;
55
55
  readonly barcode: null;
56
56
  readonly intent: null;
@@ -86,12 +86,16 @@ export type IntInputValue = InputValue<'int'>;
86
86
  export type DecimalInputValue = InputValue<'decimal'>;
87
87
  export type DateInputValue = InputValue<'date'>;
88
88
  export type GeopointInputValue = InputValue<'geopoint'>;
89
+ export type GeoshapeInputValue = InputValue<'geoshape'>;
90
+ export type GeotraceInputValue = InputValue<'geotrace'>;
89
91
 
90
92
  export type StringInputNode = InputNode<'string'>;
91
93
  export type IntInputNode = InputNode<'int'>;
92
94
  export type DecimalInputNode = InputNode<'decimal'>;
93
95
  export type DateInputNode = InputNode<'date'>;
94
96
  export type GeopointInputNode = InputNode<'geopoint'>;
97
+ export type GeoshapeInputNode = InputNode<'geoshape'>;
98
+ export type GeotraceInputNode = InputNode<'geotrace'>;
95
99
 
96
100
  // prettier-ignore
97
101
  type SupportedInputValueType =
@@ -100,7 +104,9 @@ type SupportedInputValueType =
100
104
  | 'int'
101
105
  | 'decimal'
102
106
  | 'date'
103
- | 'geopoint';
107
+ | 'geopoint'
108
+ | 'geoshape'
109
+ | 'geotrace';
104
110
 
105
111
  type TemporaryStringValueType = Exclude<ValueType, SupportedInputValueType>;
106
112
 
@@ -114,4 +120,6 @@ export type AnyInputNode =
114
120
  | DecimalInputNode
115
121
  | DateInputNode
116
122
  | GeopointInputNode
123
+ | GeoshapeInputNode
124
+ | GeotraceInputNode
117
125
  | TemporaryStringValueInputNode;
@@ -16,17 +16,20 @@ export type ElementName =
16
16
  export type MarkdownNode = ChildMarkdownNode | HtmlMarkdownNode | ParentMarkdownNode;
17
17
 
18
18
  export interface ParentMarkdownNode {
19
+ readonly id: string;
19
20
  readonly role: 'parent';
20
21
  readonly elementName: string;
21
22
  readonly children: MarkdownNode[];
22
23
  }
23
24
 
24
25
  export interface ChildMarkdownNode {
26
+ readonly id: string;
25
27
  readonly role: 'child';
26
28
  readonly value: string;
27
29
  }
28
30
 
29
31
  export interface HtmlMarkdownNode {
32
+ readonly id: string;
30
33
  readonly role: 'html';
31
34
  readonly unsafeHtml: string;
32
35
  }
@@ -83,12 +83,16 @@ export type IntNoteValue = NoteValue<'int'>;
83
83
  export type DecimalNoteValue = NoteValue<'decimal'>;
84
84
  export type DateNoteValue = NoteValue<'date'>;
85
85
  export type GeopointNoteValue = NoteValue<'geopoint'>;
86
+ export type GeoshapeNoteValue = NoteValue<'geoshape'>;
87
+ export type GeotraceNoteValue = NoteValue<'geotrace'>;
86
88
 
87
89
  export type StringNoteNode = NoteNode<'string'>;
88
90
  export type IntNoteNode = NoteNode<'int'>;
89
91
  export type DecimalNoteNode = NoteNode<'decimal'>;
90
92
  export type DateNoteNode = NoteNode<'date'>;
91
93
  export type GeopointNoteNode = NoteNode<'geopoint'>;
94
+ export type GeoshapeNoteNode = NoteNode<'geoshape'>;
95
+ export type GeotraceNoteNode = NoteNode<'geotrace'>;
92
96
 
93
97
  // prettier-ignore
94
98
  type SupportedNoteValueType =
@@ -97,7 +101,9 @@ type SupportedNoteValueType =
97
101
  | 'int'
98
102
  | 'decimal'
99
103
  | 'date'
100
- | 'geopoint';
104
+ | 'geopoint'
105
+ | 'geoshape'
106
+ | 'geotrace';
101
107
 
102
108
  type TemporaryStringValueType = Exclude<ValueType, SupportedNoteValueType>;
103
109
 
@@ -111,4 +117,6 @@ export type AnyNoteNode =
111
117
  | DecimalNoteNode
112
118
  | DateNoteNode
113
119
  | GeopointNoteNode
120
+ | GeoshapeNoteNode
121
+ | GeotraceNoteNode
114
122
  | TemporaryStringValueNoteNode;
@@ -16,6 +16,10 @@ export interface PreloadProperties {
16
16
  readonly phoneNumber?: string;
17
17
  }
18
18
 
19
+ export interface GeolocationProvider {
20
+ getLocation(): Promise<string>;
21
+ }
22
+
19
23
  export interface FormInstanceConfig {
20
24
  /**
21
25
  * A client may specify a generic function for constructing stateful objects.
@@ -35,4 +39,6 @@ export interface FormInstanceConfig {
35
39
  readonly instanceAttachments?: InstanceAttachmentsConfig;
36
40
 
37
41
  readonly preloadProperties?: PreloadProperties;
42
+
43
+ readonly geolocationProvider?: GeolocationProvider;
38
44
  }
@@ -3,6 +3,7 @@ import type { AnyFunction } from '@getodk/common/types/helpers.js';
3
3
  import type { LoadFormFailureError } from '../../error/LoadFormFailureError.ts';
4
4
  import type { CreateFormInstance } from './CreateFormInstance.ts';
5
5
  import type { EditFormInstance } from './EditFormInstance.ts';
6
+ import type { ResetFormInstance } from './ResetFormInstance.ts';
6
7
  import type { RestoreFormInstance } from './RestoreFormInstance.ts';
7
8
 
8
9
  // Re-export for client access
@@ -44,6 +45,7 @@ interface BaseLoadFormResult {
44
45
  readonly warnings: LoadFormWarnings | null;
45
46
  readonly error: LoadFormFailureError | null;
46
47
  readonly createInstance: FallibleLoadFormResultMethod<CreateFormInstance>;
48
+ readonly resetInstance: FallibleLoadFormResultMethod<ResetFormInstance>;
47
49
  readonly editInstance: FallibleLoadFormResultMethod<EditFormInstance>;
48
50
  readonly restoreInstance: FallibleLoadFormResultMethod<RestoreFormInstance>;
49
51
  }
@@ -53,6 +55,7 @@ export interface LoadFormSuccessResult extends BaseLoadFormResult {
53
55
  readonly warnings: null;
54
56
  readonly error: null;
55
57
  readonly createInstance: CreateFormInstance;
58
+ readonly resetInstance: ResetFormInstance;
56
59
  readonly editInstance: EditFormInstance;
57
60
  readonly restoreInstance: RestoreFormInstance;
58
61
  }
@@ -62,6 +65,7 @@ export interface LoadFormWarningResult extends BaseLoadFormResult {
62
65
  readonly warnings: LoadFormWarnings;
63
66
  readonly error: null;
64
67
  readonly createInstance: CreateFormInstance;
68
+ readonly resetInstance: ResetFormInstance;
65
69
  readonly editInstance: EditFormInstance;
66
70
  readonly restoreInstance: RestoreFormInstance;
67
71
  }
@@ -70,25 +74,9 @@ export interface LoadFormFailureResult extends BaseLoadFormResult {
70
74
  readonly status: 'failure';
71
75
  readonly warnings: LoadFormWarnings | null;
72
76
  readonly error: LoadFormFailureError;
73
-
74
- /**
75
- * @example A temporary demo integration was built during development of this
76
- * interface.
77
- *
78
- * @see
79
- * {@link https://github.com/getodk/web-forms/pull/345/commits/9ef36355d89dd1450d3a87c3a55506bb9b0fc414}
80
- */
81
77
  readonly createInstance: FailedLoadFormResultMethod<CreateFormInstance>;
82
-
78
+ readonly resetInstance: FailedLoadFormResultMethod<ResetFormInstance>;
83
79
  readonly editInstance: FailedLoadFormResultMethod<EditFormInstance>;
84
-
85
- /**
86
- * @example A temporary demo integration was built during development of this
87
- * interface.
88
- *
89
- * @see
90
- * {@link https://github.com/getodk/web-forms/pull/345/commits/9ef36355d89dd1450d3a87c3a55506bb9b0fc414}
91
- */
92
80
  readonly restoreInstance: FailedLoadFormResultMethod<RestoreFormInstance>;
93
81
  }
94
82
 
@@ -0,0 +1,17 @@
1
+ import type { RootNode } from '../RootNode.ts';
2
+ import type { CreatedFormInstance } from './CreateFormInstance.ts';
3
+ import type { FormInstanceConfig } from './FormInstanceConfig.ts';
4
+ import type { LoadForm } from './LoadForm.ts';
5
+ import type { LoadFormResult } from './LoadFormResult.ts';
6
+
7
+ /**
8
+ * @todo This is fallible! Client-facing interfaces will need to account for
9
+ * this. We've begun addressing fallibility _at the interface level_ in
10
+ * {@link LoadForm} (with {@link LoadFormResult}). We'll eventually have a more
11
+ * general interface pattern for this, and we'll apply it here as well. The baby
12
+ * step approach in {@link LoadFormResult} is impractical here due to
13
+ * engine-internal designs, and revising that is currently out of scope. As
14
+ * such, explicit interface-level documentation of fallibility is deferred here,
15
+ * on {@link RootNode} itself, and into any of its sub-interfaces.
16
+ */
17
+ export type ResetFormInstance = (config?: FormInstanceConfig) => CreatedFormInstance;
@@ -42,6 +42,7 @@ export class FormInstance<Mode extends FormInstanceInitializationMode>
42
42
  clientStateFactory: instanceConfig.stateFactory ?? identity,
43
43
  computeAttachmentName: instanceConfig.instanceAttachments?.fileNameFactory ?? (() => null),
44
44
  preloadProperties: instanceConfig.preloadProperties ?? {},
45
+ geolocationProvider: instanceConfig.geolocationProvider,
45
46
  };
46
47
  const primaryInstanceOptions: PrimaryInstanceOptions<Mode> = {
47
48
  ...options.instanceOptions,
@@ -49,6 +50,7 @@ export class FormInstance<Mode extends FormInstanceInitializationMode>
49
50
  initialState,
50
51
  config,
51
52
  };
53
+
52
54
  const { root } = new PrimaryInstance(primaryInstanceOptions);
53
55
 
54
56
  this.mode = mode;
@@ -29,6 +29,7 @@ export abstract class BaseFormResult<Status extends FormResultStatus> {
29
29
  readonly error: BaseFormResultProperty<Status, 'error'>;
30
30
 
31
31
  abstract readonly createInstance: BaseFormResultProperty<Status, 'createInstance'>;
32
+ abstract readonly resetInstance: BaseFormResultProperty<Status, 'resetInstance'>;
32
33
  abstract readonly editInstance: BaseFormResultProperty<Status, 'editInstance'>;
33
34
  abstract readonly restoreInstance: BaseFormResultProperty<Status, 'restoreInstance'>;
34
35
 
@@ -5,6 +5,7 @@ import type {
5
5
  } from '../../client/form/EditFormInstance.ts';
6
6
  import type { FormInstanceConfig } from '../../client/form/FormInstanceConfig.ts';
7
7
  import type { FormResultStatus } from '../../client/form/LoadFormResult.ts';
8
+ import type { ResetFormInstance } from '../../client/form/ResetFormInstance.ts';
8
9
  import type {
9
10
  RestoreFormInstance,
10
11
  RestoreFormInstanceInput,
@@ -13,7 +14,8 @@ import { ErrorProductionDesignPendingError } from '../../error/ErrorProductionDe
13
14
  import { InitialInstanceState } from '../../instance/input/InitialInstanceState.ts';
14
15
  import type { BasePrimaryInstanceOptions } from '../../instance/PrimaryInstance.ts';
15
16
  import type { FormResource } from '../../instance/resource.ts';
16
- import type { ReactiveScope } from '../../lib/reactivity/scope.ts';
17
+ import { type ReactiveScope } from '../../lib/reactivity/scope.ts';
18
+ import { createPotentiallyClientOwnedReactiveScope } from '../createPotentiallyClientOwnedReactiveScope.ts';
17
19
  import type { InstantiableFormResult } from '../FormInstance.ts';
18
20
  import { FormInstance } from '../FormInstance.ts';
19
21
  import type { BaseFormResultProperty } from './BaseFormResult.ts';
@@ -37,6 +39,7 @@ export abstract class BaseInstantiableFormResult<
37
39
  Status extends InstantiableFormResultStatus,
38
40
  > extends BaseFormResult<Status> {
39
41
  readonly createInstance: CreateFormInstance;
42
+ readonly resetInstance: ResetFormInstance;
40
43
  readonly editInstance: EditFormInstance;
41
44
  readonly restoreInstance: RestoreFormInstance;
42
45
 
@@ -60,6 +63,12 @@ export abstract class BaseInstantiableFormResult<
60
63
  });
61
64
  };
62
65
 
66
+ this.resetInstance = (instanceConfig: FormInstanceConfig = {}) => {
67
+ instanceOptions.scope.dispose();
68
+ instanceOptions.scope = createPotentiallyClientOwnedReactiveScope();
69
+ return this.createInstance(instanceConfig);
70
+ };
71
+
63
72
  this.editInstance = async (
64
73
  input: EditFormInstanceInput,
65
74
  instanceConfig: FormInstanceConfig = {}
@@ -6,6 +6,7 @@ import type {
6
6
  LoadFormFailureResult,
7
7
  LoadFormWarnings,
8
8
  } from '../../client/form/LoadFormResult.ts';
9
+ import type { ResetFormInstance } from '../../client/form/ResetFormInstance.ts';
9
10
  import type { RestoreFormInstance } from '../../client/form/RestoreFormInstance.ts';
10
11
  import { LoadFormFailureError } from '../../error/LoadFormFailureError.ts';
11
12
  import { BaseFormResult } from './BaseFormResult.ts';
@@ -25,6 +26,7 @@ const failedFormResultMethodFactory = <T extends AnyFunction>(
25
26
 
26
27
  export class FormFailureResult extends BaseFormResult<'failure'> implements LoadFormFailureResult {
27
28
  readonly createInstance: FailedLoadFormResultMethod<CreateFormInstance>;
29
+ readonly resetInstance: FailedLoadFormResultMethod<ResetFormInstance>;
28
30
  readonly editInstance: FailedLoadFormResultMethod<EditFormInstance>;
29
31
  readonly restoreInstance: FailedLoadFormResultMethod<RestoreFormInstance>;
30
32
 
@@ -38,6 +40,7 @@ export class FormFailureResult extends BaseFormResult<'failure'> implements Load
38
40
  });
39
41
 
40
42
  this.createInstance = failedFormResultMethodFactory(error);
43
+ this.resetInstance = failedFormResultMethodFactory(error);
41
44
  this.editInstance = failedFormResultMethodFactory(error);
42
45
  this.restoreInstance = failedFormResultMethodFactory(error);
43
46
  }
@@ -0,0 +1,30 @@
1
+ import { getOwner, type Owner } from 'solid-js';
2
+ import { createReactiveScope, type ReactiveScope } from '../lib/reactivity/scope';
3
+
4
+ /**
5
+ * Creates a {@link ReactiveScope | reactive scope} from which all form
6
+ * instances derive, and:
7
+ *
8
+ * - if a client loads a form within a Solid reactive context, the scope will be
9
+ * disposed along with the client's reactive context; OR
10
+ * - if a client loads a form outside a Solid reactive context (typically: if a
11
+ * client does not use Solid reactivity), the scope will disposed if and when
12
+ * the engine drops access to the loaded form
13
+ *
14
+ * **IMPORTANT:** this **MUST** be called synchronously. If it is called in an
15
+ * `async` function, it **MUST** be called before any `await` expression; if it
16
+ * is called in any other flow with mixed synchrony, it must be called before
17
+ * yielding to the event loop. Failing to do this will cause the engine to lose
18
+ * access to a client's Solid reactive context, potentially leaking form
19
+ * reactivity indefinitely.
20
+ */
21
+ export const createPotentiallyClientOwnedReactiveScope = (): ReactiveScope => {
22
+ /**
23
+ * A {@link clientOwner | client owner} is the owner of a client's Solid
24
+ * reactive context, if one exists. If none exists, the {@link ReactiveScope}
25
+ * is fully owned by the engine.
26
+ */
27
+ const clientOwner: Owner | null = getOwner();
28
+
29
+ return createReactiveScope({ owner: clientOwner });
30
+ };
@@ -1,5 +1,3 @@
1
- import type { Owner } from 'solid-js';
2
- import { getOwner } from 'solid-js';
3
1
  import { MISSING_RESOURCE_BEHAVIOR } from '../client/constants.ts';
4
2
  import type { FormResource } from '../client/form/FormResource.ts';
5
3
  import type { LoadForm, LoadFormOptions } from '../client/form/LoadForm.ts';
@@ -7,40 +5,12 @@ import type { LoadFormResult } from '../client/form/LoadFormResult.ts';
7
5
  import { LoadFormFailureError } from '../error/LoadFormFailureError.ts';
8
6
  import { retrieveFormDefinition } from '../instance/resource.ts';
9
7
  import type { ReactiveScope } from '../lib/reactivity/scope.ts';
10
- import { createReactiveScope } from '../lib/reactivity/scope.ts';
11
8
  import { XFormDOM } from '../parse/XFormDOM.ts';
12
9
  import { XFormDefinition } from '../parse/XFormDefinition.ts';
13
10
  import { SecondaryInstancesDefinition } from '../parse/model/SecondaryInstance/SecondaryInstancesDefinition.ts';
14
11
  import { FormFailureResult } from './FormResult/FormFailureResult.ts';
15
12
  import { FormSuccessResult } from './FormResult/FormSuccessResult.ts';
16
-
17
- /**
18
- * Creates a {@link ReactiveScope | reactive scope} from which all form
19
- * instances derive, and:
20
- *
21
- * - if a client loads a form within a Solid reactive context, the scope will be
22
- * disposed along with the client's reactive context; OR
23
- * - if a client loads a form outside a Solid reactive context (typically: if a
24
- * client does not use Solid reactivity), the scope will disposed if and when
25
- * the engine drops access to the loaded form
26
- *
27
- * **IMPORTANT:** this **MUST** be called synchronously. If it is called in an
28
- * `async` function, it **MUST** be called before any `await` expression; if it
29
- * is called in any other flow with mixed synchrony, it must be called before
30
- * yielding to the event loop. Failing to do this will cause the engine to lose
31
- * access to a client's Solid reactive context, potentially leaking form
32
- * reactivity indefinitely.
33
- */
34
- const createPotentiallyClientOwnedReactiveScope = (): ReactiveScope => {
35
- /**
36
- * A {@link clientOwner | client owner} is the owner of a client's Solid
37
- * reactive context, if one exists. If none exists, the {@link ReactiveScope}
38
- * is fully owned by the engine.
39
- */
40
- const clientOwner: Owner | null = getOwner();
41
-
42
- return createReactiveScope({ owner: clientOwner });
43
- };
13
+ import { createPotentiallyClientOwnedReactiveScope } from './createPotentiallyClientOwnedReactiveScope.ts';
44
14
 
45
15
  type GlobalFetch = typeof globalThis.fetch;
46
16
 
@@ -1,9 +1,8 @@
1
1
  import { XPathNodeKindKey } from '@getodk/xpath';
2
2
  import type { Accessor } from 'solid-js';
3
3
  import type { AttributeNode } from '../client/AttributeNode.ts';
4
- import type { ActiveLanguage, InstanceState, NullValidationState } from '../client/index.ts';
4
+ import type { InstanceState, NullValidationState } from '../client/index.ts';
5
5
  import type { XFormsXPathAttribute } from '../integration/xpath/adapter/XFormsXPathNode.ts';
6
- import type { EngineXPathEvaluator } from '../integration/xpath/EngineXPathEvaluator.ts';
7
6
  import type { StaticAttribute } from '../integration/xpath/static-dom/StaticAttribute.ts';
8
7
  import { createAttributeNodeInstanceState } from '../lib/client-reactivity/instance-state/createAttributeNodeInstanceState.ts';
9
8
  import {
@@ -12,6 +11,10 @@ import {
12
11
  type RuntimeValue,
13
12
  } from '../lib/codecs/getSharedValueCodec.ts';
14
13
  import type { RuntimeValueSetter, RuntimeValueState } from '../lib/codecs/ValueCodec.ts';
14
+ import {
15
+ createAttributeState,
16
+ type AttributeState,
17
+ } from '../lib/reactivity/createAttributeState.ts';
15
18
  import { createInstanceValueState } from '../lib/reactivity/createInstanceValueState.ts';
16
19
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
17
20
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
@@ -19,31 +22,32 @@ import {
19
22
  createSharedNodeState,
20
23
  type SharedNodeState,
21
24
  } from '../lib/reactivity/node-state/createSharedNodeState.ts';
22
- import type { ReactiveScope } from '../lib/reactivity/scope.ts';
23
25
  import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
24
26
  import type { AttributeDefinition } from '../parse/model/AttributeDefinition.ts';
25
- import type { AnyChildNode, AnyNode } from './hierarchy.ts';
27
+ import { DescendantNode, type DescendantNodeStateSpec } from './abstract/DescendantNode.ts';
28
+ import type { AnyNode } from './hierarchy.ts';
26
29
  import type { AttributeContext } from './internal-api/AttributeContext.ts';
27
- import type { InstanceConfig } from './internal-api/InstanceConfig.ts';
28
30
  import type { DecodeInstanceValue } from './internal-api/InstanceValueContext.ts';
29
31
  import type { ClientReactiveSerializableAttributeNode } from './internal-api/serialization/ClientReactiveSerializableAttributeNode.ts';
30
- import type { PrimaryInstance } from './PrimaryInstance.ts';
31
32
  import type { Root } from './Root.ts';
32
33
 
33
- export interface AttributeStateSpec {
34
+ export interface AttributeStateSpec extends DescendantNodeStateSpec<string> {
35
+ readonly children: null;
36
+ readonly attributes: Accessor<Attribute[]>;
34
37
  readonly value: SimpleAtomicState<string>;
35
38
  readonly instanceValue: Accessor<string>;
36
39
  readonly relevant: Accessor<boolean>;
37
40
  }
38
41
 
39
42
  export class Attribute
43
+ extends DescendantNode<AttributeDefinition, AttributeStateSpec, AnyNode, null>
40
44
  implements
41
45
  AttributeNode,
42
46
  ClientReactiveSerializableAttributeNode,
43
47
  AttributeContext,
44
48
  XFormsXPathAttribute
45
49
  {
46
- readonly [XPathNodeKindKey] = 'attribute';
50
+ override readonly [XPathNodeKindKey] = 'attribute';
47
51
 
48
52
  protected readonly state: SharedNodeState<AttributeStateSpec>;
49
53
  protected readonly engineState: EngineState<AttributeStateSpec>;
@@ -62,60 +66,30 @@ export class Attribute
62
66
  protected readonly getInstanceValue: Accessor<string>;
63
67
  protected readonly valueState: RuntimeValueState<RuntimeValue<'string'>>;
64
68
  protected readonly setValueState: RuntimeValueSetter<RuntimeInputValue<'string'>>;
65
- readonly evaluator: EngineXPathEvaluator;
66
- readonly getActiveLanguage: Accessor<ActiveLanguage>;
67
- readonly contextNode: AnyNode;
68
- readonly scope: ReactiveScope;
69
- readonly rootDocument: PrimaryInstance;
70
- readonly instanceConfig: InstanceConfig;
71
-
72
- readonly root: Root;
69
+ readonly attributeState: AttributeState;
73
70
 
74
- readonly isRelevant: Accessor<boolean> = () => {
75
- return this.owner.isRelevant();
76
- };
77
-
78
- readonly isAttached: Accessor<boolean> = () => {
71
+ override readonly isAttached: Accessor<boolean> = () => {
79
72
  return this.owner.isAttached();
80
73
  };
81
74
 
82
- readonly isReadonly: Accessor<boolean> = () => {
83
- return true;
84
- };
85
-
86
- readonly hasReadonlyAncestor: Accessor<boolean> = () => {
87
- const { owner } = this;
88
- return owner.hasReadonlyAncestor() || owner.isReadonly();
89
- };
90
-
91
- readonly hasNonRelevantAncestor: Accessor<boolean> = () => {
92
- const { owner } = this;
93
- return owner.hasNonRelevantAncestor() || !owner.isRelevant();
94
- };
95
-
96
- readonly contextReference = (): string => {
97
- return this.owner.contextReference() + '/@' + this.definition.qualifiedName.getPrefixedName();
98
- };
75
+ override readonly getXPathValue: () => string;
99
76
 
100
77
  constructor(
101
78
  readonly owner: AnyNode,
102
- readonly definition: AttributeDefinition,
103
- readonly instanceNode: StaticAttribute
79
+ definition: AttributeDefinition,
80
+ override readonly instanceNode: StaticAttribute
104
81
  ) {
105
- const codec = getSharedValueCodec('string');
82
+ const computeReference = () => {
83
+ return `${this.owner.contextReference()}/@${this.definition.qualifiedName.getPrefixedName()}`;
84
+ };
106
85
 
107
- this.contextNode = owner;
108
- this.scope = owner.scope;
109
- this.rootDocument = owner.rootDocument;
86
+ super(owner, instanceNode, definition, { computeReference });
110
87
 
111
- this.root = owner.root;
112
- this.instanceConfig = owner.instanceConfig;
88
+ const codec = getSharedValueCodec('string');
113
89
 
114
- this.getActiveLanguage = owner.getActiveLanguage;
115
90
  this.validationState = { violations: [] };
116
91
 
117
92
  this.valueType = 'string';
118
- this.evaluator = owner.evaluator;
119
93
  this.decodeInstanceValue = codec.decodeInstanceValue;
120
94
 
121
95
  const instanceValueState = createInstanceValueState(this);
@@ -134,6 +108,15 @@ export class Attribute
134
108
  value: this.valueState,
135
109
  instanceValue: this.getInstanceValue,
136
110
  relevant: this.owner.isRelevant,
111
+
112
+ readonly: () => true,
113
+ reference: this.contextReference,
114
+ required: () => false,
115
+ children: null,
116
+ label: () => null,
117
+ hint: () => null,
118
+ attributes: () => [],
119
+ valueOptions: () => [],
137
120
  },
138
121
  this.instanceConfig
139
122
  );
@@ -142,6 +125,11 @@ export class Attribute
142
125
  this.engineState = state.engineState;
143
126
  this.currentState = state.currentState;
144
127
  this.instanceState = createAttributeNodeInstanceState(this);
128
+ this.attributeState = createAttributeState(this.scope);
129
+
130
+ this.getXPathValue = () => {
131
+ return this.getInstanceValue();
132
+ };
145
133
  }
146
134
 
147
135
  setValue(value: string): Root {
@@ -150,15 +138,11 @@ export class Attribute
150
138
  return this.root;
151
139
  }
152
140
 
153
- getChildren(): readonly [] {
141
+ override getAttributes(): readonly Attribute[] {
154
142
  return [];
155
143
  }
156
144
 
157
- getXPathChildNodes(): readonly AnyChildNode[] {
145
+ getChildren(): readonly [] {
158
146
  return [];
159
147
  }
160
-
161
- getXPathValue(): string {
162
- return '';
163
- }
164
148
  }
@@ -8,7 +8,10 @@ import type { AncestorNodeValidationState } from '../client/validation.ts';
8
8
  import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPathNode.ts';
9
9
  import type { StaticElement } from '../integration/xpath/static-dom/StaticElement.ts';
10
10
  import { createParentNodeInstanceState } from '../lib/client-reactivity/instance-state/createParentNodeInstanceState.ts';
11
- import { createAttributeState } from '../lib/reactivity/createAttributeState.ts';
11
+ import {
12
+ createAttributeState,
13
+ type AttributeState,
14
+ } from '../lib/reactivity/createAttributeState.ts';
12
15
  import type { ChildrenState } from '../lib/reactivity/createChildrenState.ts';
13
16
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
14
17
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -53,6 +56,7 @@ export class Group
53
56
  // InstanceNode
54
57
  protected readonly state: SharedNodeState<GroupStateSpec>;
55
58
  protected override engineState: EngineState<GroupStateSpec>;
59
+ readonly attributeState: AttributeState;
56
60
 
57
61
  // GroupNode
58
62
  readonly nodeType = 'group';
@@ -72,7 +76,7 @@ export class Group
72
76
  this.appearances = definition.bodyElement?.appearances ?? null;
73
77
 
74
78
  const childrenState = createChildrenState<Group, GeneralChildNode>(this);
75
- const attributeState = createAttributeState(this.scope);
79
+ this.attributeState = createAttributeState(this.scope);
76
80
 
77
81
  this.childrenState = childrenState;
78
82
 
@@ -87,7 +91,7 @@ export class Group
87
91
  label: createNodeLabel(this, definition),
88
92
  hint: null,
89
93
  children: childrenState.childIds,
90
- attributes: attributeState.getAttributes,
94
+ attributes: this.attributeState.getAttributes,
91
95
  valueOptions: null,
92
96
  value: null,
93
97
  },
@@ -103,7 +107,7 @@ export class Group
103
107
  );
104
108
 
105
109
  childrenState.setChildren(buildChildren(this));
106
- attributeState.setAttributes(buildAttributes(this));
110
+ this.attributeState.setAttributes(buildAttributes(this));
107
111
  this.validationState = createAggregatedViolations(this, this.instanceConfig);
108
112
  this.instanceState = createParentNodeInstanceState(this);
109
113
  }
@@ -111,4 +115,8 @@ export class Group
111
115
  getChildren(): readonly GeneralChildNode[] {
112
116
  return this.childrenState.getChildren();
113
117
  }
118
+
119
+ override getAttributes(): readonly Attribute[] {
120
+ return this.attributeState.getAttributes();
121
+ }
114
122
  }