@getodk/xforms-engine 0.14.0 → 0.15.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 (87) hide show
  1. package/dist/client/AttributeNode.d.ts +53 -0
  2. package/dist/client/index.d.ts +1 -0
  3. package/dist/client/node-types.d.ts +1 -1
  4. package/dist/client/validation.d.ts +7 -1
  5. package/dist/index.js +436 -164
  6. package/dist/index.js.map +1 -1
  7. package/dist/instance/Attribute.d.ts +58 -0
  8. package/dist/instance/Group.d.ts +4 -0
  9. package/dist/instance/PrimaryInstance.d.ts +4 -0
  10. package/dist/instance/Root.d.ts +4 -0
  11. package/dist/instance/UploadControl.d.ts +4 -0
  12. package/dist/instance/abstract/InstanceNode.d.ts +7 -4
  13. package/dist/instance/abstract/ValueNode.d.ts +1 -0
  14. package/dist/instance/attachments/buildAttributes.d.ts +3 -0
  15. package/dist/instance/hierarchy.d.ts +2 -1
  16. package/dist/instance/internal-api/AttributeContext.d.ts +29 -0
  17. package/dist/instance/internal-api/serialization/ClientReactiveSerializableAttributeNode.d.ts +16 -0
  18. package/dist/instance/internal-api/serialization/ClientReactiveSerializableParentNode.d.ts +2 -0
  19. package/dist/instance/internal-api/serialization/ClientReactiveSerializableTemplatedNode.d.ts +2 -2
  20. package/dist/instance/repeat/BaseRepeatRange.d.ts +5 -0
  21. package/dist/instance/repeat/RepeatInstance.d.ts +4 -2
  22. package/dist/integration/xpath/adapter/XFormsXPathNode.d.ts +2 -2
  23. package/dist/lib/client-reactivity/instance-state/createAttributeNodeInstanceState.d.ts +3 -0
  24. package/dist/lib/client-reactivity/instance-state/createTemplatedNodeInstanceState.d.ts +0 -3
  25. package/dist/lib/names/NamespaceDeclarationMap.d.ts +1 -1
  26. package/dist/lib/reactivity/createAttributeState.d.ts +16 -0
  27. package/dist/lib/reactivity/createAttributeValueState.d.ts +15 -0
  28. package/dist/lib/xml-serialization.d.ts +5 -9
  29. package/dist/parse/XFormDOM.d.ts +1 -1
  30. package/dist/parse/model/AttributeDefinition.d.ts +22 -0
  31. package/dist/parse/model/{RootAttributeMap.d.ts → AttributeDefinitionMap.d.ts} +4 -10
  32. package/dist/parse/model/GroupDefinition.d.ts +4 -1
  33. package/dist/parse/model/LeafNodeDefinition.d.ts +1 -0
  34. package/dist/parse/model/NodeDefinition.d.ts +8 -3
  35. package/dist/parse/model/RepeatDefinition.d.ts +4 -1
  36. package/dist/parse/model/RootDefinition.d.ts +2 -2
  37. package/dist/solid.js +436 -164
  38. package/dist/solid.js.map +1 -1
  39. package/package.json +5 -5
  40. package/src/client/AttributeNode.ts +62 -0
  41. package/src/client/index.ts +1 -0
  42. package/src/client/node-types.ts +1 -0
  43. package/src/client/validation.ts +9 -1
  44. package/src/instance/Attribute.ts +164 -0
  45. package/src/instance/Group.ts +16 -0
  46. package/src/instance/InputControl.ts +1 -0
  47. package/src/instance/ModelValue.ts +1 -0
  48. package/src/instance/Note.ts +1 -0
  49. package/src/instance/PrimaryInstance.ts +17 -0
  50. package/src/instance/RangeControl.ts +1 -0
  51. package/src/instance/RankControl.ts +1 -0
  52. package/src/instance/Root.ts +16 -0
  53. package/src/instance/SelectControl.ts +1 -0
  54. package/src/instance/TriggerControl.ts +1 -0
  55. package/src/instance/UploadControl.ts +14 -0
  56. package/src/instance/abstract/DescendantNode.ts +5 -1
  57. package/src/instance/abstract/InstanceNode.ts +6 -3
  58. package/src/instance/abstract/ValueNode.ts +1 -0
  59. package/src/instance/attachments/buildAttributes.ts +8 -0
  60. package/src/instance/hierarchy.ts +3 -1
  61. package/src/instance/internal-api/AttributeContext.ts +34 -0
  62. package/src/instance/internal-api/serialization/ClientReactiveSerializableAttributeNode.ts +19 -0
  63. package/src/instance/internal-api/serialization/ClientReactiveSerializableParentNode.ts +2 -0
  64. package/src/instance/internal-api/serialization/ClientReactiveSerializableTemplatedNode.ts +2 -3
  65. package/src/instance/repeat/BaseRepeatRange.ts +14 -0
  66. package/src/instance/repeat/RepeatInstance.ts +14 -5
  67. package/src/integration/xpath/adapter/XFormsXPathNode.ts +3 -1
  68. package/src/lib/client-reactivity/instance-state/createAttributeNodeInstanceState.ts +16 -0
  69. package/src/lib/client-reactivity/instance-state/createParentNodeInstanceState.ts +5 -5
  70. package/src/lib/client-reactivity/instance-state/createRootInstanceState.ts +6 -9
  71. package/src/lib/client-reactivity/instance-state/createTemplatedNodeInstanceState.ts +5 -15
  72. package/src/lib/names/NamespaceDeclarationMap.ts +1 -1
  73. package/src/lib/reactivity/createAttributeState.ts +51 -0
  74. package/src/lib/reactivity/createAttributeValueState.ts +189 -0
  75. package/src/lib/xml-serialization.ts +30 -34
  76. package/src/parse/model/AttributeDefinition.ts +58 -0
  77. package/src/parse/model/{RootAttributeMap.ts → AttributeDefinitionMap.ts} +7 -13
  78. package/src/parse/model/GroupDefinition.ts +6 -0
  79. package/src/parse/model/LeafNodeDefinition.ts +1 -0
  80. package/src/parse/model/NodeDefinition.ts +11 -3
  81. package/src/parse/model/RepeatDefinition.ts +8 -1
  82. package/src/parse/model/RootDefinition.ts +5 -5
  83. package/src/parse/model/nodeDefinitionMap.ts +1 -1
  84. package/dist/error/TemplatedNodeAttributeSerializationError.d.ts +0 -22
  85. package/dist/parse/model/RootAttributeDefinition.d.ts +0 -21
  86. package/src/error/TemplatedNodeAttributeSerializationError.ts +0 -24
  87. package/src/parse/model/RootAttributeDefinition.ts +0 -44
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getodk/xforms-engine",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "XForms engine for ODK Web Forms",
6
6
  "type": "module",
