@getodk/xforms-engine 0.15.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 (94) hide show
  1. package/dist/client/AttributeNode.d.ts +2 -5
  2. package/dist/client/BaseNode.d.ts +5 -0
  3. package/dist/client/form/FormInstanceConfig.d.ts +15 -0
  4. package/dist/index.js +395 -231
  5. package/dist/index.js.map +1 -1
  6. package/dist/instance/Attribute.d.ts +17 -11
  7. package/dist/instance/Group.d.ts +0 -2
  8. package/dist/instance/InputControl.d.ts +2 -0
  9. package/dist/instance/PrimaryInstance.d.ts +0 -2
  10. package/dist/instance/Root.d.ts +0 -2
  11. package/dist/instance/UploadControl.d.ts +0 -2
  12. package/dist/instance/abstract/InstanceNode.d.ts +2 -2
  13. package/dist/instance/abstract/ValueNode.d.ts +2 -1
  14. package/dist/instance/attachments/buildAttributes.d.ts +6 -2
  15. package/dist/instance/hierarchy.d.ts +1 -2
  16. package/dist/instance/internal-api/AttributeContext.d.ts +6 -0
  17. package/dist/instance/internal-api/InstanceConfig.d.ts +2 -0
  18. package/dist/instance/internal-api/InstanceValueContext.d.ts +6 -0
  19. package/dist/instance/internal-api/serialization/ClientReactiveSerializableAttributeNode.d.ts +0 -1
  20. package/dist/instance/internal-api/serialization/ClientReactiveSerializableValueNode.d.ts +4 -0
  21. package/dist/instance/repeat/RepeatInstance.d.ts +0 -2
  22. package/dist/lib/codecs/items/SingleValueItemCodec.d.ts +1 -1
  23. package/dist/lib/reactivity/createInstanceValueState.d.ts +4 -1
  24. package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +2 -2
  25. package/dist/lib/xml-serialization.d.ts +1 -1
  26. package/dist/parse/XFormDOM.d.ts +3 -0
  27. package/dist/parse/expression/ActionComputationExpression.d.ts +4 -0
  28. package/dist/parse/model/ActionDefinition.d.ts +15 -0
  29. package/dist/parse/model/AttributeDefinition.d.ts +3 -1
  30. package/dist/parse/model/BindPreloadDefinition.d.ts +6 -10
  31. package/dist/parse/model/Event.d.ts +8 -0
  32. package/dist/parse/model/LeafNodeDefinition.d.ts +5 -2
  33. package/dist/parse/model/ModelActionMap.d.ts +9 -0
  34. package/dist/parse/model/ModelDefinition.d.ts +8 -1
  35. package/dist/parse/model/NoteNodeDefinition.d.ts +3 -2
  36. package/dist/parse/model/RangeNodeDefinition.d.ts +2 -1
  37. package/dist/parse/model/RootDefinition.d.ts +1 -0
  38. package/dist/solid.js +395 -231
  39. package/dist/solid.js.map +1 -1
  40. package/package.json +21 -17
  41. package/src/client/AttributeNode.ts +2 -5
  42. package/src/client/BaseNode.ts +6 -0
  43. package/src/client/form/FormInstanceConfig.ts +17 -0
  44. package/src/client/validation.ts +1 -1
  45. package/src/entrypoints/FormInstance.ts +1 -0
  46. package/src/instance/Attribute.ts +45 -45
  47. package/src/instance/Group.ts +1 -10
  48. package/src/instance/InputControl.ts +8 -1
  49. package/src/instance/ModelValue.ts +7 -1
  50. package/src/instance/Note.ts +6 -1
  51. package/src/instance/PrimaryInstance.ts +1 -11
  52. package/src/instance/RangeControl.ts +6 -1
  53. package/src/instance/RankControl.ts +7 -1
  54. package/src/instance/Root.ts +1 -10
  55. package/src/instance/SelectControl.ts +6 -1
  56. package/src/instance/TriggerControl.ts +6 -1
  57. package/src/instance/UploadControl.ts +1 -10
  58. package/src/instance/abstract/DescendantNode.ts +0 -5
  59. package/src/instance/abstract/InstanceNode.ts +2 -2
  60. package/src/instance/abstract/ValueNode.ts +2 -1
  61. package/src/instance/attachments/buildAttributes.ts +11 -4
  62. package/src/instance/children/normalizeChildInitOptions.ts +1 -1
  63. package/src/instance/hierarchy.ts +1 -3
  64. package/src/instance/internal-api/AttributeContext.ts +6 -0
  65. package/src/instance/internal-api/InstanceConfig.ts +6 -1
  66. package/src/instance/internal-api/InstanceValueContext.ts +6 -0
  67. package/src/instance/internal-api/serialization/ClientReactiveSerializableAttributeNode.ts +0 -1
  68. package/src/instance/internal-api/serialization/ClientReactiveSerializableValueNode.ts +4 -0
  69. package/src/instance/repeat/RepeatInstance.ts +1 -10
  70. package/src/lib/client-reactivity/instance-state/createValueNodeInstanceState.ts +2 -1
  71. package/src/lib/client-reactivity/instance-state/prepareInstancePayload.ts +1 -0
  72. package/src/lib/codecs/NoteCodec.ts +1 -1
  73. package/src/lib/codecs/items/SingleValueItemCodec.ts +1 -3
  74. package/src/lib/reactivity/createInstanceValueState.ts +152 -53
  75. package/src/lib/reactivity/node-state/createSharedNodeState.ts +2 -2
  76. package/src/lib/xml-serialization.ts +9 -1
  77. package/src/parse/XFormDOM.ts +9 -0
  78. package/src/parse/body/GroupElementDefinition.ts +1 -1
  79. package/src/parse/body/control/InputControlDefinition.ts +1 -1
  80. package/src/parse/expression/ActionComputationExpression.ts +12 -0
  81. package/src/parse/model/ActionDefinition.ts +70 -0
  82. package/src/parse/model/AttributeDefinition.ts +3 -2
  83. package/src/parse/model/AttributeDefinitionMap.ts +1 -1
  84. package/src/parse/model/BindDefinition.ts +1 -6
  85. package/src/parse/model/BindPreloadDefinition.ts +44 -12
  86. package/src/parse/model/Event.ts +9 -0
  87. package/src/parse/model/LeafNodeDefinition.ts +5 -1
  88. package/src/parse/model/ModelActionMap.ts +37 -0
  89. package/src/parse/model/ModelDefinition.ts +18 -3
  90. package/src/parse/model/NoteNodeDefinition.ts +5 -2
  91. package/src/parse/model/RangeNodeDefinition.ts +5 -2
  92. package/src/parse/model/RootDefinition.ts +22 -4
  93. package/dist/lib/reactivity/createAttributeValueState.d.ts +0 -15
  94. package/src/lib/reactivity/createAttributeValueState.ts +0 -189
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getodk/xforms-engine",
3
- "version": "0.15.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.1",
66
- "@getodk/xpath": "0.9.0",
67
- "@playwright/test": "^1.55.1",
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.8",
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"
@@ -5,6 +5,7 @@ import type { InstanceState } from './serialization/InstanceState.ts';
5
5
 
