@getodk/xforms-engine 0.14.0 → 0.16.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 (125) hide show
  1. package/dist/client/AttributeNode.d.ts +50 -0
  2. package/dist/client/BaseNode.d.ts +5 -0
  3. package/dist/client/form/FormInstanceConfig.d.ts +15 -0
  4. package/dist/client/index.d.ts +1 -0
  5. package/dist/client/node-types.d.ts +1 -1
  6. package/dist/client/validation.d.ts +7 -1
  7. package/dist/index.js +730 -294
  8. package/dist/index.js.map +1 -1
  9. package/dist/instance/Attribute.d.ts +64 -0
  10. package/dist/instance/Group.d.ts +2 -0
  11. package/dist/instance/InputControl.d.ts +2 -0
  12. package/dist/instance/PrimaryInstance.d.ts +2 -0
  13. package/dist/instance/Root.d.ts +2 -0
  14. package/dist/instance/UploadControl.d.ts +2 -0
  15. package/dist/instance/abstract/InstanceNode.d.ts +5 -2
  16. package/dist/instance/abstract/ValueNode.d.ts +2 -0
  17. package/dist/instance/attachments/buildAttributes.d.ts +7 -0
  18. package/dist/instance/internal-api/AttributeContext.d.ts +35 -0
  19. package/dist/instance/internal-api/InstanceConfig.d.ts +2 -0
  20. package/dist/instance/internal-api/InstanceValueContext.d.ts +6 -0
  21. package/dist/instance/internal-api/serialization/ClientReactiveSerializableAttributeNode.d.ts +15 -0
  22. package/dist/instance/internal-api/serialization/ClientReactiveSerializableParentNode.d.ts +2 -0
  23. package/dist/instance/internal-api/serialization/ClientReactiveSerializableTemplatedNode.d.ts +2 -2
  24. package/dist/instance/internal-api/serialization/ClientReactiveSerializableValueNode.d.ts +4 -0
  25. package/dist/instance/repeat/BaseRepeatRange.d.ts +5 -0
  26. package/dist/instance/repeat/RepeatInstance.d.ts +2 -2
  27. package/dist/integration/xpath/adapter/XFormsXPathNode.d.ts +2 -2
  28. package/dist/lib/client-reactivity/instance-state/createAttributeNodeInstanceState.d.ts +3 -0
  29. package/dist/lib/client-reactivity/instance-state/createTemplatedNodeInstanceState.d.ts +0 -3
  30. package/dist/lib/codecs/items/SingleValueItemCodec.d.ts +1 -1
  31. package/dist/lib/names/NamespaceDeclarationMap.d.ts +1 -1
  32. package/dist/lib/reactivity/createAttributeState.d.ts +16 -0
  33. package/dist/lib/reactivity/createInstanceValueState.d.ts +4 -1
  34. package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +2 -2
  35. package/dist/lib/xml-serialization.d.ts +5 -9
  36. package/dist/parse/XFormDOM.d.ts +4 -1
  37. package/dist/parse/expression/ActionComputationExpression.d.ts +4 -0
  38. package/dist/parse/model/ActionDefinition.d.ts +15 -0
  39. package/dist/parse/model/AttributeDefinition.d.ts +24 -0
  40. package/dist/parse/model/{RootAttributeMap.d.ts → AttributeDefinitionMap.d.ts} +4 -10
  41. package/dist/parse/model/BindPreloadDefinition.d.ts +6 -10
  42. package/dist/parse/model/Event.d.ts +8 -0
  43. package/dist/parse/model/GroupDefinition.d.ts +4 -1
  44. package/dist/parse/model/LeafNodeDefinition.d.ts +5 -1
  45. package/dist/parse/model/ModelActionMap.d.ts +9 -0
  46. package/dist/parse/model/ModelDefinition.d.ts +8 -1
  47. package/dist/parse/model/NodeDefinition.d.ts +8 -3
  48. package/dist/parse/model/NoteNodeDefinition.d.ts +3 -2
  49. package/dist/parse/model/RangeNodeDefinition.d.ts +2 -1
  50. package/dist/parse/model/RepeatDefinition.d.ts +4 -1
  51. package/dist/parse/model/RootDefinition.d.ts +3 -2
  52. package/dist/solid.js +730 -294
  53. package/dist/solid.js.map +1 -1
  54. package/package.json +21 -17
  55. package/src/client/AttributeNode.ts +59 -0
  56. package/src/client/BaseNode.ts +6 -0
  57. package/src/client/form/FormInstanceConfig.ts +17 -0
  58. package/src/client/index.ts +1 -0
  59. package/src/client/node-types.ts +1 -0
  60. package/src/client/validation.ts +9 -1
  61. package/src/entrypoints/FormInstance.ts +1 -0
  62. package/src/instance/Attribute.ts +164 -0
  63. package/src/instance/Group.ts +7 -0
  64. package/src/instance/InputControl.ts +8 -0
  65. package/src/instance/ModelValue.ts +7 -0
  66. package/src/instance/Note.ts +6 -0
  67. package/src/instance/PrimaryInstance.ts +7 -0
  68. package/src/instance/RangeControl.ts +6 -0
  69. package/src/instance/RankControl.ts +7 -0
  70. package/src/instance/Root.ts +7 -0
  71. package/src/instance/SelectControl.ts +6 -0
  72. package/src/instance/TriggerControl.ts +6 -0
  73. package/src/instance/UploadControl.ts +5 -0
  74. package/src/instance/abstract/DescendantNode.ts +0 -1
  75. package/src/instance/abstract/InstanceNode.ts +4 -1
  76. package/src/instance/abstract/ValueNode.ts +2 -0
  77. package/src/instance/attachments/buildAttributes.ts +15 -0
  78. package/src/instance/children/normalizeChildInitOptions.ts +1 -1
  79. package/src/instance/internal-api/AttributeContext.ts +40 -0
  80. package/src/instance/internal-api/InstanceConfig.ts +6 -1
  81. package/src/instance/internal-api/InstanceValueContext.ts +6 -0
  82. package/src/instance/internal-api/serialization/ClientReactiveSerializableAttributeNode.ts +18 -0
  83. package/src/instance/internal-api/serialization/ClientReactiveSerializableParentNode.ts +2 -0
  84. package/src/instance/internal-api/serialization/ClientReactiveSerializableTemplatedNode.ts +2 -3
  85. package/src/instance/internal-api/serialization/ClientReactiveSerializableValueNode.ts +4 -0
  86. package/src/instance/repeat/BaseRepeatRange.ts +14 -0
  87. package/src/instance/repeat/RepeatInstance.ts +5 -5
  88. package/src/integration/xpath/adapter/XFormsXPathNode.ts +3 -1
  89. package/src/lib/client-reactivity/instance-state/createAttributeNodeInstanceState.ts +16 -0
  90. package/src/lib/client-reactivity/instance-state/createParentNodeInstanceState.ts +5 -5
  91. package/src/lib/client-reactivity/instance-state/createRootInstanceState.ts +6 -9
  92. package/src/lib/client-reactivity/instance-state/createTemplatedNodeInstanceState.ts +5 -15
  93. package/src/lib/client-reactivity/instance-state/createValueNodeInstanceState.ts +2 -1
  94. package/src/lib/client-reactivity/instance-state/prepareInstancePayload.ts +1 -0
  95. package/src/lib/codecs/NoteCodec.ts +1 -1
  96. package/src/lib/codecs/items/SingleValueItemCodec.ts +1 -3
  97. package/src/lib/names/NamespaceDeclarationMap.ts +1 -1
  98. package/src/lib/reactivity/createAttributeState.ts +51 -0
  99. package/src/lib/reactivity/createInstanceValueState.ts +152 -53
  100. package/src/lib/reactivity/node-state/createSharedNodeState.ts +2 -2
  101. package/src/lib/xml-serialization.ts +38 -34
  102. package/src/parse/XFormDOM.ts +9 -0
  103. package/src/parse/body/GroupElementDefinition.ts +1 -1
  104. package/src/parse/body/control/InputControlDefinition.ts +1 -1
  105. package/src/parse/expression/ActionComputationExpression.ts +12 -0
  106. package/src/parse/model/ActionDefinition.ts +70 -0
  107. package/src/parse/model/AttributeDefinition.ts +59 -0
  108. package/src/parse/model/{RootAttributeMap.ts → AttributeDefinitionMap.ts} +7 -13
  109. package/src/parse/model/BindDefinition.ts +1 -6
  110. package/src/parse/model/BindPreloadDefinition.ts +44 -12
  111. package/src/parse/model/Event.ts +9 -0
  112. package/src/parse/model/GroupDefinition.ts +6 -0
  113. package/src/parse/model/LeafNodeDefinition.ts +5 -0
  114. package/src/parse/model/ModelActionMap.ts +37 -0
  115. package/src/parse/model/ModelDefinition.ts +18 -3
  116. package/src/parse/model/NodeDefinition.ts +11 -3
  117. package/src/parse/model/NoteNodeDefinition.ts +5 -2
  118. package/src/parse/model/RangeNodeDefinition.ts +5 -2
  119. package/src/parse/model/RepeatDefinition.ts +8 -1
  120. package/src/parse/model/RootDefinition.ts +27 -9
  121. package/src/parse/model/nodeDefinitionMap.ts +1 -1
  122. package/dist/error/TemplatedNodeAttributeSerializationError.d.ts +0 -22
  123. package/dist/parse/model/RootAttributeDefinition.d.ts +0 -21
  124. package/src/error/TemplatedNodeAttributeSerializationError.ts +0 -24
  125. 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.16.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "XForms engine for ODK Web Forms",