@@ -62,16 +62,16 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "@babel/core": "^7.28.0",
65
- "@getodk/tree-sitter-xpath": "0.2.0",
66
- "@getodk/xpath": "0.8.1",
67
- "@playwright/test": "^1.53.2",
65
+ "@getodk/tree-sitter-xpath": "0.2.1",
66
+ "@getodk/xpath": "0.9.0",
67
+ "@playwright/test": "^1.55.1",
68
68
  "@types/papaparse": "^5.3.16",
69
69
  "@vitest/browser": "^3.2.4",
70
70
  "babel-plugin-transform-jsbi-to-bigint": "^1.4.2",
71
71
  "http-server": "^14.1.1",
72
72
  "jsdom": "^26.1.0",
73
73
  "typedoc": "^0.28.7",
74
- "vite": "^7.0.3",
74
+ "vite": "^7.0.8",
75
75
  "vite-plugin-dts": "^4.5.4",
76
76
  "vite-plugin-no-bundle": "^4.0.0",
77
77
  "vitest": "^3.2.4"
@@ -0,0 +1,62 @@
1
+ import type { Root } from '../instance/Root.ts';
2
+ import type { AttributeDefinition } from '../parse/model/AttributeDefinition.ts';
3
+ import type { OpaqueReactiveObjectFactory } from './OpaqueReactiveObjectFactory.ts';
4
+ import type { InstanceState } from './serialization/InstanceState.ts';
5
+
6
+ export interface AttributeNodeState {
7
+ get value(): string;
8
+ }
9
+
10
+ /**
11
+ * Base interface for common/shared aspects of attributes.
12
+ */
13
+ export interface AttributeNode {
14
+ /**
15
+ * Specifies the node's general type. This can be useful for narrowing types,
16
+ * e.g. those of children.
17
+ */
18
+ readonly nodeType: 'attribute';
19
+
20
+ /**
21
+ * Each node has a definition which specifies aspects of the node defined in
22
+ * the form. These aspects include (but are not limited to) the node's data
23
+ * type, its body presentation constraints (if any), its bound nodeset, and
24
+ * so on...
25
+ */
26
+ readonly definition: AttributeDefinition;
27
+
28
+ /**
29
+ * Each node links back to the node representing the root of the form.
30
+ */
31
+ readonly root: Root;
32
+
33
+ /**
34
+ * Each node links back to its parent, if any. All nodes have a parent except
35
+ * the form's {@link root}.
36
+ */
37
+ readonly parent: unknown;
38
+
39
+ /**
40
+ * Each node provides a discrete object representing the stateful aspects of
41
+ * that node which will change over time. This includes state which is either
42
+ * client-/user-mutable, or state which is computed based on the core XForms
43
+ * computation model. Each node also exposes {@link validationState}, which
44
+ * reflects the validity of the node, or its descendants.
45
+ *
46
+ * When a client provides a {@link OpaqueReactiveObjectFactory}, the engine
47
+ * will update the properties of this object as their respective states
48
+ * change, so a client can implement reactive updates that respond to changes
49
+ * as they occur.
50
+ */
51
+ readonly currentState: AttributeNodeState;
52
+
53
+ /**
54
+ * Represents the current instance state of the node.
55
+ *
56
+ * @see {@link InstanceState.instanceXML} for additional detail.
57
+ */
58
+ readonly instanceState: InstanceState;
59
+
60
+ readonly appearances: null;
61
+ readonly nodeOptions: null;
62
+ }
@@ -1,5 +1,6 @@
1
1
  export type * from './attachments/InstanceAttachmentMeta.ts';
