@getodk/xforms-engine 0.16.0 → 0.16.1

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 (54) hide show
  1. package/dist/client/AttributeNode.d.ts +4 -3
  2. package/dist/index.js +272 -211
  3. package/dist/index.js.map +1 -1
  4. package/dist/instance/Attribute.d.ts +11 -23
  5. package/dist/instance/Group.d.ts +3 -0
  6. package/dist/instance/InputControl.d.ts +3 -0
  7. package/dist/instance/ModelValue.d.ts +4 -0
  8. package/dist/instance/Note.d.ts +4 -0
  9. package/dist/instance/PrimaryInstance.d.ts +3 -0
  10. package/dist/instance/RangeControl.d.ts +4 -0
  11. package/dist/instance/RankControl.d.ts +5 -1
  12. package/dist/instance/Root.d.ts +3 -0
  13. package/dist/instance/SelectControl.d.ts +5 -1
  14. package/dist/instance/TriggerControl.d.ts +4 -0
  15. package/dist/instance/UploadControl.d.ts +3 -0
  16. package/dist/instance/abstract/DescendantNode.d.ts +5 -4
  17. package/dist/instance/abstract/InstanceNode.d.ts +4 -3
  18. package/dist/instance/hierarchy.d.ts +2 -1
  19. package/dist/instance/repeat/RepeatInstance.d.ts +2 -0
  20. package/dist/integration/xpath/adapter/XFormsXPathNode.d.ts +1 -1
  21. package/dist/integration/xpath/adapter/kind.d.ts +5 -3
  22. package/dist/integration/xpath/adapter/traversal.d.ts +3 -3
  23. package/dist/integration/xpath/static-dom/StaticAttribute.d.ts +1 -0
  24. package/dist/parse/model/AttributeDefinition.d.ts +2 -0
  25. package/dist/solid.js +272 -211
  26. package/dist/solid.js.map +1 -1
  27. package/package.json +2 -2
  28. package/src/client/AttributeNode.ts +4 -3
  29. package/src/instance/Attribute.ts +38 -54
  30. package/src/instance/Group.ts +12 -4
  31. package/src/instance/InputControl.ts +12 -4
  32. package/src/instance/ModelValue.ts +13 -4
  33. package/src/instance/Note.ts +13 -4
  34. package/src/instance/PrimaryInstance.ts +12 -4
  35. package/src/instance/RangeControl.ts +13 -4
  36. package/src/instance/RankControl.ts +14 -5
  37. package/src/instance/Root.ts +12 -4
  38. package/src/instance/SelectControl.ts +14 -5
  39. package/src/instance/TriggerControl.ts +13 -4
  40. package/src/instance/UploadControl.ts +13 -3
  41. package/src/instance/abstract/DescendantNode.ts +4 -3
  42. package/src/instance/abstract/InstanceNode.ts +5 -3
  43. package/src/instance/attachments/buildAttributes.ts +5 -1
  44. package/src/instance/children/childrenInitOptions.ts +2 -1
  45. package/src/instance/hierarchy.ts +2 -0
  46. package/src/instance/repeat/RepeatInstance.ts +11 -3
  47. package/src/integration/xpath/adapter/XFormsXPathNode.ts +1 -0
  48. package/src/integration/xpath/adapter/engineDOMAdapter.ts +2 -2
  49. package/src/integration/xpath/adapter/kind.ts +6 -1
  50. package/src/integration/xpath/adapter/names.ts +1 -0
  51. package/src/integration/xpath/adapter/traversal.ts +5 -6
  52. package/src/integration/xpath/static-dom/StaticAttribute.ts +1 -0
  53. package/src/lib/reactivity/createInstanceValueState.ts +29 -3
  54. package/src/parse/model/AttributeDefinition.ts +7 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getodk/xforms-engine",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
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.9.2",
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
@@ -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
  }
@@ -13,7 +13,10 @@ import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPat
13
13
  import type { StaticLeafElement } from '../integration/xpath/static-dom/StaticElement.ts';
14
14
  import type { RuntimeInputValue, RuntimeValue } from '../lib/codecs/getSharedValueCodec.ts';
15
15
  import { getSharedValueCodec } from '../lib/codecs/getSharedValueCodec.ts';