6
6
  "type": "module",
@@ -29,20 +29,20 @@
29
29
  "README.md"
30
30
  ],
31
31
  "engines": {
32
- "node": "^20.19.3 || ^22.12.0 || ^24.3.0",
33
- "yarn": "1.22.22"
32
+ "node": "^20.19.3 || ^22.12.0 || ^24.11.0",
33
+ "yarn": "4.11.0"
34
34
  },
35
35
  "scripts": {
36
- "build": "npm-run-all -nl build:*",
36
+ "build": "npm-run-all -nl 'build:*'",
37
37
  "build:clean": "rimraf dist/",
38
38
  "build:js": "vite build",
39
- "build:solid": "export VITE_BUILD_TARGET=solid && vite build",
39
+ "build:solid": "VITE_BUILD_TARGET=solid vite build",
40
40
  "dev": "vite",
41
- "docs": "npm-run-all -nl docs:*",
41
+ "docs": "npm-run-all -nl 'docs:*'",
42
42
  "docs:clean": "rimraf api-docs/",
43
43
  "docs:api": "typedoc --readme none --out api-docs --entryPoints \"src/**/*\"",
44
44
  "docs:serve": "http-server api-docs -o modules/client",
45
- "test": "npm-run-all --print-name --print-label test-node:* test-browser:*",
45
+ "test": "npm-run-all -nl 'test-node:*' 'test-browser:*'",
46
46
  "test-node:jsdom": "vitest run",
47
47
  "test-browser:chromium": "BROWSER_NAME=chromium vitest run",
48
48
  "test-browser:firefox": "BROWSER_NAME=firefox vitest run",
@@ -57,24 +57,28 @@
57
57
  "bin-packer": "1.7.0",
58
58
  "mdast-util-from-markdown": "^2.0.2",
59
59
  "papaparse": "^5.5.3",
60
- "solid-js": "^1.9.7",
60
+ "solid-js": "^1.9.10",
61
61
  "temporal-polyfill": "^0.3.0"
62
62
  },