2
2
  export type * from './attachments/InstanceAttachmentsConfig.ts';
3
+ export type * from './AttributeNode.ts';
3
4
  export type * from './constants.ts';
4
5
  export * as constants from './constants.ts';
5
6
  export type * from './form/CreateFormInstance.ts';
@@ -13,6 +13,7 @@ export type LeafNodeType =
13
13
  | 'trigger'
14
14
  | 'range'
15
15
  | 'rank'
16
+ | 'attribute'
16
17
  | 'upload';
17
18
 
18
19
  // prettier-ignore
@@ -193,7 +193,15 @@ export interface AncestorNodeValidationState {
193
193
  get violations(): readonly DescendantNodeViolationReference[];
194
194
  }
195
195
 
196
+ /**
197
+ * Convenience interface for nodes that cannot be invalid.
198
+ */
199
+ export interface NullValidationState {
200
+ get violations(): readonly [];
201
+ }
202
+
196
203
  // prettier-ignore
197
204
  export type NodeValidationState =
198
205
  | AncestorNodeValidationState
199
- | LeafNodeValidationState;
206
+ | LeafNodeValidationState
207
+ | NullValidationState;
@@ -0,0 +1,164 @@
1
+ import { XPathNodeKindKey } from '@getodk/xpath';
2
+ import type { Accessor } from 'solid-js';
3
+ import type { AttributeNode } from '../client/AttributeNode.ts';
4
+ import type { ActiveLanguage, InstanceState, NullValidationState } from '../client/index.ts';
5
+ import type { XFormsXPathAttribute } from '../integration/xpath/adapter/XFormsXPathNode.ts';
6
+ import type { EngineXPathEvaluator } from '../integration/xpath/EngineXPathEvaluator.ts';
7
+ import type { StaticAttribute } from '../integration/xpath/static-dom/StaticAttribute.ts';
8
+ import { createAttributeNodeInstanceState } from '../lib/client-reactivity/instance-state/createAttributeNodeInstanceState.ts';
9
+ import {
10
+ getSharedValueCodec,
11
+ type RuntimeInputValue,
12
+ type RuntimeValue,
13
+ } from '../lib/codecs/getSharedValueCodec.ts';
14
+ import type { RuntimeValueSetter, RuntimeValueState } from '../lib/codecs/ValueCodec.ts';
15
+ import { createAttributeValueState } from '../lib/reactivity/createAttributeValueState.ts';
16
+ import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
17
+ import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
18
+ import {
19
+ createSharedNodeState,
20
+ type SharedNodeState,
21
+ } from '../lib/reactivity/node-state/createSharedNodeState.ts';
22
+ import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
23
+ import type { AttributeDefinition } from '../parse/model/AttributeDefinition.ts';
24
+ import type { DescendantNodeSharedStateSpec } from './abstract/DescendantNode.ts';
25
+ import { InstanceNode } from './abstract/InstanceNode.ts';
26
+ import type { AnyParentNode } from './hierarchy.ts';
27
+ import type { AttributeContext } from './internal-api/AttributeContext.ts';
28
+ import type { DecodeInstanceValue } from './internal-api/InstanceValueContext.ts';
29
+ import type { ClientReactiveSerializableAttributeNode } from './internal-api/serialization/ClientReactiveSerializableAttributeNode.ts';
30
+ import type { Root } from './Root.ts';
31
+
32
+ export interface AttributeStateSpec extends DescendantNodeSharedStateSpec {
33
+ readonly children: null;
34
+ readonly attributes: null;
35
+ readonly value: SimpleAtomicState<string>;
36
+ readonly instanceValue: Accessor<string>;
37
+ readonly label: null;
38
+ readonly hint: null;
39
+ readonly valueOptions: null;
40
+ }
41
+
42
+ export class Attribute
43
+ extends InstanceNode<AttributeDefinition, AttributeStateSpec, AnyParentNode, null>
44
+ implements
45
+ AttributeNode,
46
+ ClientReactiveSerializableAttributeNode,
47
+ AttributeContext,
48
+ XFormsXPathAttribute
49
+ {
50
+ override readonly [XPathNodeKindKey] = 'attribute';
51
+
52
+ protected readonly state: SharedNodeState<AttributeStateSpec>;
53
+ protected readonly engineState: EngineState<AttributeStateSpec>;
54
+ readonly validationState: NullValidationState;
55
+
56
+ readonly nodeType = 'attribute';
57
+ readonly currentState: CurrentState<AttributeStateSpec>;
58
+ override readonly instanceState: InstanceState;
59
+
60
+ readonly appearances = null;
61
+ readonly nodeOptions = null;
62
+
63
+ readonly valueType: string;
64
+ readonly decodeInstanceValue: DecodeInstanceValue;
65
+
66
+ protected readonly getInstanceValue: Accessor<string>;
67
+ protected readonly valueState: RuntimeValueState<RuntimeValue<'string'>>;
68
+ protected readonly setValueState: RuntimeValueSetter<RuntimeInputValue<'string'>>;
69
+ readonly evaluator: EngineXPathEvaluator;
70
+ readonly getActiveLanguage: Accessor<ActiveLanguage>;
71
+
72
+ override readonly root: Root;
73
+
74
+ readonly isRelevant: Accessor<boolean> = () => {
75
+ return this.parent.isRelevant();
76
+ };
77
+
78
+ readonly isAttached: Accessor<boolean> = () => {
79
+ return this.parent.isAttached();
80
+ };
81
+
82
+ readonly isReadonly: Accessor<boolean> = () => {
83
+ return true;
84
+ };
85
+
86
+ readonly hasReadonlyAncestor: Accessor<boolean> = () => {
87
+ const { parent } = this;
88
+ return parent.hasReadonlyAncestor() || parent.isReadonly();
89
+ };
90
+
91
+ readonly hasNonRelevantAncestor: Accessor<boolean> = () => {
92
+ const { parent } = this;
93
+ return parent.hasNonRelevantAncestor() || !parent.isRelevant();
94
+ };
95
+
96
+ constructor(
97
+ parent: AnyParentNode,
98
+ definition: AttributeDefinition,
99
+ override readonly instanceNode: StaticAttribute
100
+ ) {
101
+ const codec = getSharedValueCodec('string');
102
+
103
+ super(parent.instanceConfig, parent, instanceNode, definition, {
104
+ scope: parent.scope,
105
+ computeReference: (): string => '@' + this.definition.qualifiedName.getPrefixedName(),
106
+ });
107
+
108
+ this.root = parent.root;
109
+
110
+ this.getActiveLanguage = parent.getActiveLanguage;
111
+ this.validationState = { violations: [] };
112
+
113
+ this.valueType = 'string';
114
+ this.evaluator = parent.evaluator;
115
+ this.decodeInstanceValue = codec.decodeInstanceValue;
116
+
117
+ const instanceValueState = createAttributeValueState(this);
118
+ const valueState = codec.createRuntimeValueState(instanceValueState);
119
+
120
+ const [getInstanceValue] = instanceValueState;
121
+ const [, setValueState] = valueState;
122
+
123
+ this.getInstanceValue = getInstanceValue;
124
+ this.setValueState = setValueState;
125
+ this.getXPathValue = () => {
126
+ return this.getInstanceValue();
127
+ };
128
+ this.valueState = valueState;
129
+
130
+ const state = createSharedNodeState(
131
+ this.scope,
132
+ {
133
+ reference: this.contextReference,
134
+ readonly: this.isReadonly,
135
+ relevant: this.isRelevant,
136
+ required: () => false,
137
+
138
+ label: null,
139
+ hint: null,
140
+ children: null,
141
+ valueOptions: null,
142
+ value: this.valueState,
143
+ instanceValue: this.getInstanceValue,
144
+ attributes: null,
145
+ },
146
+ this.instanceConfig
147
+ );
148
+
149
+ this.state = state;
150
+ this.engineState = state.engineState;
151
+ this.currentState = state.currentState;
152
+ this.instanceState = createAttributeNodeInstanceState(this);
153
+ }
154
+
155
+ setValue(value: string): Root {
156
+ this.setValueState(value);
157
+
158
+ return this.root;
159
+ }
160
+
161
+ getChildren(): readonly [] {
162
+ return [];
163
+ }
164
+ }
@@ -8,6 +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 {
12
+ createAttributeState,
13
+ type AttributeState,
14
+ } from '../lib/reactivity/createAttributeState.ts';
11
15
  import type { ChildrenState } from '../lib/reactivity/createChildrenState.ts';