16
- import { createAttributeState } from '../lib/reactivity/createAttributeState.ts';
16
+ import {
17
+ createAttributeState,
18
+ type AttributeState,
19
+ } from '../lib/reactivity/createAttributeState.ts';
17
20
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
18
21
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
19
22
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
@@ -104,6 +107,7 @@ export class InputControl<V extends ValueType = ValueType>
104
107
  // InstanceNode
105
108
  protected readonly state: SharedNodeState<InputControlStateSpec<V>>;
106
109
  protected readonly engineState: EngineState<InputControlStateSpec<V>>;
110
+ readonly attributeState: AttributeState;
107
111
 
108
112
  // InputNode
109
113
  readonly nodeType = 'input';
@@ -122,7 +126,7 @@ export class InputControl<V extends ValueType = ValueType>
122
126
 
123
127
  this.appearances = definition.bodyElement.appearances;
124
128
  this.nodeOptions = nodeOptionsFactoryByType[definition.valueType](definition.bodyElement);
125
- const attributeState = createAttributeState(this.scope);
129
+ this.attributeState = createAttributeState(this.scope);
126
130
 
127
131
  const state = createSharedNodeState(
128
132
  this.scope,
@@ -135,7 +139,7 @@ export class InputControl<V extends ValueType = ValueType>
135
139
  label: createNodeLabel(this, definition),
136
140
  hint: createFieldHint(this, definition),
137
141
  children: null,
138
- attributes: attributeState.getAttributes,
142
+ attributes: this.attributeState.getAttributes,
139
143
  valueOptions: null,
140
144
  value: this.valueState,
141
145
  instanceValue: this.getInstanceValue,
@@ -143,7 +147,7 @@ export class InputControl<V extends ValueType = ValueType>
143
147
  this.instanceConfig
144
148
  );
145
149
 
146
- attributeState.setAttributes(buildAttributes(this));
150
+ this.attributeState.setAttributes(buildAttributes(this));
147
151
 
148
152
  this.state = state;
149
153
  this.engineState = state.engineState;
@@ -155,6 +159,10 @@ export class InputControl<V extends ValueType = ValueType>
155
159
 
156
160
  return this.root;
157
161
  }
162
+
163
+ override getAttributes(): readonly Attribute[] {
164
+ return this.attributeState.getAttributes();
165
+ }
158
166
  }
159
167
 
160
168
  export type AnyInputControl =
@@ -5,11 +5,15 @@ import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPat
5
5
  import type { StaticLeafElement } from '../integration/xpath/static-dom/StaticElement.ts';
6
6
  import type { RuntimeInputValue, RuntimeValue } from '../lib/codecs/getSharedValueCodec.ts';
7
7
  import { getSharedValueCodec } from '../lib/codecs/getSharedValueCodec.ts';
8
- import { createAttributeState } from '../lib/reactivity/createAttributeState.ts';
8
+ import {
9
+ createAttributeState,
10
+ type AttributeState,
11
+ } from '../lib/reactivity/createAttributeState.ts';
9
12
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
10
13
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
11
14
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
12
15
  import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
16
+ import type { Attribute } from './Attribute.ts';
13
17
  import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
14
18
  import { buildAttributes } from './attachments/buildAttributes.ts';
15
19
  import type { GeneralParentNode } from './hierarchy.ts';
@@ -51,6 +55,7 @@ export class ModelValue<V extends ValueType = ValueType>
51
55
  // InstanceNode
52
56
  protected readonly state: SharedNodeState<ModelValueStateSpec<V>>;
53
57
  protected readonly engineState: EngineState<ModelValueStateSpec<V>>;
58
+ readonly attributeState: AttributeState;
54
59
 
55
60
  // ModelValueNode
56
61
  readonly nodeType = 'model-value';
@@ -67,7 +72,7 @@ export class ModelValue<V extends ValueType = ValueType>
67
72
 
68
73
  super(parent, instanceNode, definition, codec);
69
74
 
70
- const attributeState = createAttributeState(this.scope);
75
+ this.attributeState = createAttributeState(this.scope);
71
76
 
72
77
  const state = createSharedNodeState(
73
78
  this.scope,
@@ -80,7 +85,7 @@ export class ModelValue<V extends ValueType = ValueType>
80
85
  label: null,
81
86
  hint: null,
82
87
  children: null,
83
- attributes: attributeState.getAttributes,
88
+ attributes: this.attributeState.getAttributes,
84
89
  valueOptions: null,
85
90
  value: this.valueState,
86
91
  instanceValue: this.getInstanceValue,
@@ -88,12 +93,16 @@ export class ModelValue<V extends ValueType = ValueType>
88
93
  this.instanceConfig
89
94
  );