6
6
  export interface AttributeNodeState {
7
7
  get value(): string;
8
+ get relevant(): boolean;
8
9
  }
9
10
 
10
11
  /**
@@ -30,11 +31,7 @@ export interface AttributeNode {
30
31
  */
31
32
  readonly root: Root;
32
33
 
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;
34
+ readonly owner: unknown;
38
35
 
39
36
  /**
40
37
  * Each node provides a discrete object representing the stateful aspects of
@@ -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
  }
@@ -204,4 +204,4 @@ export interface NullValidationState {
204
204
  export type NodeValidationState =
205
205
  | AncestorNodeValidationState
206
206
  | LeafNodeValidationState
207
- | NullValidationState;
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,
@@ -12,42 +12,38 @@ import {
12
12
  type RuntimeValue,
13
13
  } from '../lib/codecs/getSharedValueCodec.ts';
14
14
  import type { RuntimeValueSetter, RuntimeValueState } from '../lib/codecs/ValueCodec.ts';
15
- import { createAttributeValueState } from '../lib/reactivity/createAttributeValueState.ts';
15
+ import { createInstanceValueState } from '../lib/reactivity/createInstanceValueState.ts';
16
16
  import type { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
17
17
  import type { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
18
18
  import {
19
19
  createSharedNodeState,
20
20
  type SharedNodeState,
21
21
  } from '../lib/reactivity/node-state/createSharedNodeState.ts';
22
+ import type { ReactiveScope } from '../lib/reactivity/scope.ts';
22
23
  import type { SimpleAtomicState } from '../lib/reactivity/types.ts';
23
24
  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';
25
+ import type { AnyChildNode, AnyNode } from './hierarchy.ts';
27
26
  import type { AttributeContext } from './internal-api/AttributeContext.ts';
27
+ import type { InstanceConfig } from './internal-api/InstanceConfig.ts';
28
28
  import type { DecodeInstanceValue } from './internal-api/InstanceValueContext.ts';
29
29
  import type { ClientReactiveSerializableAttributeNode } from './internal-api/serialization/ClientReactiveSerializableAttributeNode.ts';
30
+ import type { PrimaryInstance } from './PrimaryInstance.ts';
30
31
  import type { Root } from './Root.ts';
31
32
 
32
- export interface AttributeStateSpec extends DescendantNodeSharedStateSpec {
33
- readonly children: null;
34
- readonly attributes: null;
33
+ export interface AttributeStateSpec {
35
34
  readonly value: SimpleAtomicState<string>;
36
35
  readonly instanceValue: Accessor<string>;
37
- readonly label: null;
38
- readonly hint: null;
39
- readonly valueOptions: null;
36
+ readonly relevant: Accessor<boolean>;
40
37
  }
41
38
 
42
39
  export class Attribute
43
- extends InstanceNode<AttributeDefinition, AttributeStateSpec, AnyParentNode, null>
44
40
  implements
45
41
  AttributeNode,
46
42
  ClientReactiveSerializableAttributeNode,
47
43
  AttributeContext,
48
44
  XFormsXPathAttribute
49
45
  {
50
- override readonly [XPathNodeKindKey] = 'attribute';
46
+ readonly [XPathNodeKindKey] = 'attribute';
51
47
 
52
48
  protected readonly state: SharedNodeState<AttributeStateSpec>;
53
49
  protected readonly engineState: EngineState<AttributeStateSpec>;
@@ -55,7 +51,7 @@ export class Attribute
55
51
 
56
52
  readonly nodeType = 'attribute';
57
53
  readonly currentState: CurrentState<AttributeStateSpec>;
58
- override readonly instanceState: InstanceState;
54
+ readonly instanceState: InstanceState;
59
55
 
60
56
  readonly appearances = null;
61
57
  readonly nodeOptions = null;
@@ -68,15 +64,19 @@ export class Attribute
68
64
  protected readonly setValueState: RuntimeValueSetter<RuntimeInputValue<'string'>>;
69
65
  readonly evaluator: EngineXPathEvaluator;
70
66
  readonly getActiveLanguage: Accessor<ActiveLanguage>;
67
+ readonly contextNode: AnyNode;
68
+ readonly scope: ReactiveScope;
69
+ readonly rootDocument: PrimaryInstance;
70
+ readonly instanceConfig: InstanceConfig;
71
71
 
72
- override readonly root: Root;
72
+ readonly root: Root;
73
73
 
74
74
  readonly isRelevant: Accessor<boolean> = () => {
75
- return this.parent.isRelevant();
75
+ return this.owner.isRelevant();
76
76
  };
77
77
 
78
78
  readonly isAttached: Accessor<boolean> = () => {
79
- return this.parent.isAttached();
79
+ return this.owner.isAttached();
80
80
  };
81
81
 
82
82
  readonly isReadonly: Accessor<boolean> = () => {
@@ -84,37 +84,41 @@ export class Attribute
84
84
  };
85
85
 
86
86
  readonly hasReadonlyAncestor: Accessor<boolean> = () => {
87
- const { parent } = this;
88
- return parent.hasReadonlyAncestor() || parent.isReadonly();
87
+ const { owner } = this;
88
+ return owner.hasReadonlyAncestor() || owner.isReadonly();
89
89
  };
90
90
 
91
91
  readonly hasNonRelevantAncestor: Accessor<boolean> = () => {
92
- const { parent } = this;
93
- return parent.hasNonRelevantAncestor() || !parent.isRelevant();
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();
94
98
  };
95
99
 
96
100
  constructor(
97
- parent: AnyParentNode,
98
- definition: AttributeDefinition,
99
- override readonly instanceNode: StaticAttribute
101
+ readonly owner: AnyNode,
102
+ readonly definition: AttributeDefinition,
103
+ readonly instanceNode: StaticAttribute
100
104
  ) {
101
105
  const codec = getSharedValueCodec('string');
102
106
 
103
- super(parent.instanceConfig, parent, instanceNode, definition, {
104
- scope: parent.scope,
105
- computeReference: (): string => '@' + this.definition.qualifiedName.getPrefixedName(),
106
- });
107
+ this.contextNode = owner;
108
+ this.scope = owner.scope;
109
+ this.rootDocument = owner.rootDocument;
107
110
 
108
- this.root = parent.root;
111
+ this.root = owner.root;
112
+ this.instanceConfig = owner.instanceConfig;
109
113
 
110
- this.getActiveLanguage = parent.getActiveLanguage;
114
+ this.getActiveLanguage = owner.getActiveLanguage;
111
115
  this.validationState = { violations: [] };
112
116
 
113
117
  this.valueType = 'string';
114
- this.evaluator = parent.evaluator;
118
+ this.evaluator = owner.evaluator;
115
119
  this.decodeInstanceValue = codec.decodeInstanceValue;
116
120
 
117
- const instanceValueState = createAttributeValueState(this);
121
+ const instanceValueState = createInstanceValueState(this);
118
122
  const valueState = codec.createRuntimeValueState(instanceValueState);
119
123
 
120
124
  const [getInstanceValue] = instanceValueState;
@@ -122,26 +126,14 @@ export class Attribute
122
126
 
123
127
  this.getInstanceValue = getInstanceValue;
124
128
  this.setValueState = setValueState;
125
- this.getXPathValue = () => {
126
- return this.getInstanceValue();
127
- };
128
129
  this.valueState = valueState;
129
130
 
130
131
  const state = createSharedNodeState(
131
- this.scope,
132
+ owner.scope,
132
133
  {
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
134
  value: this.valueState,
143
135
  instanceValue: this.getInstanceValue,
144
- attributes: null,
136
+ relevant: this.owner.isRelevant,
145
137
  },
146
138
  this.instanceConfig
147
139
  );
@@ -161,4 +153,12 @@ export class Attribute
161
153
  getChildren(): readonly [] {
162
154
  return [];
163
155
  }
156
+
157
+ getXPathChildNodes(): readonly AnyChildNode[] {
158
+ return [];
159
+ }
160
+
161
+ getXPathValue(): string {
162
+ return '';
163
+ }
164
164
  }
@@ -8,10 +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 {
12
- createAttributeState,
13
- type AttributeState,
14
- } from '../lib/reactivity/createAttributeState.ts';
11
+ import { createAttributeState } from '../lib/reactivity/createAttributeState.ts';
15
12
  import type { ChildrenState } from '../lib/reactivity/createChildrenState.ts';
16
13
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
17
14
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -50,7 +47,6 @@ export class Group
50
47
  ClientReactiveSerializableParentNode<GeneralChildNode>
51
48
  {
52
49
  private readonly childrenState: ChildrenState<GeneralChildNode>;
53
- private readonly attributeState: AttributeState;
54
50
 
55
51
  override readonly [XPathNodeKindKey] = 'element';
56
52
 
@@ -79,7 +75,6 @@ export class Group
79
75
  const attributeState = createAttributeState(this.scope);
80
76
 
81
77
  this.childrenState = childrenState;
82
- this.attributeState = attributeState;
83
78
 
84
79
  const state = createSharedNodeState(
85
80
  this.scope,
@@ -116,8 +111,4 @@ export class Group
116
111
  getChildren(): readonly GeneralChildNode[] {
117
112
  return this.childrenState.getChildren();
118
113
  }
119
-
120
- getAttributes(): readonly Attribute[] {
121
- return this.attributeState.getAttributes();
122
- }
123
114
  }
@@ -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,7 +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,
133
- attributes: null,
138
+ attributes: attributeState.getAttributes,
134
139
  valueOptions: null,
135
140
  value: this.valueState,
136
141
  instanceValue: this.getInstanceValue,
@@ -138,6 +143,8 @@ export class InputControl<V extends ValueType = ValueType>
138
143
  this.instanceConfig
139
144
  );
140
145
 
146
+ attributeState.setAttributes(buildAttributes(this));
147
+
141
148
  this.state = state;
142
149
  this.engineState = state.engineState;
143
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,7 +80,7 @@ export class ModelValue<V extends ValueType = ValueType>
76
80
  label: null,
77
81
  hint: null,
78
82
  children: null,
79
- attributes: null,
83
+ attributes: attributeState.getAttributes,
80
84
  valueOptions: null,
81
85
  value: this.valueState,
82
86
  instanceValue: this.getInstanceValue,
@@ -84,6 +88,8 @@ export class ModelValue<V extends ValueType = ValueType>
84
88
  this.instanceConfig
85
89
  );
86
90
 
91
+ attributeState.setAttributes(buildAttributes(this));
92
+
87
93
  this.state = state;
88
94
  this.engineState = state.engineState;
89
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,7 +108,7 @@ export class Note<V extends ValueType = ValueType>
105
108
  noteText,
106
109
 
107
110
  children: null,
108
- attributes: null,
111
+ attributes: attributeState.getAttributes,
109
112
  valueOptions: null,
110
113
  value: this.valueState,
111
114
  instanceValue: this.getInstanceValue,
@@ -113,6 +116,8 @@ export class Note<V extends ValueType = ValueType>
113
116
  this.instanceConfig
114
117
  );
115
118
 
119
+ attributeState.setAttributes(buildAttributes(this));
120
+
116
121
  this.state = state;
117
122
  this.engineState = state.engineState;
118
123
  this.currentState = state.currentState;
@@ -17,10 +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 {
21
- createAttributeState,
22
- type AttributeState,
23
- } from '../lib/reactivity/createAttributeState.ts';
20
+ import { createAttributeState } from '../lib/reactivity/createAttributeState.ts';
24
21
  import { createChildrenState } from '../lib/reactivity/createChildrenState.ts';
25
22
  import { createTranslationState } from '../lib/reactivity/createTranslationState.ts';
26
23
  import type { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -161,8 +158,6 @@ export class PrimaryInstance<
161
158
  readonly evaluator: EngineXPathEvaluator;
162
159
  override readonly contextNode = this;
163
160
 
164
- private readonly attributeState: AttributeState;
165
-
166
161
  constructor(options: PrimaryInstanceOptions<Mode>) {
167
162
  const { mode, initialState, scope, model, secondaryInstances, config } = options;
168
163
  const { instance: modelInstance } = model;
@@ -205,7 +200,6 @@ export class PrimaryInstance<
205
200
  const attributeState = createAttributeState(this.scope);
206
201
 
207
202
  this.getChildren = childrenState.getChildren;
208
- this.attributeState = attributeState;
209
203
 
210
204
  const stateSpec: PrimaryInstanceStateSpec = {
211
205
  activeLanguage: getActiveLanguage,
@@ -292,8 +286,4 @@ export class PrimaryInstance<
292
286
 
293
287
  return Promise.resolve(result);
294
288
  }
295
-
296
- getAttributes(): readonly Attribute[] {
297
- return this.attributeState.getAttributes();
298
- }
299
289
  }
@@ -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,7 +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,
97
- attributes: null,
100
+ attributes: attributeState.getAttributes,
98
101
  valueOptions: null,
99
102
  value: this.valueState,
100
103
  instanceValue: this.getInstanceValue,
@@ -102,6 +105,8 @@ export class RangeControl<V extends RangeValueType = RangeValueType>
102
105
  this.instanceConfig
103
106
  );
104
107
 
108
+ attributeState.setAttributes(buildAttributes(this));
109
+
105
110
  this.state = state;
106
111
  this.engineState = state.engineState;
107
112
  this.currentState = state.currentState;
@@ -10,6 +10,7 @@ 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
14
  import { createItemCollection } from '../lib/reactivity/createItemCollection.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';
@@ -22,6 +23,7 @@ import type { UnknownAppearanceDefinition } from '../parse/body/appearance/unkno
22
23
  import type { Root } from './Root.ts';
23
24
  import type { ValueNodeStateSpec } from './abstract/ValueNode.ts';
24
25
  import { ValueNode } from './abstract/ValueNode.ts';
26
+ import { buildAttributes } from './attachments/buildAttributes.ts';
25
27
  import type { GeneralParentNode } from './hierarchy.ts';
26
28
  import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
27
29
  import type { ValidationContext } from './internal-api/ValidationContext.ts';
@@ -123,6 +125,8 @@ export class RankControl
123
125
  const baseValueState = this.valueState;
124
126
  const [baseGetValue, setValue] = baseValueState;
125
127
 
128
+ const attributeState = createAttributeState(this.scope);
129
+
126
130
  /**
127
131
  * @ToDo As new value options become available, they're not yet in the
128
132
  * `currentValues` state. This appends them. We intend to change this
@@ -174,7 +178,7 @@ export class RankControl
174
178
  label: createNodeLabel(this, definition),
175
179
  hint: createFieldHint(this, definition),
176
180
  children: null,
177
- attributes: null,
181
+ attributes: attributeState.getAttributes,
178
182
  valueOptions,
179
183
  value: valueState,
180
184
  instanceValue: this.getInstanceValue,
@@ -182,6 +186,8 @@ export class RankControl
182
186
  this.instanceConfig
183
187
  );
184
188
 
189
+ attributeState.setAttributes(buildAttributes(this));
190
+
185
191
  this.state = state;
186
192
  this.engineState = state.engineState;
187
193
  this.currentState = state.currentState;