12
16
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
13
17
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -20,6 +24,8 @@ import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
20
24
  import { createAggregatedViolations } from '../lib/reactivity/validation/createAggregatedViolations.ts';
21
25
  import type { DescendantNodeSharedStateSpec } from './abstract/DescendantNode.ts';
22
26
  import { DescendantNode } from './abstract/DescendantNode.ts';
27
+ import { buildAttributes } from './attachments/buildAttributes.ts';
28
+ import { Attribute } from './Attribute.ts';
23
29
  import { buildChildren } from './children/buildChildren.ts';
24
30
  import type { GeneralChildNode, GeneralParentNode } from './hierarchy.ts';
25
31
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
@@ -30,6 +36,7 @@ interface GroupStateSpec extends DescendantNodeSharedStateSpec {
30
36
  readonly label: Accessor<TextRange<'label'> | null>;
31
37
  readonly hint: null;
32
38
  readonly children: Accessor<readonly FormNodeID[]>;
39
+ readonly attributes: Accessor<readonly Attribute[]>;
33
40
  readonly valueOptions: null;
34
41
  readonly value: null;
35
42
  }
@@ -43,6 +50,7 @@ export class Group
43
50
  ClientReactiveSerializableParentNode<GeneralChildNode>
44
51
  {
45
52
  private readonly childrenState: ChildrenState<GeneralChildNode>;
53
+ private readonly attributeState: AttributeState;
46
54
 
47
55
  override readonly [XPathNodeKindKey] = 'element';
48
56
 
@@ -68,8 +76,10 @@ export class Group
68
76
  this.appearances = definition.bodyElement?.appearances ?? null;
69
77
 
70
78
  const childrenState = createChildrenState<Group, GeneralChildNode>(this);
79
+ const attributeState = createAttributeState(this.scope);
71
80
 
72
81
  this.childrenState = childrenState;
82
+ this.attributeState = attributeState;
73
83
 
74
84
  const state = createSharedNodeState(
75
85
  this.scope,
@@ -82,6 +92,7 @@ export class Group
82
92
  label: createNodeLabel(this, definition),
83
93
  hint: null,
84
94
  children: childrenState.childIds,
95
+ attributes: attributeState.getAttributes,
85
96
  valueOptions: null,
86
97
  value: null,
87
98
  },
@@ -97,6 +108,7 @@ export class Group
97
108
  );