90
95
 
91
- attributeState.setAttributes(buildAttributes(this));
96
+ this.attributeState.setAttributes(buildAttributes(this));
92
97
 
93
98
  this.state = state;
94
99
  this.engineState = state.engineState;
95
100
  this.currentState = state.currentState;
96
101
  }
102
+
103
+ override getAttributes(): readonly Attribute[] {
104
+ return this.attributeState.getAttributes();
105
+ }
97
106
  }
98
107
 
99
108
  export type AnyModelValue =
@@ -8,7 +8,10 @@ import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPat
8
8
  import type { StaticLeafElement } from '../integration/xpath/static-dom/StaticElement.ts';
9
9
  import { getNoteCodec } from '../lib/codecs/getNoteCodec.ts';
10
10
  import type { NoteInputValue, NoteRuntimeValue } from '../lib/codecs/NoteCodec.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 { createNoteReadonlyThunk } from '../lib/reactivity/createNoteReadonlyThunk.ts';
13
16
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
14
17
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
@@ -20,6 +23,7 @@ import { createNoteText, type ComputedNoteText } from '../lib/reactivity/text/cr
20
23
  import type { NoteNodeDefinition } from '../parse/model/NoteNodeDefinition.ts';
21
24
  import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
22
25
  import { buildAttributes } from './attachments/buildAttributes.ts';
26
+ import type { Attribute } from './Attribute.ts';
23
27
  import type { GeneralParentNode } from './hierarchy.ts';
24
28
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
25
29
  import type { ClientReactiveSerializableValueNode } from './internal-api/serialization/ClientReactiveSerializableValueNode.ts';
@@ -48,6 +52,7 @@ export class Note<V extends ValueType = ValueType>
48
52
  // InstanceNode
49
53
  protected readonly state: SharedNodeState<NoteStateSpec<V>>;
50
54
  protected readonly engineState: EngineState<NoteStateSpec<V>>;
55
+ readonly attributeState: AttributeState;
51
56
 
52
57
  // NoteNode
53
58
  readonly nodeType = 'note';
@@ -68,7 +73,7 @@ export class Note<V extends ValueType = ValueType>
68
73
 
69
74
  const isReadonly = createNoteReadonlyThunk(this, definition);
70
75
  const noteTextComputation = createNoteText(this, definition.noteTextDefinition);
71
- const attributeState = createAttributeState(this.scope);
76
+ this.attributeState = createAttributeState(this.scope);
72
77
 
73
78
  let noteText: ComputedNoteText;
74
79
  let label: Accessor<TextRange<'label', 'form'> | null>;
@@ -108,7 +113,7 @@ export class Note<V extends ValueType = ValueType>
108
113
  noteText,
109
114
 
110
115
  children: null,
111
- attributes: attributeState.getAttributes,
116
+ attributes: this.attributeState.getAttributes,
112
117
  valueOptions: null,
113
118
  value: this.valueState,
114
119
  instanceValue: this.getInstanceValue,
@@ -116,12 +121,16 @@ export class Note<V extends ValueType = ValueType>
116
121
  this.instanceConfig
117
122
  );
118
123
 
119
- attributeState.setAttributes(buildAttributes(this));
124
+ this.attributeState.setAttributes(buildAttributes(this));
120
125
 
121
126
  this.state = state;
122
127
  this.engineState = state.engineState;
123
128
  this.currentState = state.currentState;
124
129
  }
130
+
131
+ override getAttributes(): readonly Attribute[] {
132
+ return this.attributeState.getAttributes();
133
+ }
125
134
  }
126
135
 
127
136
  export type AnyNote =
@@ -17,7 +17,10 @@ import { EngineXPathEvaluator } from '../integration/xpath/EngineXPathEvaluator.
17
17
  import type { StaticDocument } from '../integration/xpath/static-dom/StaticDocument.ts';
18
18
  import { createPrimaryInstanceState } from '../lib/client-reactivity/instance-state/createPrimaryInstanceState.ts';
19
19
  import { prepareInstancePayload } from '../lib/client-reactivity/instance-state/prepareInstancePayload.ts';
20
- import { createAttributeState } from '../lib/reactivity/createAttributeState.ts';
20
+ import {
21
+ createAttributeState,
22
+ type AttributeState,
23
+ } from '../lib/reactivity/createAttributeState.ts';
21
24
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
22
25
  import { createTranslationState } from '../lib/reactivity/createTranslationState.ts';