63
63
  "devDependencies": {
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",
68
- "@types/papaparse": "^5.3.16",
64
+ "@babel/core": "^7.28.5",
65
+ "@getodk/tree-sitter-xpath": "0.2.2",
66
+ "@getodk/xpath": "0.9.1",
67
+ "@playwright/test": "^1.57.0",
68
+ "@types/papaparse": "^5.5.0",
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
- "jsdom": "^26.1.0",
73
- "typedoc": "^0.28.7",
74
- "vite": "^7.0.3",
72
+ "jsdom": "^27.2.0",
73
+ "npm-run-all2": "^8.0.4",
74
+ "rimraf": "^6.0.1",
75
+ "typedoc": "^0.28.14",
76
+ "typescript": "~5.9.3",
77
+ "vite": "^7.2.4",
75
78
  "vite-plugin-dts": "^4.5.4",
76
79
  "vite-plugin-no-bundle": "^4.0.0",
77
- "vitest": "^3.2.4"
80
+ "vitest": "^3.2.4",
81
+ "web-tree-sitter": "0.24.5"
78
82
  },
79
83
  "peerDependencies": {
80
84
  "solid-js": "^1.9.7"
@@ -0,0 +1,59 @@
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
+ get relevant(): boolean;
9
+ }
10
+
11
+ /**
12
+ * Base interface for common/shared aspects of attributes.
13
+ */
14
+ export interface AttributeNode {
15
+ /**
16
+ * Specifies the node's general type. This can be useful for narrowing types,
17
+ * e.g. those of children.
18
+ */
19
+ readonly nodeType: 'attribute';
20
+
21
+ /**
22
+ * Each node has a definition which specifies aspects of the node defined in
23
+ * the form. These aspects include (but are not limited to) the node's data
24
+ * type, its body presentation constraints (if any), its bound nodeset, and
25
+ * so on...
26
+ */
27
+ readonly definition: AttributeDefinition;
28
+
29
+ /**
30
+ * Each node links back to the node representing the root of the form.
31
+ */
32
+ readonly root: Root;
33
+
34
+ readonly owner: unknown;
35
+
36
+ /**
37
+ * Each node provides a discrete object representing the stateful aspects of
38
+ * that node which will change over time. This includes state which is either
39
+ * client-/user-mutable, or state which is computed based on the core XForms
40
+ * computation model. Each node also exposes {@link validationState}, which
41
+ * reflects the validity of the node, or its descendants.
42
+ *
43
+ * When a client provides a {@link OpaqueReactiveObjectFactory}, the engine
44
+ * will update the properties of this object as their respective states
45
+ * change, so a client can implement reactive updates that respond to changes
46
+ * as they occur.
47
+ */
48
+ readonly currentState: AttributeNodeState;
49
+
50
+ /**
51
+ * Represents the current instance state of the node.
52
+ *
53
+ * @see {@link InstanceState.instanceXML} for additional detail.
54
+ */
55
+ readonly instanceState: InstanceState;
56
+
57
+ readonly appearances: null;
58
+ readonly nodeOptions: null;
59
+ }
@@ -1,3 +1,4 @@
1
+ import type { Attribute } from '../instance/Attribute.ts';
1
2
  import type { TokenListParser } from '../lib/TokenListParser.ts';