98
109
 
99
110
  childrenState.setChildren(buildChildren(this));
111
+ attributeState.setAttributes(buildAttributes(this));
100
112
  this.validationState = createAggregatedViolations(this, this.instanceConfig);
101
113
  this.instanceState = createParentNodeInstanceState(this);
102
114
  }
@@ -104,4 +116,8 @@ export class Group
104
116
  getChildren(): readonly GeneralChildNode[] {
105
117
  return this.childrenState.getChildren();
106
118
  }
119
+
120
+ getAttributes(): readonly Attribute[] {
121
+ return this.attributeState.getAttributes();
122
+ }
107
123
  }
@@ -130,6 +130,7 @@ export class InputControl<V extends ValueType = ValueType>
130
130
  label: createNodeLabel(this, definition),
131
131
  hint: createFieldHint(this, definition),
132
132
  children: null,
133
+ attributes: null,
133
134
  valueOptions: null,
134
135
  value: this.valueState,
135
136
  instanceValue: this.getInstanceValue,
@@ -76,6 +76,7 @@ export class ModelValue<V extends ValueType = ValueType>
76
76
  label: null,
77
77
  hint: null,
78
78
  children: null,
79
+ attributes: null,
79
80
  valueOptions: null,
80
81
  value: this.valueState,
81
82
  instanceValue: this.getInstanceValue,