23
26
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -125,6 +128,7 @@ export class PrimaryInstance<
125
128
  // InstanceNode
126
129
  protected readonly state: SharedNodeState<PrimaryInstanceStateSpec>;
127
130
  protected readonly engineState: EngineState<PrimaryInstanceStateSpec>;
131
+ readonly attributeState: AttributeState;
128
132
 
129
133
  override readonly instanceNode: StaticDocument;
130
134
  readonly getChildren: Accessor<readonly Root[]>;
@@ -197,7 +201,7 @@ export class PrimaryInstance<
197
201
  this.classes = definition.classes;
198
202
 
199
203
  const childrenState = createChildrenState<this, Root>(this);
200
- const attributeState = createAttributeState(this.scope);
204
+ this.attributeState = createAttributeState(this.scope);
201
205
 
202
206
  this.getChildren = childrenState.getChildren;
203
207
 
@@ -212,7 +216,7 @@ export class PrimaryInstance<
212
216
  valueOptions: null,
213
217
  value: null,
214
218
  children: childrenState.childIds,
215
- attributes: attributeState.getAttributes,
219
+ attributes: this.attributeState.getAttributes,
216
220
  };
217
221
 
218
222
  const state = createSharedNodeState(scope, stateSpec, config);
@@ -233,10 +237,14 @@ export class PrimaryInstance<
233
237
  this.instanceState = createPrimaryInstanceState(this);
234
238
 
235
239
  childrenState.setChildren([root]);
236
- attributeState.setAttributes(buildAttributes(this));
240
+ this.attributeState.setAttributes(buildAttributes(this));
237
241
  setIsAttached(true);
238
242
  }
239
243
 
244
+ override getAttributes(): readonly Attribute[] {
245
+ return this.attributeState.getAttributes();
246
+ }
247
+
240
248
  // PrimaryInstanceDocument