2
3
  import type { AnyNodeDefinition } from '../parse/model/NodeDefinition.ts';
3
4
  import type { NodeAppearances } from './NodeAppearances.ts';
@@ -129,6 +130,11 @@ export interface BaseNodeState {
129
130
  * is an internal engine consideration.
130
131
  */
131
132
  get value(): unknown;
133
+
134
+ /**
135
+ * Nodes can own attributes, which have a literal or reference value.
136
+ */
137
+ get attributes(): readonly Attribute[];
132
138
  }
133
139
 
134
140
  /**
@@ -1,6 +1,21 @@
1
1
  import type { InstanceAttachmentsConfig } from '../attachments/InstanceAttachmentsConfig.ts';
2
2
  import type { OpaqueReactiveObjectFactory } from '../OpaqueReactiveObjectFactory.ts';
3
3
 
4
+ /**
5
+ * @see https://getodk.github.io/xforms-spec/#preload-attributes
6
+ */
7
+ export interface PreloadProperties {
8
+ /**
9
+ * The unique identifier for this device. If not provided, then an identifier will be
10
+ * generated during the first page load and stored in localstorage and reused for
11
+ * subsequent form loads.
12
+ */
13
+ readonly deviceID?: string;
14
+ readonly email?: string;
15
+ readonly username?: string;
16
+ readonly phoneNumber?: string;
17
+ }
18
+
4
19
  export interface FormInstanceConfig {
5
20
  /**
6
21
  * A client may specify a generic function for constructing stateful objects.
@@ -18,4 +33,6 @@ export interface FormInstanceConfig {
18
33
  readonly stateFactory?: OpaqueReactiveObjectFactory;
19
34
 
20
35
  readonly instanceAttachments?: InstanceAttachmentsConfig;
36
+
37
+ readonly preloadProperties?: PreloadProperties;
21
38
  }
@@ -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; // eslint-disable-line @typescript-eslint/no-redundant-type-constituents
@@ -41,6 +41,7 @@ export class FormInstance<Mode extends FormInstanceInitializationMode>
41
41
  const config: InstanceConfig = {
42
42
  clientStateFactory: instanceConfig.stateFactory ?? identity,
43
43
  computeAttachmentName: instanceConfig.instanceAttachments?.fileNameFactory ?? (() => null),
44
+ preloadProperties: instanceConfig.preloadProperties ?? {},
44
45
  };
45
46
  const primaryInstanceOptions: PrimaryInstanceOptions<Mode> = {
46
47
  ...options.instanceOptions,
@@ -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 { createInstanceValueState } from '../lib/reactivity/createInstanceValueState.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 { ReactiveScope } from '../lib/reactivity/scope.ts';
23
+ import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
24
+ import type { AttributeDefinition } from '../parse/model/AttributeDefinition.ts';
25
+ import type { AnyChildNode, AnyNode } from './hierarchy.ts';
26
+ import type { AttributeContext } from './internal-api/AttributeContext.ts';
27
+ import type { InstanceConfig } from './internal-api/InstanceConfig.ts';
28
+ import type { DecodeInstanceValue } from './internal-api/InstanceValueContext.ts';
29
+ import type { ClientReactiveSerializableAttributeNode } from './internal-api/serialization/ClientReactiveSerializableAttributeNode.ts';
30
+ import type { PrimaryInstance } from './PrimaryInstance.ts';
31
+ import type { Root } from './Root.ts';
32
+
33
+ export interface AttributeStateSpec {
34
+ readonly value: SimpleAtomicState<string>;
35
+ readonly instanceValue: Accessor<string>;
36
+ readonly relevant: Accessor<boolean>;
37
+ }
38
+
39
+ export class Attribute
40
+ implements
41
+ AttributeNode,
42
+ ClientReactiveSerializableAttributeNode,
43
+ AttributeContext,
44
+ XFormsXPathAttribute
45
+ {
46
+ readonly [XPathNodeKindKey] = 'attribute';
47
+
48
+ protected readonly state: SharedNodeState<AttributeStateSpec>;
49
+ protected readonly engineState: EngineState<AttributeStateSpec>;
50
+ readonly validationState: NullValidationState;
51
+
52
+ readonly nodeType = 'attribute';
53
+ readonly currentState: CurrentState<AttributeStateSpec>;
54
+ readonly instanceState: InstanceState;
55
+
56
+ readonly appearances = null;
57
+ readonly nodeOptions = null;
58
+
59
+ readonly valueType: string;
60
+ readonly decodeInstanceValue: DecodeInstanceValue;
61
+
62
+ protected readonly getInstanceValue: Accessor<string>;
63
+ protected readonly valueState: RuntimeValueState<RuntimeValue<'string'>>;
64
+ 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;
73
+
74
+ readonly isRelevant: Accessor<boolean> = () => {
75
+ return this.owner.isRelevant();
76
+ };
77
+
78
+ readonly isAttached: Accessor<boolean> = () => {
79
+ return this.owner.isAttached();
80
+ };
81
+
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
+ };
99
+
100
+ constructor(
101
+ readonly owner: AnyNode,
102
+ readonly definition: AttributeDefinition,
103
+ readonly instanceNode: StaticAttribute
104
+ ) {
105
+ const codec = getSharedValueCodec('string');
106
+
107
+ this.contextNode = owner;
108
+ this.scope = owner.scope;
109
+ this.rootDocument = owner.rootDocument;
110
+
111
+ this.root = owner.root;
112
+ this.instanceConfig = owner.instanceConfig;
113
+
114
+ this.getActiveLanguage = owner.getActiveLanguage;
115
+ this.validationState = { violations: [] };
116
+
117
+ this.valueType = 'string';
118
+ this.evaluator = owner.evaluator;
119
+ this.decodeInstanceValue = codec.decodeInstanceValue;
120
+
121
+ const instanceValueState = createInstanceValueState(this);
122
+ const valueState = codec.createRuntimeValueState(instanceValueState);
123
+
124
+ const [getInstanceValue] = instanceValueState;
125
+ const [, setValueState] = valueState;
126
+
127
+ this.getInstanceValue = getInstanceValue;
128
+ this.setValueState = setValueState;
129
+ this.valueState = valueState;
130
+
131
+ const state = createSharedNodeState(
132
+ owner.scope,
133
+ {
134
+ value: this.valueState,
135
+ instanceValue: this.getInstanceValue,
136
+ relevant: this.owner.isRelevant,
137
+ },
138
+ this.instanceConfig
139
+ );
140
+
141
+ this.state = state;
142
+ this.engineState = state.engineState;
143
+ this.currentState = state.currentState;
144
+ this.instanceState = createAttributeNodeInstanceState(this);
145
+ }
146
+
147
+ setValue(value: string): Root {
148
+ this.setValueState(value);
149
+
150
+ return this.root;
151
+ }
152
+
153
+ getChildren(): readonly [] {
154
+ return [];
155
+ }
156
+
157
+ getXPathChildNodes(): readonly AnyChildNode[] {
158
+ return [];
159
+ }
160
+
161
+ getXPathValue(): string {
162
+ return '';
163
+ }
164
+ }
@@ -8,6 +8,7 @@ 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
12
  import type { ChildrenState } from '../lib/reactivity/createChildrenState.ts';
12
13
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
13
14
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -20,6 +21,8 @@ import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
20
21
  import { createAggregatedViolations } from '../lib/reactivity/validation/createAggregatedViolations.ts';
21
22
  import type { DescendantNodeSharedStateSpec } from './abstract/DescendantNode.ts';
22
23
  import { DescendantNode } from './abstract/DescendantNode.ts';
24
+ import { buildAttributes } from './attachments/buildAttributes.ts';
25
+ import { Attribute } from './Attribute.ts';
23
26
  import { buildChildren } from './children/buildChildren.ts';
24
27
  import type { GeneralChildNode, GeneralParentNode } from './hierarchy.ts';
25
28
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
@@ -30,6 +33,7 @@ interface GroupStateSpec extends DescendantNodeSharedStateSpec {
30
33
  readonly label: Accessor<TextRange<'label'> | null>;
31
34
  readonly hint: null;
32
35
  readonly children: Accessor<readonly FormNodeID[]>;
36
+ readonly attributes: Accessor<readonly Attribute[]>;
33
37
  readonly valueOptions: null;
34
38
  readonly value: null;
35
39
  }
@@ -68,6 +72,7 @@ export class Group
68
72
  this.appearances = definition.bodyElement?.appearances ?? null;
69
73
 
70
74
  const childrenState = createChildrenState<Group, GeneralChildNode>(this);
75
+ const attributeState = createAttributeState(this.scope);
71
76
 
72
77
  this.childrenState = childrenState;
73
78
 
@@ -82,6 +87,7 @@ export class Group
82
87
  label: createNodeLabel(this, definition),
83
88
  hint: null,
84
89
  children: childrenState.childIds,
90
+ attributes: attributeState.getAttributes,
85
91
  valueOptions: null,
86
92
  value: null,
87
93
  },
@@ -97,6 +103,7 @@ export class Group
97
103
  );
98
104
 
99
105
  childrenState.setChildren(buildChildren(this));
106
+ attributeState.setAttributes(buildAttributes(this));
100
107
  this.validationState = createAggregatedViolations(this, this.instanceConfig);
101
108
  this.instanceState = createParentNodeInstanceState(this);
102
109
  }
@@ -13,6 +13,7 @@ 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
17
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
17
18
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
18
19
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
@@ -21,6 +22,8 @@ import { createFieldHint } from '../lib/reactivity/text/createFieldHint.ts';
21
22
  import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
22
23
  import type { InputControlDefinition } from '../parse/body/control/InputControlDefinition.ts';
23
24
  import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
25
+ import { buildAttributes } from './attachments/buildAttributes.ts';
26
+ import type { Attribute } from './Attribute.ts';
24
27
  import type { GeneralParentNode } from './hierarchy.ts';
25
28
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
26
29
  import type { ClientReactiveSerializableValueNode } from './internal-api/serialization/ClientReactiveSerializableValueNode.ts';
@@ -69,6 +72,7 @@ const nodeOptionsFactoryByType: NodeOptionsFactoryByType = {
69
72
  interface InputControlStateSpec<V extends ValueType> extends ValueNodeStateSpec<RuntimeValue<V>> {
70
73
  readonly label: Accessor<TextRange<'label'> | null>;
71
74
  readonly hint: Accessor<TextRange<'hint'> | null>;
75
+ readonly attributes: Accessor<readonly Attribute[]>;
72
76
  readonly valueOptions: null;
73
77
  }
74
78
 
@@ -118,6 +122,7 @@ export class InputControl<V extends ValueType = ValueType>
118
122
 
119
123
  this.appearances = definition.bodyElement.appearances;
120
124
  this.nodeOptions = nodeOptionsFactoryByType[definition.valueType](definition.bodyElement);
125
+ const attributeState = createAttributeState(this.scope);
121
126
 
122
127
  const state = createSharedNodeState(
123
128
  this.scope,
@@ -130,6 +135,7 @@ export class InputControl<V extends ValueType = ValueType>
130
135
  label: createNodeLabel(this, definition),
131
136
  hint: createFieldHint(this, definition),
132
137
  children: null,
138
+ attributes: attributeState.getAttributes,
133
139
  valueOptions: null,
134
140
  value: this.valueState,
135
141
  instanceValue: this.getInstanceValue,
@@ -137,6 +143,8 @@ export class InputControl<V extends ValueType = ValueType>
137
143
  this.instanceConfig
138
144
  );
139
145
 
146
+ attributeState.setAttributes(buildAttributes(this));
147
+
140
148
  this.state = state;
141
149
  this.engineState = state.engineState;
142
150
  this.currentState = state.currentState;
@@ -5,11 +5,13 @@ 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
9
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
9
10
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
10
11
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
11
12
  import { createSharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
12
13
  import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
14
+ import { buildAttributes } from './attachments/buildAttributes.ts';
13
15
  import type { GeneralParentNode } from './hierarchy.ts';
14
16
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
15
17
  import type { ValidationContext } from './internal-api/ValidationContext.ts';
@@ -65,6 +67,8 @@ export class ModelValue<V extends ValueType = ValueType>
65
67
 
66
68
  super(parent, instanceNode, definition, codec);
67
69
 
70
+ const attributeState = createAttributeState(this.scope);
71
+
68
72
  const state = createSharedNodeState(
69
73
  this.scope,
70
74
  {
@@ -76,6 +80,7 @@ export class ModelValue<V extends ValueType = ValueType>
76
80
  label: null,
77
81
  hint: null,
78
82
  children: null,
83
+ attributes: attributeState.getAttributes,
79
84
  valueOptions: null,
80
85
  value: this.valueState,
81
86
  instanceValue: this.getInstanceValue,
@@ -83,6 +88,8 @@ export class ModelValue<V extends ValueType = ValueType>
83
88
  this.instanceConfig
84
89
  );
85
90
 
91
+ attributeState.setAttributes(buildAttributes(this));
92
+
86
93
  this.state = state;
87
94
  this.engineState = state.engineState;
88
95
  this.currentState = state.currentState;
@@ -8,6 +8,7 @@ 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
12
  import { createNoteReadonlyThunk } from '../lib/reactivity/createNoteReadonlyThunk.ts';
12
13
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
13
14
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
@@ -18,6 +19,7 @@ import { createNodeLabel } from '../lib/reactivity/text/createNodeLabel.ts';
18
19
  import { createNoteText, type ComputedNoteText } from '../lib/reactivity/text/createNoteText.ts';
19
20
  import type { NoteNodeDefinition } from '../parse/model/NoteNodeDefinition.ts';
20
21
  import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
22
+ import { buildAttributes } from './attachments/buildAttributes.ts';
21
23
  import type { GeneralParentNode } from './hierarchy.ts';
22
24
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
23
25
  import type { ClientReactiveSerializableValueNode } from './internal-api/serialization/ClientReactiveSerializableValueNode.ts';
@@ -66,6 +68,7 @@ export class Note<V extends ValueType = ValueType>
66
68
 
67
69
  const isReadonly = createNoteReadonlyThunk(this, definition);
68
70
  const noteTextComputation = createNoteText(this, definition.noteTextDefinition);
71
+ const attributeState = createAttributeState(this.scope);
69
72
 
70
73
  let noteText: ComputedNoteText;
71
74
  let label: Accessor<TextRange<'label', 'form'> | null>;
@@ -105,6 +108,7 @@ export class Note<V extends ValueType = ValueType>
105
108
  noteText,
106
109
 
107
110
  children: null,
111
+ attributes: attributeState.getAttributes,
108
112
  valueOptions: null,
109
113
  value: this.valueState,
110
114
  instanceValue: this.getInstanceValue,
@@ -112,6 +116,8 @@ export class Note<V extends ValueType = ValueType>
112
116
  this.instanceConfig
113
117
  );
114
118
 
119
+ attributeState.setAttributes(buildAttributes(this));
120
+
115
121
  this.state = state;
116
122
  this.engineState = state.engineState;
117
123
  this.currentState = state.currentState;
@@ -17,6 +17,7 @@ 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
21
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
21
22
  import { createTranslationState } from '../lib/reactivity/createTranslationState.ts';
22
23
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -32,7 +33,9 @@ import type { ModelDefinition } from '../parse/model/ModelDefinition.ts';
32
33
  import type { RootDefinition } from '../parse/model/RootDefinition.ts';
33
34
  import type { SecondaryInstancesDefinition } from '../parse/model/SecondaryInstance/SecondaryInstancesDefinition.ts';
34
35
  import { InstanceNode } from './abstract/InstanceNode.ts';
36
+ import { buildAttributes } from './attachments/buildAttributes.ts';
35
37
  import { InstanceAttachmentsState } from './attachments/InstanceAttachmentsState.ts';
38
+ import type { Attribute } from './Attribute.ts';
36
39
  import type { InitialInstanceState } from './input/InitialInstanceState.ts';
37
40
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
38
41
  import type { InstanceConfig } from './internal-api/InstanceConfig.ts';
@@ -71,6 +74,7 @@ interface PrimaryInstanceStateSpec {
71
74
  readonly label: null;
72
75
  readonly hint: null;
73
76
  readonly children: Accessor<readonly FormNodeID[]>;
77
+ readonly attributes: Accessor<readonly Attribute[]>;
74
78
  readonly valueOptions: null;
75
79
  readonly value: null;
76
80
 
@@ -193,6 +197,7 @@ export class PrimaryInstance<
193
197
  this.classes = definition.classes;
194
198
 
195
199
  const childrenState = createChildrenState<this, Root>(this);
200
+ const attributeState = createAttributeState(this.scope);
196
201
 
197
202
  this.getChildren = childrenState.getChildren;
198
203
 
@@ -207,6 +212,7 @@ export class PrimaryInstance<
207
212
  valueOptions: null,
208
213
  value: null,
209
214
  children: childrenState.childIds,
215
+ attributes: attributeState.getAttributes,
210
216
  };
211
217
 
212
218
  const state = createSharedNodeState(scope, stateSpec, config);
@@ -227,6 +233,7 @@ export class PrimaryInstance<
227
233
  this.instanceState = createPrimaryInstanceState(this);
228
234
 
229
235
  childrenState.setChildren([root]);
236
+ attributeState.setAttributes(buildAttributes(this));
230
237
  setIsAttached(true);
231
238
  }
232
239
 
@@ -11,6 +11,7 @@ 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
15
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
15
16
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
16
17
  import type { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
@@ -24,6 +25,7 @@ import type {
24
25
  } from '../parse/model/RangeNodeDefinition.ts';
25
26
  import type { Root } from './Root.ts';
26
27
  import { ValueNode, type ValueNodeStateSpec } from './abstract/ValueNode.ts';
28
+ import { buildAttributes } from './attachments/buildAttributes.ts';
27
29
  import type { GeneralParentNode } from './hierarchy.ts';
28
30
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
29
31
  import type { ValidationContext } from './internal-api/ValidationContext.ts';
@@ -82,6 +84,7 @@ export class RangeControl<V extends RangeValueType = RangeValueType>
82
84
  super(parent, instanceNode, definition, codec);
83
85
 
84
86
  this.appearances = definition.bodyElement.appearances;
87
+ const attributeState = createAttributeState(this.scope);
85
88
 
86
89
  const state = createSharedNodeState(
87
90
  this.scope,
@@ -94,6 +97,7 @@ export class RangeControl<V extends RangeValueType = RangeValueType>
94
97
  label: createNodeLabel(this, definition),
95
98
  hint: createFieldHint(this, definition),
96
99
  children: null,
100
+ attributes: attributeState.getAttributes,
97
101
  valueOptions: null,
98
102
  value: this.valueState,
99
103
  instanceValue: this.getInstanceValue,
@@ -101,6 +105,8 @@ export class RangeControl<V extends RangeValueType = RangeValueType>
101
105
  this.instanceConfig
102
106
  );
103
107
 
108
+ attributeState.setAttributes(buildAttributes(this));
109
+
104
110
  this.state = state;
105
111
  this.engineState = state.engineState;
106
112
  this.currentState = state.currentState;