@@ -105,6 +105,7 @@ export class Note<V extends ValueType = ValueType>
105
105
  noteText,
106
106
 
107
107
  children: null,
108
+ attributes: null,
108
109
  valueOptions: null,
109
110
  value: this.valueState,
110
111
  instanceValue: this.getInstanceValue,
@@ -17,6 +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 {
21
+ createAttributeState,
22
+ type AttributeState,
23
+ } from '../lib/reactivity/createAttributeState.ts';
20
24
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
21
25
  import { createTranslationState } from '../lib/reactivity/createTranslationState.ts';
22
26
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -32,7 +36,9 @@ import type { ModelDefinition } from '../parse/model/ModelDefinition.ts';
32
36
  import type { RootDefinition } from '../parse/model/RootDefinition.ts';
33
37
  import type { SecondaryInstancesDefinition } from '../parse/model/SecondaryInstance/SecondaryInstancesDefinition.ts';
34
38
  import { InstanceNode } from './abstract/InstanceNode.ts';
39
+ import { buildAttributes } from './attachments/buildAttributes.ts';
35
40
  import { InstanceAttachmentsState } from './attachments/InstanceAttachmentsState.ts';
41
+ import type { Attribute } from './Attribute.ts';
36
42
  import type { InitialInstanceState } from './input/InitialInstanceState.ts';
37
43
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
38
44
  import type { InstanceConfig } from './internal-api/InstanceConfig.ts';
@@ -71,6 +77,7 @@ interface PrimaryInstanceStateSpec {
71
77
  readonly label: null;
72
78
  readonly hint: null;
73
79
  readonly children: Accessor<readonly FormNodeID[]>;
80
+ readonly attributes: Accessor<readonly Attribute[]>;
74
81
  readonly valueOptions: null;
75
82
  readonly value: null;
76
83
 
@@ -154,6 +161,8 @@ export class PrimaryInstance<
154
161
  readonly evaluator: EngineXPathEvaluator;
155
162
  override readonly contextNode = this;
156
163
 
164
+ private readonly attributeState: AttributeState;
165
+
157
166
  constructor(options: PrimaryInstanceOptions<Mode>) {
158
167
  const { mode, initialState, scope, model, secondaryInstances, config } = options;
159
168
  const { instance: modelInstance } = model;
@@ -193,8 +202,10 @@ export class PrimaryInstance<
193
202
  this.classes = definition.classes;
194
203
 
195
204
  const childrenState = createChildrenState<this, Root>(this);
205
+ const attributeState = createAttributeState(this.scope);
196
206
 
197
207
  this.getChildren = childrenState.getChildren;
208
+ this.attributeState = attributeState;
198
209
 
199
210
  const stateSpec: PrimaryInstanceStateSpec = {
200
211
  activeLanguage: getActiveLanguage,
@@ -207,6 +218,7 @@ export class PrimaryInstance<
207
218
  valueOptions: null,
208
219
  value: null,
209
220
  children: childrenState.childIds,
221
+ attributes: attributeState.getAttributes,
210
222
  };
211
223
 
212
224
  const state = createSharedNodeState(scope, stateSpec, config);
@@ -227,6 +239,7 @@ export class PrimaryInstance<
227
239
  this.instanceState = createPrimaryInstanceState(this);
228
240
 
229
241
  childrenState.setChildren([root]);
242
+ attributeState.setAttributes(buildAttributes(this));
230
243
  setIsAttached(true);
231
244
  }
232
245
 
@@ -279,4 +292,8 @@ export class PrimaryInstance<
279
292
 
280
293
  return Promise.resolve(result);
281
294
  }
295
+
296
+ getAttributes(): readonly Attribute[] {
297
+ return this.attributeState.getAttributes();
298
+ }
282
299
  }