241
249
  /**
242
250
  * @todo Note that this method's signature is intentionally derived from
@@ -11,7 +11,10 @@ import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPat
11
11
  import type { StaticLeafElement } from '../integration/xpath/static-dom/StaticElement.ts';
12
12
  import { RangeCodec } from '../lib/codecs/RangeCodec.ts';
13
13
  import { getSharedValueCodec } from '../lib/codecs/getSharedValueCodec.ts';
14
- import { createAttributeState } from '../lib/reactivity/createAttributeState.ts';
14
+ import {
15
+ createAttributeState,
16
+ type AttributeState,
17
+ } from '../lib/reactivity/createAttributeState.ts';
15
18
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
16
19
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
17
20
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
@@ -23,6 +26,7 @@ import type {
23
26
  RangeNodeDefinition,
24
27
  RangeValueType,
25
28
  } from '../parse/model/RangeNodeDefinition.ts';
29
+ import type { Attribute } from './Attribute.ts';
26
30
  import type { Root } from './Root.ts';
27
31
  import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
28
32
  import { buildAttributes } from './attachments/buildAttributes.ts';
@@ -66,6 +70,7 @@ export class RangeControl<V extends RangeValueType = RangeValueType>
66
70
  // InstanceNode
67
71
  protected readonly state: SharedNodeState<RangeControlStateSpec<V>>;
68
72
  protected readonly engineState: EngineState<RangeControlStateSpec<V>>;
73
+ readonly attributeState: AttributeState;
69
74
 
70
75
  // RangeNode
71
76
  readonly nodeType = 'range';
@@ -84,7 +89,7 @@ export class RangeControl<V extends RangeValueType = RangeValueType>
84
89
  super(parent, instanceNode, definition, codec);
85
90
 
86
91
  this.appearances = definition.bodyElement.appearances;
87
- const attributeState = createAttributeState(this.scope);
92
+ this.attributeState = createAttributeState(this.scope);
88
93
 
89
94
  const state = createSharedNodeState(
90
95
  this.scope,
@@ -97,7 +102,7 @@ export class RangeControl<V extends RangeValueType = RangeValueType>
97
102
  label: createNodeLabel(this, definition),
98
103
  hint: createFieldHint(this, definition),
99
104
  children: null,
100
- attributes: attributeState.getAttributes,
105
+ attributes: this.attributeState.getAttributes,
101
106
  valueOptions: null,
102
107
  value: this.valueState,
103
108
  instanceValue: this.getInstanceValue,
@@ -105,7 +110,7 @@ export class RangeControl<V extends RangeValueType = RangeValueType>
105
110
  this.instanceConfig
106
111
  );
107
112
 
108
- attributeState.setAttributes(buildAttributes(this));
113
+ this.attributeState.setAttributes(buildAttributes(this));
109
114
 
110
115
  this.state = state;
111
116
  this.engineState = state.engineState;
@@ -117,6 +122,10 @@ export class RangeControl<V extends RangeValueType = RangeValueType>
117
122
 
118
123
  return this.root;
119
124
  }
125
+
126
+ override getAttributes(): readonly Attribute[] {
127
+ return this.attributeState.getAttributes();
128
+ }
120
129
  }
121
130
 
122
131
  // prettier-ignore
@@ -1,4 +1,4 @@
1
- import { type XPathChoiceNode, XPathNodeKindKey } from '@getodk/xpath';
1
+ import { XPathNodeKindKey, type XPathChoiceNode } from '@getodk/xpath';
2
2
  import type { Accessor } from 'solid-js';
3
3
  import { createMemo } from 'solid-js';
4
4
  import type { RankDefinition, RankItem, RankNode, RankValueOptions } from '../client/RankNode.ts';
@@ -10,7 +10,10 @@ import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPat
10
10
  import type { StaticLeafElement } from '../integration/xpath/static-dom/StaticElement.ts';
11
11
  import { sharedValueCodecs } from '../lib/codecs/getSharedValueCodec.ts';
12
12
  import { MultipleValueItemCodec } from '../lib/codecs/items/MultipleValueItemCodec.ts';
13
- import { createAttributeState } from '../lib/reactivity/createAttributeState.ts';
13
+ import {
14
+ createAttributeState,
15
+ type AttributeState,
16
+ } from '../lib/reactivity/createAttributeState.ts';
14
17
  import { createItemCollection } from '../lib/reactivity/createItemCollection.ts';
15
18
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
16
19
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
@@ -20,6 +23,7 @@ import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
20
23
  import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
21
24
  import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
22
25
  import type { UnknownAppearanceDefinition } from '../parse/body/appearance/unknownAppearanceParser.ts';
26
+ import type { Attribute } from './Attribute.ts';
23
27
  import type { Root } from './Root.ts';
24
28
  import type { ValueNodeStateSpec } from './abstract/ValueNode.ts';
25
29
  import { ValueNode } from './abstract/ValueNode.ts';
@@ -96,6 +100,7 @@ export class RankControl
96
100
  // InstanceNode
97
101
  protected readonly state: SharedNodeState<RankControlStateSpec>;
98
102
  protected readonly engineState: EngineState<RankControlStateSpec>;
103
+ readonly attributeState: AttributeState;
99
104
 
100
105
  // RankNode
101
106
  readonly nodeType = 'rank';
@@ -125,7 +130,7 @@ export class RankControl
125
130
  const baseValueState = this.valueState;
126
131
  const [baseGetValue, setValue] = baseValueState;
127
132
 
128
- const attributeState = createAttributeState(this.scope);
133
+ this.attributeState = createAttributeState(this.scope);
129
134
 
130
135
  /**
131
136
  * @ToDo As new value options become available, they're not yet in the
@@ -178,7 +183,7 @@ export class RankControl
178
183
  label: createNodeLabel(this, definition),
179
184
  hint: createFieldHint(this, definition),
180
185
  children: null,
181
- attributes: attributeState.getAttributes,
186
+ attributes: this.attributeState.getAttributes,
182
187
  valueOptions,
183
188
  value: valueState,
184
189
  instanceValue: this.getInstanceValue,
@@ -186,13 +191,17 @@ export class RankControl
186
191
  this.instanceConfig
187
192
  );
188
193
 
189
- attributeState.setAttributes(buildAttributes(this));
194
+ this.attributeState.setAttributes(buildAttributes(this));
190
195
 
191
196
  this.state = state;
192
197
  this.engineState = state.engineState;
193
198
  this.currentState = state.currentState;
194
199
  }
195
200
 
201
+ override getAttributes(): readonly Attribute[] {
202
+ return this.attributeState.getAttributes();
203
+ }
204
+
196
205
  getValueLabel(value: string): TextRange<'item-label'> | null {
197
206
  const valueOption = this.currentState.valueOptions.find((item) => item.value === value);
198
207
  return valueOption?.label ?? null;