@@ -94,6 +94,7 @@ export class RangeControl<V extends RangeValueType = RangeValueType>
94
94
  label: createNodeLabel(this, definition),
95
95
  hint: createFieldHint(this, definition),
96
96
  children: null,
97
+ attributes: null,
97
98
  valueOptions: null,
98
99
  value: this.valueState,
99
100
  instanceValue: this.getInstanceValue,
@@ -174,6 +174,7 @@ export class RankControl
174
174
  label: createNodeLabel(this, definition),
175
175
  hint: createFieldHint(this, definition),
176
176
  children: null,
177
+ attributes: null,
177
178
  valueOptions,
178
179
  value: valueState,
179
180
  instanceValue: this.getInstanceValue,
@@ -12,6 +12,10 @@ import type { InstanceState } from '../client/serialization/InstanceState.ts';
12
12
  import type { AncestorNodeValidationState } from '../client/validation.ts';
13
13
  import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPathNode.ts';
14
14
  import { createRootInstanceState } from '../lib/client-reactivity/instance-state/createRootInstanceState.ts';
15
+ import {
16
+ createAttributeState,
17
+ type AttributeState,
18
+ } from '../lib/reactivity/createAttributeState.ts';
15
19
  import type { ChildrenState } from '../lib/reactivity/createChildrenState.ts';
16
20
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
17
21
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -24,6 +28,8 @@ import { createAggregatedViolations } from '../lib/reactivity/validation/createA
24
28
  import type { BodyClassList } from '../parse/body/BodyDefinition.ts';
25
29
  import type { RootDefinition } from '../parse/model/RootDefinition.ts';
26
30
  import { DescendantNode } from './abstract/DescendantNode.ts';
31
+ import { buildAttributes } from './attachments/buildAttributes.ts';
32
+ import { Attribute } from './Attribute.ts';
27
33
  import { buildChildren } from './children/buildChildren.ts';
28
34
  import type { GeneralChildNode } from './hierarchy.ts';
29
35
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
@@ -39,6 +45,7 @@ interface RootStateSpec {
39
45
  readonly label: null;
40
46
  readonly hint: null;
41
47
  readonly children: Accessor<readonly FormNodeID[]>;
48
+ readonly attributes: Accessor<readonly Attribute[]>;
42
49
  readonly valueOptions: null;
43
50
  readonly value: null;
44
51
 
@@ -56,6 +63,7 @@ export class Root
56
63
  ClientReactiveSerializableParentNode<GeneralChildNode>
57
64
  {
58
65
  private readonly childrenState: ChildrenState<GeneralChildNode>;
66
+ private readonly attributeState: AttributeState;
59
67
 
60
68
  // XFormsXPathElement
61
69
  override readonly [XPathNodeKindKey] = 'element';
@@ -95,8 +103,10 @@ export class Root
95
103
  this.classes = parent.classes;
96
104
 
97
105
  const childrenState = createChildrenState<Root, GeneralChildNode>(this);
106
+ const attributeState = createAttributeState(this.scope);
98
107
 
99
108
  this.childrenState = childrenState;
109
+ this.attributeState = attributeState;
100
110
  this.languages = parent.languages;
101
111
 
102
112
  const state = createSharedNodeState(
@@ -112,6 +122,7 @@ export class Root
112
122
  valueOptions: null,
113
123
  value: null,
114
124
  children: childrenState.childIds,
125
+ attributes: attributeState.getAttributes,
115
126
  },
116
127
  this.instanceConfig
117
128
  );
@@ -125,6 +136,7 @@ export class Root
125
136
  );
126
137
 
127
138
  childrenState.setChildren(buildChildren(this));
139
+ attributeState.setAttributes(buildAttributes(this));
128
140
  this.validationState = createAggregatedViolations(this, this.instanceConfig);
129
141
  this.instanceState = createRootInstanceState(this);
130
142
  }
@@ -133,6 +145,10 @@ export class Root
133
145
  return this.childrenState.getChildren();
134
146
  }
135
147
 
148
+ getAttributes(): readonly Attribute[] {
149
+ return this.attributeState.getAttributes();
150
+ }
151
+
136
152
  // RootNode
137
153
  setLanguage(language: FormLanguage): Root {
138
154
  this.rootDocument.setLanguage(language);
@@ -153,6 +153,7 @@ export class SelectControl
153
153
  label: createNodeLabel(this, definition),
154
154
  hint: createFieldHint(this, definition),
155
155
  children: null,
156
+ attributes: null,
156
157
  valueOptions,
157
158
  value: valueState,
158
159
  instanceValue: this.getInstanceValue,
@@ -100,6 +100,7 @@ export class TriggerControl
100
100
  label: createNodeLabel(this, definition),
101
101
  hint: createFieldHint(this, definition),
102
102
  children: null,
103
+ attributes: null,
103
104
  valueOptions: null,
104
105
  value: this.valueState,
105
106
  instanceValue: this.getInstanceValue,
@@ -9,6 +9,10 @@ import { UploadValueTypeError } from '../error/UploadValueTypeError.ts';
9
9
  import type { XFormsXPathElement } from '../integration/xpath/adapter/XFormsXPathNode.ts';
10
10
  import type { StaticLeafElement } from '../integration/xpath/static-dom/StaticElement.ts';
11
11
  import { createValueNodeInstanceState } from '../lib/client-reactivity/instance-state/createValueNodeInstanceState.ts';
12
+ import {
13
+ createAttributeState,
14
+ type AttributeState,
15
+ } from '../lib/reactivity/createAttributeState.ts';
12
16
  import { createInstanceAttachment } from '../lib/reactivity/createInstanceAttachment.ts';
13
17
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
14
18
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
@@ -20,6 +24,7 @@ import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
20
24
  import type { SharedValidationState } from '../lib/reactivity/validation/createValidation.ts';
21
25
  import { createValidationState } from '../lib/reactivity/validation/createValidation.ts';
22
26
  import type { UnknownAppearanceDefinition } from '../parse/body/appearance/unknownAppearanceParser.ts';
27
+ import type { Attribute } from './Attribute.ts';
23
28
  import type { Root } from './Root.ts';
24
29
  import type { DescendantNodeStateSpec } from './abstract/DescendantNode.ts';
25
30
  import { DescendantNode } from './abstract/DescendantNode.ts';
@@ -57,6 +62,7 @@ interface UploadControlStateSpec extends DescendantNodeStateSpec<InstanceAttachm
57
62
  readonly label: Accessor<TextRange<'label'> | null>;
58
63
  readonly hint: Accessor<TextRange<'hint'> | null>;
59
64
  readonly children: null;
65
+ readonly attributes: Accessor<readonly Attribute[]>;
60
66
  readonly valueOptions: null;
61
67
  readonly value: SimpleAtomicState<InstanceAttachmentRuntimeValue>;
62
68
  readonly instanceValue: Accessor<InstanceAttachmentFileName>;
@@ -95,6 +101,7 @@ export class UploadControl
95
101
 
96
102
  private readonly validation: SharedValidationState;
97
103
  private readonly instanceAttachment: InstanceAttachment;
104
+ private readonly attributeState: AttributeState;
98
105
 
99
106
  // XFormsXPathElement
100
107
  override readonly [XPathNodeKindKey] = 'element';
@@ -133,6 +140,8 @@ export class UploadControl
133
140
  const instanceAttachment = createInstanceAttachment(this);
134
141
 
135
142
  this.instanceAttachment = instanceAttachment;
143
+ const attributeState = createAttributeState(this.scope);
144
+ this.attributeState = attributeState;
136
145
  this.decodeInstanceValue = instanceAttachment.decodeInstanceValue;
137
146
  this.getXPathValue = instanceAttachment.getInstanceValue;
138
147
 
@@ -149,6 +158,7 @@ export class UploadControl
149
158
  children: null,
150
159
  valueOptions: null,
151
160
  value: instanceAttachment.valueState,
161
+ attributes: attributeState.getAttributes,
152
162
  instanceValue: instanceAttachment.getInstanceValue,
153
163
  },
154
164
  this.instanceConfig
@@ -181,4 +191,8 @@ export class UploadControl
181
191
 
182
192
  return this.root;
183
193
  }
194
+
195
+ getAttributes(): readonly Attribute[] {
196
+ return this.attributeState.getAttributes();
197
+ }
184
198
  }
@@ -167,7 +167,11 @@ export abstract class DescendantNode<
167
167
  return true;
168
168
  }
169
169
  }
170
-
170
+ for (const attr of parent.getAttributes()) {
171
+ if (attr === self) {
172
+ return true;
173
+ }
174
+ }
171
175
  return false;
172
176
  });
173
177
  });