@getodk/xforms-engine 0.1.1 → 0.2.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 (101) hide show
  1. package/dist/body/BodyDefinition.d.ts +24 -7
  2. package/dist/body/RepeatElementDefinition.d.ts +19 -0
  3. package/dist/body/appearance/inputAppearanceParser.d.ts +4 -0
  4. package/dist/body/appearance/selectAppearanceParser.d.ts +4 -0
  5. package/dist/body/appearance/structureElementAppearanceParser.d.ts +4 -0
  6. package/dist/body/control/ControlDefinition.d.ts +2 -0
  7. package/dist/body/control/InputDefinition.d.ts +5 -0
  8. package/dist/body/control/select/SelectDefinition.d.ts +11 -1
  9. package/dist/body/group/BaseGroupDefinition.d.ts +3 -8
  10. package/dist/body/text/LabelDefinition.d.ts +2 -0
  11. package/dist/body/text/TextElementDefinition.d.ts +3 -3
  12. package/dist/client/BaseNode.d.ts +6 -1
  13. package/dist/client/GroupNode.d.ts +5 -2
  14. package/dist/client/NodeAppearances.d.ts +15 -0
  15. package/dist/client/RepeatInstanceNode.d.ts +3 -0
  16. package/dist/client/RepeatRangeNode.d.ts +5 -2
  17. package/dist/client/RootNode.d.ts +19 -0
  18. package/dist/client/SelectNode.d.ts +3 -0
  19. package/dist/client/StringNode.d.ts +3 -0
  20. package/dist/client/SubtreeNode.d.ts +1 -0
  21. package/dist/index.js +624 -368
  22. package/dist/index.js.map +1 -1
  23. package/dist/instance/Group.d.ts +3 -3
  24. package/dist/instance/RepeatInstance.d.ts +26 -2
  25. package/dist/instance/RepeatRange.d.ts +84 -5
  26. package/dist/instance/Root.d.ts +8 -23
  27. package/dist/instance/SelectField.d.ts +2 -2
  28. package/dist/instance/StringField.d.ts +2 -2
  29. package/dist/instance/Subtree.d.ts +1 -1
  30. package/dist/instance/abstract/DescendantNode.d.ts +12 -4
  31. package/dist/instance/abstract/InstanceNode.d.ts +26 -29
  32. package/dist/instance/internal-api/EvaluationContext.d.ts +5 -4
  33. package/dist/instance/internal-api/ValueContext.d.ts +2 -2
  34. package/dist/lib/TokenListParser.d.ts +84 -0
  35. package/dist/lib/dom/query.d.ts +5 -0
  36. package/dist/lib/reactivity/materializeCurrentStateChildren.d.ts +2 -1
  37. package/dist/model/DescendentNodeDefinition.d.ts +1 -2
  38. package/dist/model/NodeDefinition.d.ts +12 -12
  39. package/dist/model/RepeatInstanceDefinition.d.ts +5 -6
  40. package/dist/model/{RepeatSequenceDefinition.d.ts → RepeatRangeDefinition.d.ts} +4 -4
  41. package/dist/model/RepeatTemplateDefinition.d.ts +6 -7
  42. package/dist/model/RootDefinition.d.ts +3 -1
  43. package/dist/model/SubtreeDefinition.d.ts +2 -2
  44. package/dist/model/ValueNodeDefinition.d.ts +2 -3
  45. package/dist/solid.js +625 -369
  46. package/dist/solid.js.map +1 -1
  47. package/package.json +2 -2
  48. package/src/XFormDOM.ts +81 -8
  49. package/src/body/BodyDefinition.ts +38 -23
  50. package/src/body/RepeatElementDefinition.ts +70 -0
  51. package/src/body/appearance/inputAppearanceParser.ts +39 -0
  52. package/src/body/appearance/selectAppearanceParser.ts +38 -0
  53. package/src/body/appearance/structureElementAppearanceParser.ts +7 -0
  54. package/src/body/control/ControlDefinition.ts +4 -0
  55. package/src/body/control/InputDefinition.ts +13 -0
  56. package/src/body/control/select/SelectDefinition.ts +14 -5
  57. package/src/body/group/BaseGroupDefinition.ts +11 -49
  58. package/src/body/text/LabelDefinition.ts +15 -1
  59. package/src/body/text/TextElementDefinition.ts +5 -5
  60. package/src/client/BaseNode.ts +9 -1
  61. package/src/client/GroupNode.ts +6 -2
  62. package/src/client/NodeAppearances.ts +22 -0
  63. package/src/client/RepeatInstanceNode.ts +4 -0
  64. package/src/client/RepeatRangeNode.ts +6 -2
  65. package/src/client/RootNode.ts +22 -0
  66. package/src/client/SelectNode.ts +4 -0
  67. package/src/client/StringNode.ts +4 -0
  68. package/src/client/SubtreeNode.ts +1 -0
  69. package/src/instance/Group.ts +14 -9
  70. package/src/instance/RepeatInstance.ts +59 -15
  71. package/src/instance/RepeatRange.ts +133 -15
  72. package/src/instance/Root.ts +20 -64
  73. package/src/instance/SelectField.ts +7 -7
  74. package/src/instance/StringField.ts +8 -7
  75. package/src/instance/Subtree.ts +10 -7
  76. package/src/instance/abstract/DescendantNode.ts +45 -43
  77. package/src/instance/abstract/InstanceNode.ts +69 -86
  78. package/src/instance/children.ts +17 -7
  79. package/src/instance/index.ts +1 -1
  80. package/src/instance/internal-api/EvaluationContext.ts +5 -6
  81. package/src/instance/internal-api/ValueContext.ts +2 -2
  82. package/src/lib/TokenListParser.ts +156 -0
  83. package/src/lib/dom/query.ts +13 -0
  84. package/src/lib/reactivity/createChildrenState.ts +51 -6
  85. package/src/lib/reactivity/createComputedExpression.ts +1 -1
  86. package/src/lib/reactivity/createSelectItems.ts +4 -6
  87. package/src/lib/reactivity/createValueState.ts +6 -6
  88. package/src/lib/reactivity/materializeCurrentStateChildren.ts +3 -1
  89. package/src/model/DescendentNodeDefinition.ts +1 -2
  90. package/src/model/ModelDefinition.ts +1 -1
  91. package/src/model/NodeDefinition.ts +12 -12
  92. package/src/model/RepeatInstanceDefinition.ts +8 -13
  93. package/src/model/{RepeatSequenceDefinition.ts → RepeatRangeDefinition.ts} +6 -6
  94. package/src/model/RepeatTemplateDefinition.ts +10 -15
  95. package/src/model/RootDefinition.ts +6 -12
  96. package/src/model/SubtreeDefinition.ts +3 -3
  97. package/src/model/ValueNodeDefinition.ts +2 -3
  98. package/dist/body/RepeatDefinition.d.ts +0 -16
  99. package/dist/body/group/RepeatGroupDefinition.d.ts +0 -13
  100. package/src/body/RepeatDefinition.ts +0 -54
  101. package/src/body/group/RepeatGroupDefinition.ts +0 -91
@@ -1,5 +1,5 @@
1
1
  import { Accessor } from 'solid-js';
2
- import { GroupDefinition, GroupNode } from '../client/GroupNode.ts';
2
+ import { GroupDefinition, GroupNode, GroupNodeAppearances } from '../client/GroupNode.ts';
3
3
  import { TextRange } from '../index.ts';
4
4
  import { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
5
5
  import { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
@@ -22,10 +22,10 @@ export declare class Group extends DescendantNode<GroupDefinition, GroupStateSpe
22
22
  private readonly childrenState;
23
23
  protected readonly state: SharedNodeState<GroupStateSpec>;
24
24
  protected engineState: EngineState<GroupStateSpec>;
25
- readonly currentState: MaterializedChildren<CurrentState<GroupStateSpec>, GeneralChildNode>;
26
25
  readonly nodeType = "group";
26
+ readonly appearances: GroupNodeAppearances;
27
+ readonly currentState: MaterializedChildren<CurrentState<GroupStateSpec>, GeneralChildNode>;
27
28
  constructor(parent: GeneralParentNode, definition: GroupDefinition);
28
- protected computeReference(parent: GeneralParentNode): string;
29
29
  getChildren(): readonly GeneralChildNode[];
30
30
  }
31
31
  export {};
@@ -1,5 +1,5 @@
1
1
  import { Accessor } from 'solid-js';
2
- import { RepeatDefinition, RepeatInstanceNode } from '../client/RepeatInstanceNode.ts';
2
+ import { RepeatDefinition, RepeatInstanceNode, RepeatInstanceNodeAppearances } from '../client/RepeatInstanceNode.ts';
3
3
  import { TextRange } from '../index.ts';
4
4
  import { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
5
5
  import { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
@@ -30,10 +30,34 @@ export declare class RepeatInstance extends DescendantNode<RepeatDefinition, Rep
30
30
  private readonly currentIndex;
31
31
  protected readonly state: SharedNodeState<RepeatInstanceStateSpec>;
32
32
  protected engineState: EngineState<RepeatInstanceStateSpec>;
33
+ /**
34
+ * @todo Should we special case repeat `readonly` inheritance the same way
35
+ * we do for `relevant`?
36
+ *
37
+ * @see {@link hasNonRelevantAncestor}
38
+ */
39
+ readonly hasReadonlyAncestor: Accessor<boolean>;
40
+ /**
41
+ * A repeat instance can inherit non-relevance, just like any other node. That
42
+ * inheritance is derived from the repeat instance's parent node in the
43
+ * primary instance XML/DOM tree (and would be semantically expected to do so
44
+ * even if we move away from that implementation detail).
45
+ *
46
+ * Since {@link RepeatInstance.parent} is a {@link RepeatRange}, which is a
47
+ * runtime data model fiction that does not exist in that hierarchy, we pass
48
+ * this call through, allowing the {@link RepeatRange} to check the actual
49
+ * primary instance parent node's relevance state.
50
+ *
51
+ * @todo Should we apply similar reasoning in {@link hasReadonlyAncestor}?
52
+ */
53
+ readonly hasNonRelevantAncestor: Accessor<boolean>;
33
54
  readonly nodeType = "repeat-instance";
55
+ /**
56
+ * @see {@link RepeatRange.appearances}
57
+ */
58
+ readonly appearances: RepeatInstanceNodeAppearances;
34
59
  readonly currentState: MaterializedChildren<CurrentState<RepeatInstanceStateSpec>, GeneralChildNode>;
35
60
  constructor(parent: RepeatRange, definition: RepeatDefinition, options: RepeatInstanceOptions);
36
- protected computeReference(parent: RepeatRange): string;
37
61
  protected initializeContextNode(parentContextNode: Element, nodeName: string): Element;
38
62
  subscribe(): void;
39
63
  getChildren(): readonly GeneralChildNode[];
@@ -1,10 +1,10 @@
1
1
  import { Accessor } from 'solid-js';
2
- import { RepeatRangeNode } from '../client/RepeatRangeNode.ts';
2
+ import { RepeatRangeNode, RepeatRangeNodeAppearances } from '../client/RepeatRangeNode.ts';
3
3
  import { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
4
4
  import { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
5
5
  import { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
6
6
  import { SharedNodeState } from '../lib/reactivity/node-state/createSharedNodeState.ts';
7
- import { RepeatSequenceDefinition } from '../model/RepeatSequenceDefinition.ts';
7
+ import { RepeatRangeDefinition } from '../model/RepeatRangeDefinition.ts';
8
8
  import { RepeatDefinition, RepeatInstance } from './RepeatInstance.ts';
9
9
  import { Root } from './Root.ts';
10
10
  import { DescendantNodeSharedStateSpec, DescendantNode } from './abstract/DescendantNode.ts';
@@ -21,7 +21,7 @@ interface RepeatRangeStateSpec extends DescendantNodeSharedStateSpec {
21
21
  readonly valueOptions: null;
22
22
  readonly value: null;
23
23
  }
24
- export declare class RepeatRange extends DescendantNode<RepeatSequenceDefinition, RepeatRangeStateSpec, RepeatInstance> implements RepeatRangeNode, EvaluationContext, SubscribableDependency {
24
+ export declare class RepeatRange extends DescendantNode<RepeatRangeDefinition, RepeatRangeStateSpec, RepeatInstance> implements RepeatRangeNode, EvaluationContext, SubscribableDependency {
25
25
  /**
26
26
  * A repeat range doesn't have a corresponding primary instance element of its
27
27
  * own, and its instances are appended to the range's parent element. During
@@ -48,12 +48,91 @@ export declare class RepeatRange extends DescendantNode<RepeatSequenceDefinition
48
48
  private readonly childrenState;
49
49
  protected readonly state: SharedNodeState<RepeatRangeStateSpec>;
50
50
  protected engineState: EngineState<RepeatRangeStateSpec>;
51
+ /**
52
+ * @todo Should we special case repeat `readonly` state the same way
53
+ * we do for `relevant`?
54
+ *
55
+ * @see {@link isSelfRelevant}
56
+ */
57
+ isSelfReadonly: Accessor<boolean>;
58
+ private readonly emptyRangeEvaluationContext;
59
+ /**
60
+ * @see {@link isSelfRelevant}
61
+ */
62
+ private readonly isEmptyRangeSelfRelevant;
63
+ /**
64
+ * A repeat range does not exist in the primary instance tree. A `relevant`
65
+ * expression applies to each {@link RepeatInstance} child of the repeat
66
+ * range. Determining whether a repeat range itself "is relevant" isn't a
67
+ * concept the spec addresses, but it may be used by clients to determine
68
+ * whether to allow interaction with the range (e.g. by adding a repeat
69
+ * instance, or presenting the range's label when empty).
70
+ *
71
+ * As a naive first pass, it seems like the heuristic for this should be:
72
+ *
73
+ * 1. Does the repeat range have any repeat instance children?
74
+ *
75
+ * - If yes, go to 2.
76
+ * - If no, go to 3.
77
+ *
78
+ * 2. Does one or more of those children return `true` for the node's
79
+ * `relevant` expression (i.e. is the repeat instance "self relevant")?
80
+ *
81
+ * 3. Does the relevant expression return `true` for the repeat range itself
82
+ * (where, at least for now, the context of that evaluation would be the
83
+ * repeat range's {@link anchorNode} to ensure correct relative expressions
84
+ * resolve correctly)?
85
+ *
86
+ * @todo While (3) is proactively implemented, there isn't presently a test
87
+ * exercising it. It felt best for now to surface this for discussion in
88
+ * review to validate that it's going in the right direction.
89
+ *
90
+ * @todo While (2) **is actually tested**, the tests currently in place behave
91
+ * the same way with only the logic for (3), regardless of whether the repeat
92
+ * range actually has any repeat instance children. It's unclear (a) if that's
93
+ * a preferable simplification and (b) how that might affect performance (in
94
+ * theory it could vary depending on form structure and runtime state).
95
+ */
96
+ readonly isSelfRelevant: Accessor<boolean>;
51
97
  readonly nodeType = "repeat-range";
98
+ /**
99
+ * @todo RepeatRange*, RepeatInstance* (and RepeatTemplate*) all share the
100
+ * same body element, and thus all share the same definition `bodyElement`. As
101
+ * such, they also all share the same `appearances`. At time of writing,
102
+ * `web-forms` (Vue UI package) treats a `RepeatRangeNode`...
103
+ *
104
+ * - ... as a group, if the node has a label (i.e.
105
+ * `<group><label/><repeat/></group>`)
106
+ * - ... effectively as a fragment containing only its instances, otherwise
107
+ *
108
+ * We now collapse `<group><repeat>` into `<repeat>`, and no longer treat
109
+ * "repeat group" as a concept (after parsing). According to the spec, these
110
+ * appearances **are supposed to** come from that "repeat group" in the form
111
+ * definition. In practice, many forms do define appearances directly on a
112
+ * repeat element. The engine currently produces an error if both are defined
113
+ * simultaneously, but otherwise makes no distinction between appearances in
114
+ * these form definition shapes:
115
+ *
116
+ * ```xml
117
+ * <group ref="/data/rep1" appearance="...">
118
+ * <repeat nodeset="/data/rep1"/>
119
+ * </group>
120
+ *
121
+ * <group ref="/data/rep1">
122
+ * <repeat nodeset="/data/rep1"/ appearance="...">
123
+ * </group>
124
+ *
125
+ * <repeat nodeset="/data/rep1"/ appearance="...">
126
+ * ```
127
+ *
128
+ * All of the above creates considerable ambiguity about where "repeat
129
+ * appearances" should apply, under which circumstances.
130
+ */
131
+ readonly appearances: RepeatRangeNodeAppearances;
52
132
  readonly currentState: MaterializedChildren<CurrentState<RepeatRangeStateSpec>, RepeatInstance>;
53
- constructor(parent: GeneralParentNode, definition: RepeatSequenceDefinition);
133
+ constructor(parent: GeneralParentNode, definition: RepeatRangeDefinition);
54
134
  private getLastIndex;
55
135
  protected initializeContextNode(parentContextNode: Element): Element;
56
- protected computeReference(parent: GeneralParentNode): string;
57
136
  getInstanceIndex(instance: RepeatInstance): number;
58
137
  addInstances(afterIndex?: number, count?: number, definition?: RepeatDefinition): Root;
59
138
  /**
@@ -1,6 +1,7 @@
1
1
  import { XFormsXPathEvaluator } from '@getodk/xpath';
2
2
  import { Accessor, Signal } from 'solid-js';
3
3
  import { XFormDOM } from '../XFormDOM.ts';
4
+ import { BodyClassList } from '../body/BodyDefinition.ts';
4
5
  import { ActiveLanguage, FormLanguage, FormLanguages } from '../client/FormLanguage.ts';
5
6
  import { RootNode } from '../client/RootNode.ts';
6
7
  import { MaterializedChildren } from '../lib/reactivity/materializeCurrentStateChildren.ts';
@@ -29,41 +30,25 @@ interface RootStateSpec {
29
30
  readonly activeLanguage: Signal<ActiveLanguage>;
30
31
  }
31
32
  export declare class Root extends InstanceNode<RootDefinition, RootStateSpec, GeneralChildNode> implements RootNode, EvaluationContext, EvaluationContextRoot, SubscribableDependency, TranslationContext {
32
- static initialize(xformDOM: XFormDOM, definition: RootDefinition, engineConfig: InstanceConfig): Promise<Root>;
33
33
  private readonly childrenState;
34
+ readonly hasReadonlyAncestor: () => boolean;
35
+ readonly isReadonly: () => boolean;
36
+ readonly hasNonRelevantAncestor: () => boolean;
37
+ readonly isRelevant: () => boolean;
34
38
  protected readonly state: SharedNodeState<RootStateSpec>;
35
39
  protected readonly engineState: EngineState<RootStateSpec>;
36
40
  readonly nodeType = "root";
41
+ readonly appearances: null;
42
+ readonly classes: BodyClassList;
37
43
  readonly currentState: MaterializedChildren<CurrentState<RootStateSpec>, GeneralChildNode>;
38
44
  protected readonly instanceDOM: XFormDOM;
39
45
  readonly root: this;
40
46
  readonly evaluator: XFormsXPathEvaluator;
41
- private readonly rootReference;
42
- get contextReference(): string;
43
47
  readonly contextNode: Element;
44
48
  readonly parent: null;
45
49
  readonly languages: FormLanguages;
46
50
  get activeLanguage(): ActiveLanguage;
47
- protected constructor(xformDOM: XFormDOM, definition: RootDefinition, engineConfig: InstanceConfig);
48
- /**
49
- * Waits until form state is fully initialized.
50
- *
51
- * As much as possible, all instance state computations are implemented so
52
- * that they complete synchronously.
53
- *
54
- * There is currently one exception: because instance nodes may form
55
- * computation dependencies into their descendants as well as their ancestors,
56
- * there is an allowance **during form initialization only** to account for
57
- * this chicken/egg scenario. Note that this allowance is intentionally,
58
- * strictly limited: if form state initialization is not resolved within a
59
- * single microtask tick we throw/reject.
60
- *
61
- * All subsequent computations are always performed synchronously (and we will
62
- * use tests to validate this, by utilizing the synchronously returned `Root`
63
- * state from client-facing write interfaces).
64
- */
65
- formStateInitialized(): Promise<void>;
66
- protected computeReference(_parent: null, definition: RootDefinition): string;
51
+ constructor(xformDOM: XFormDOM, definition: RootDefinition, engineConfig: InstanceConfig);
67
52
  getChildren(): readonly GeneralChildNode[];
68
53
  setLanguage(language: FormLanguage): Root;
69
54
  subscribe(): void;
@@ -1,6 +1,6 @@
1
1
  import { Accessor } from 'solid-js';
2
2
  import { AnySelectDefinition } from '../body/control/select/SelectDefinition.ts';
3
- import { SelectItem, SelectNode } from '../client/SelectNode.ts';
3
+ import { SelectItem, SelectNode, SelectNodeAppearances } from '../client/SelectNode.ts';
4
4
  import { TextRange } from '../index.ts';
5
5
  import { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
6
6
  import { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
@@ -29,13 +29,13 @@ export declare class SelectField extends DescendantNode<SelectFieldDefinition, S
29
29
  protected readonly state: SharedNodeState<SelectFieldStateSpec>;
30
30
  protected engineState: EngineState<SelectFieldStateSpec>;
31
31
  readonly nodeType = "select";
32
+ readonly appearances: SelectNodeAppearances;
32
33
  readonly currentState: CurrentState<SelectFieldStateSpec>;
33
34
  readonly encodeValue: (runtimeValue: readonly SelectItem[]) => string;
34
35
  readonly decodeValue: (instanceValue: string) => readonly SelectItem[];
35
36
  protected readonly getValueOptions: Accessor<readonly SelectItem[]>;
36
37
  constructor(parent: GeneralParentNode, definition: SelectFieldDefinition);
37
38
  protected getSelectItemsByValue(valueOptions?: readonly SelectItem[]): ReadonlyMap<string, SelectItem>;
38
- protected computeReference(parent: GeneralParentNode): string;
39
39
  protected updateSelectedItemValues(values: readonly string[]): void;
40
40
  protected setSelectedItemValue(value: string | null): void;
41
41
  select(selectedItem: SelectItem): Root;
@@ -1,6 +1,6 @@
1
1
  import { Accessor } from 'solid-js';
2
2
  import { InputDefinition } from '../body/control/InputDefinition.ts';
3
- import { StringNode } from '../client/StringNode.ts';
3
+ import { StringNode, StringNodeAppearances } from '../client/StringNode.ts';
4
4
  import { TextRange } from '../index.ts';
5
5
  import { CurrentState } from '../lib/reactivity/node-state/createCurrentState.ts';
6
6
  import { EngineState } from '../lib/reactivity/node-state/createEngineState.ts';
@@ -28,11 +28,11 @@ export declare class StringField extends DescendantNode<StringFieldDefinition, S
28
28
  protected readonly state: SharedNodeState<StringFieldStateSpec>;
29
29
  protected engineState: EngineState<StringFieldStateSpec>;
30
30
  readonly nodeType = "string";
31
+ readonly appearances: StringNodeAppearances;
31
32
  readonly currentState: CurrentState<StringFieldStateSpec>;
32
33
  readonly encodeValue: (value: string) => string;
33
34
  readonly decodeValue: (value: string) => string;
34
35
  constructor(parent: GeneralParentNode, definition: StringFieldDefinition);
35
- protected computeReference(parent: GeneralParentNode): string;
36
36
  getChildren(): readonly [];
37
37
  setValue(value: string): Root;
38
38
  }
@@ -22,9 +22,9 @@ export declare class Subtree extends DescendantNode<SubtreeDefinition, SubtreeSt
22
22
  protected readonly state: SharedNodeState<SubtreeStateSpec>;
23
23
  protected engineState: EngineState<SubtreeStateSpec>;
24
24
  readonly nodeType = "subtree";
25
+ readonly appearances: null;
25
26
  readonly currentState: MaterializedChildren<CurrentState<SubtreeStateSpec>, GeneralChildNode>;
26
27
  constructor(parent: GeneralParentNode, definition: SubtreeDefinition);
27
- protected computeReference(parent: GeneralParentNode): string;
28
28
  getChildren(): readonly GeneralChildNode[];
29
29
  }
30
30
  export {};
@@ -22,16 +22,23 @@ export type DescendantNodeStateSpec<Value = never> = InstanceNodeStateSpec<Value
22
22
  export type DescendantNodeDefinition = Extract<AnyNodeDefinition, AnyDescendantNodeDefinition>;
23
23
  export type DescendantNodeParent<Definition extends DescendantNodeDefinition> = Definition extends ValueNodeDefinition ? GeneralParentNode : Definition extends RepeatInstanceDefinition ? RepeatRange : GeneralParentNode;
24
24
  export type AnyDescendantNode = DescendantNode<DescendantNodeDefinition, DescendantNodeStateSpec<any>, any>;
25
+ interface DescendantNodeOptions {
26
+ readonly computeReference?: Accessor<string>;
27
+ }
25
28
  export declare abstract class DescendantNode<Definition extends DescendantNodeDefinition, Spec extends DescendantNodeStateSpec<any>, Child extends AnyChildNode | null = null> extends InstanceNode<Definition, Spec, Child> implements BaseNode, EvaluationContext, SubscribableDependency {
26
29
  readonly parent: DescendantNodeParent<Definition>;
27
30
  readonly definition: Definition;
31
+ readonly hasReadonlyAncestor: Accessor<boolean>;
32
+ readonly isSelfReadonly: Accessor<boolean>;
33
+ readonly isReadonly: Accessor<boolean>;
34
+ readonly hasNonRelevantAncestor: Accessor<boolean>;
35
+ readonly isSelfRelevant: Accessor<boolean>;
36
+ readonly isRelevant: Accessor<boolean>;
37
+ protected readonly isRequired: Accessor<boolean>;
28
38
  readonly root: Root;
29
39
  readonly evaluator: XFormsXPathEvaluator;
30
40
  readonly contextNode: Element;
31
- constructor(parent: DescendantNodeParent<Definition>, definition: Definition);
32
- protected computeChildStepReference(parent: DescendantNodeParent<Definition>): string;
33
- protected abstract computeReference(parent: DescendantNodeParent<Definition>, definition: Definition): string;
34
- protected buildSharedStateSpec(parent: DescendantNodeParent<Definition>, definition: Definition): DescendantNodeSharedStateSpec;
41
+ constructor(parent: DescendantNodeParent<Definition>, definition: Definition, options?: DescendantNodeOptions);
35
42
  protected createContextNode(parentContextNode: Element, nodeName: string): Element;
36
43
  /**
37
44
  * Currently expected to be overridden by...
@@ -74,3 +81,4 @@ export declare abstract class DescendantNode<Definition extends DescendantNodeDe
74
81
  */
75
82
  remove(this: AnyChildNode): void;
76
83
  }
84
+ export {};
@@ -1,6 +1,7 @@
1
1
  import { XFormsXPathEvaluator } from '@getodk/xpath';
2
2
  import { Accessor, Signal } from 'solid-js';
3
3
  import { BaseNode } from '../../client/BaseNode.ts';
4
+ import { NodeAppearances } from '../../client/NodeAppearances.ts';
4
5
  import { InstanceNodeType } from '../../client/node-types.ts';
5
6
  import { TextRange } from '../../index.ts';
6
7
  import { CurrentState } from '../../lib/reactivity/node-state/createCurrentState.ts';
@@ -28,9 +29,6 @@ export interface InstanceNodeStateSpec<Value = never> {
28
29
  readonly value: Signal<Value> | SimpleAtomicState<Value> | null;
29
30
  }
30
31
  type AnyInstanceNode = InstanceNode<AnyNodeDefinition, InstanceNodeStateSpec<any>, any>;
31
- interface InitializedStateOptions<T, K extends keyof T> {
32
- readonly uninitializedFallback: T[K];
33
- }
34
32
  /**
35
33
  * This type has the same effect as {@link MaterializedChildren}, but abstractly
36
34
  * handles leaf node types as well.
@@ -38,52 +36,52 @@ interface InitializedStateOptions<T, K extends keyof T> {
38
36
  export type InstanceNodeCurrentState<Spec extends InstanceNodeStateSpec<any>, Child> = CurrentState<Omit<Spec, 'children'>> & {
39
37
  readonly children: [Child] extends [AnyChildNode] ? readonly Child[] : null;
40
38
  };
39
+ interface ComputableReferenceNode {
40
+ readonly parent: AnyParentNode | null;
41
+ readonly definition: AnyNodeDefinition;
42
+ }
43
+ type ComputeInstanceNodeReference = <This extends ComputableReferenceNode>(this: This, parent: This['parent'], definition: This['definition']) => string;
44
+ export interface InstanceNodeOptions {
45
+ readonly computeReference?: () => string;
46
+ }
41
47
  export declare abstract class InstanceNode<Definition extends AnyNodeDefinition, Spec extends InstanceNodeStateSpec<any>, Child extends AnyChildNode | null = null> implements BaseNode, EvaluationContext, SubscribableDependency {
42
48
  readonly engineConfig: InstanceConfig;
43
49
  readonly parent: AnyParentNode | null;
44
50
  readonly definition: Definition;
45
- protected readonly isStateInitialized: Accessor<boolean>;
46
51
  protected abstract readonly state: SharedNodeState<Spec>;
47
52
  protected abstract readonly engineState: EngineState<Spec>;
48
53
  /**
49
- * Provides a generalized mechanism for accessing a reactive state value
50
- * during a node's construction, while {@link engineState} is still being
51
- * defined and thus isn't assigned.
52
- *
53
- * The fallback value specified in {@link options} will be returned on access
54
- * until {@link isStateInitialized} returns true. This ensures:
55
- *
56
- * - a value of the expected type will be available
57
- * - any read access will become reactive to the actual state, once it has
58
- * been initialized and {@link engineState} is assigned
59
- *
60
- * @todo This is one among several chicken/egg problems encountered trying to
61
- * support state initialization in which some aspects of the state derive from
62
- * other aspects of it. It would be nice to dispense with this entirely. But
63
- * if it must persist, we should also consider replacing the method with a
64
- * direct accessor once state initialization completes, so the initialized
65
- * check is only called until it becomes impertinent.
54
+ * @package Exposed on every node type to facilitate inheritance, as well as
55
+ * conditional behavior for value nodes.
56
+ */
57
+ abstract readonly hasReadonlyAncestor: Accessor<boolean>;
58
+ /**
59
+ * @package Exposed on every node type to facilitate inheritance, as well as
60
+ * conditional behavior for value nodes.
66
61
  */
67
- protected getInitializedState<K extends keyof EngineState<Spec>>(key: K, options: InitializedStateOptions<EngineState<Spec>, K>): EngineState<Spec>[K];
62
+ abstract readonly isReadonly: Accessor<boolean>;
68
63
  /**
69
64
  * @package Exposed on every node type to facilitate inheritance, as well as
70
65
  * conditional behavior for value nodes.
71
66
  */
72
- get isReadonly(): boolean;
67
+ abstract readonly hasNonRelevantAncestor: Accessor<boolean>;
73
68
  /**
74
69
  * @package Exposed on every node type to facilitate inheritance, as well as
75
70
  * conditional behavior for value nodes.
76
71
  */
77
- get isRelevant(): boolean;
72
+ abstract readonly isRelevant: Accessor<boolean>;
78
73
  readonly nodeId: NodeID;
79
74
  abstract readonly nodeType: InstanceNodeType;
75
+ abstract readonly appearances: NodeAppearances<Definition>;
80
76
  abstract readonly currentState: InstanceNodeCurrentState<Spec, Child>;
81
77
  abstract readonly root: Root;
82
78
  abstract readonly evaluator: XFormsXPathEvaluator;
83
79
  readonly scope: ReactiveScope;
84
- get contextReference(): string;
80
+ readonly computeReference: ComputeInstanceNodeReference;
81
+ protected readonly computeChildStepReference: ComputeInstanceNodeReference;
82
+ readonly contextReference: () => string;
85
83
  abstract readonly contextNode: Element;
86
- constructor(engineConfig: InstanceConfig, parent: AnyParentNode | null, definition: Definition);
84
+ constructor(engineConfig: InstanceConfig, parent: AnyParentNode | null, definition: Definition, options?: InstanceNodeOptions);
87
85
  /**
88
86
  * @package This presently serves a few internal use cases, where certain
89
87
  * behaviors depend on arbitrary traversal from any point in the instance
@@ -95,9 +93,8 @@ export declare abstract class InstanceNode<Definition extends AnyNodeDefinition,
95
93
  * interface for those internal uses.
96
94
  */
97
95
  abstract getChildren(this: AnyInstanceNode): readonly AnyChildNode[];
98
- protected abstract computeReference(parent: AnyInstanceNode | null, definition: Definition): string;
99
- getNodeByReference(this: AnyNode, visited: WeakSet<AnyNode>, dependencyReference: string): SubscribableDependency | null;
100
- getSubscribableDependencyByReference(this: AnyNode, reference: string): SubscribableDependency | null;
96
+ getNodesByReference(this: AnyNode, visited: WeakSet<AnyNode>, dependencyReference: string): readonly SubscribableDependency[];
97
+ getSubscribableDependenciesByReference(this: AnyNode, reference: string): readonly SubscribableDependency[];
101
98
  /**
102
99
  * This is a default implementation suitable for most node types. The rest
103
100
  * (currently: `Root`, `RepeatRange`, `RepeatInstance`) should likely extend
@@ -1,4 +1,5 @@
1
1
  import { XFormsXPathEvaluator } from '@getodk/xpath';
2
+ import { Accessor } from 'solid-js';
2
3
  import { ReactiveScope } from '../../lib/reactivity/scope.ts';
3
4
  import { SubscribableDependency } from './SubscribableDependency.ts';
4
5
  import { TranslationContext } from './TranslationContext.ts';
@@ -25,11 +26,11 @@ export interface EvaluationContext {
25
26
  * Produces the current absolute reference to the {@link contextNode}, where
26
27
  * the absolute `/` resolves to the active form state's primary instance root.
27
28
  */
28
- get contextReference(): string;
29
+ readonly contextReference: Accessor<string>;
29
30
  readonly contextNode: Node;
30
31
  /**
31
- * Resolves a nodeset reference, possibly relative to the
32
- * {@link EvaluationContext.contextNode}.
32
+ * Resolves nodes corresponding to the provided node-set reference, possibly
33
+ * relative to the {@link EvaluationContext.contextNode}.
33
34
  */
34
- readonly getSubscribableDependencyByReference: (reference: string) => SubscribableDependency | null;
35
+ getSubscribableDependenciesByReference(reference: string): readonly SubscribableDependency[];
35
36
  }
@@ -15,8 +15,8 @@ export interface ValueContext<RuntimeValue> extends EvaluationContext {
15
15
  readonly scope: ReactiveScope;
16
16
  readonly definition: ValueContextDefinition;
17
17
  readonly contextNode: Element;
18
- get isReadonly(): boolean;
19
- get isRelevant(): boolean;
18
+ isReadonly(): boolean;
19
+ isRelevant(): boolean;
20
20
  readonly encodeValue: (this: unknown, runtimeValue: RuntimeValue) => InstanceValue;
21
21
  readonly decodeValue: (this: unknown, instanceValue: InstanceValue) => RuntimeValue;
22
22
  }
@@ -0,0 +1,84 @@
1
+ import { PartiallyKnownString } from '../../../common/types/string/PartiallyKnownString.ts';
2
+
3
+ type SymbolIterator = typeof Symbol.iterator;
4
+ type TokenListKey<CanonicalToken extends string> = PartiallyKnownString<CanonicalToken> | SymbolIterator;
5
+ type TokenListIterator<CanonicalToken extends string> = IterableIterator<PartiallyKnownString<CanonicalToken>>;
6
+ /**
7
+ * @see {@link TokenListParser}
8
+ */
9
+ export type TokenList<CanonicalToken extends string = string> = {
10
+ readonly [Key in TokenListKey<CanonicalToken>]: Key extends SymbolIterator ? () => TokenListIterator<CanonicalToken> : boolean;
11
+ };
12
+ interface TokenListAlias<CanonicalToken extends string> {
13
+ readonly fromAlias: string;
14
+ readonly toCanonical: CanonicalToken;
15
+ }
16
+ type TokenAliases<CanonicalToken extends string> = ReadonlyArray<TokenListAlias<CanonicalToken>>;
17
+ interface TokenListParserOptions<CanonicalToken extends string> {
18
+ readonly aliases?: TokenAliases<CanonicalToken>;
19
+ }
20
+ type TokenListAttributeName = PartiallyKnownString<'appearance' | 'class'>;
21
+ /**
22
+ * Intended primarily for use in parsing these features:
23
+ *
24
+ * - {@link https://getodk.github.io/xforms-spec/#appearances | appearances} ({@link https://xlsform.org/en/#appearance | additional documentation})
25
+ *
26
+ * - {@link https://getodk.github.io/xforms-spec/#body-attributes | body `class` attribute}
27
+ *
28
+ * This class is named as a reference to {@link DOMTokenList}
29
+ * ({@link https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList | MDN}),
30
+ * with these similarities:
31
+ *
32
+ * - Represents a value whose serialization is a space-separated list.
33
+ * - Each member ("token") is a string (without whitespace).
34
+ * - Provides set-like semantics to determine presence of members.
35
+ * - Provides ordering semantics as determined by the serialization.
36
+ *
37
+ * This class differs from that prior art in that:
38
+ *
39
+ * - It provides a notion of "canonical" members. This is _mostly_ (but not
40
+ * entirely) intended as a convenience to client developers, automatically
41
+ * populating canonical/known tokens for a given parser type in
42
+ * editor-provided autocomplete, etc. **Importantly**, non-canonical tokens
43
+ * _are not ignored_ in either the {@link TokenList}'s types or runtime
44
+ * values.
45
+ *
46
+ * - Provided "canonical" members may also be used to specify
47
+ * {@link TokenListParserOptions.aliases | optional aliases}. (Example: when
48
+ * parsing "appearances", an alias might map an older deprecated appearance to
49
+ * a newer canonical equivalent.) **Importantly**, when a token matches an
50
+ * alias, both that alias _and the token as-specified_ will be present in the
51
+ * produced {@link TokenList}.
52
+ *
53
+ * - As a parser, it is intended to be read-only. The serialized format which it
54
+ * parses is _generally_ the source of truth (excepting e.g. aliases).
55
+ * Notably, and as mentioned above, ordering is determined by:
56
+ *
57
+ * - Iterating each member, as provided by the serialized representation
58
+ *
59
+ * - If that member corresponds to an alias, that alias is yielded first
60
+ *
61
+ * - Regardless of whether the member corresponds to an alias, the member is
62
+ * yielded as-specified
63
+ *
64
+ * - A parsed {@link TokenList} is intended to maximize convenience of read-only
65
+ * access. Despite many _conceptual similarities_, most of the
66
+ * {@link DOMTokenList} **interface** is eschewed in favor of two (mutually
67
+ * equivalent) access mechanisms:
68
+ *
69
+ * - `Iterable<Token>`, with the ordering semantics described above
70
+ * - `Record<Token, boolean>`
71
+ *
72
+ * \* This may change, as we refine requirements. In the future, we may
73
+ * introduce a notion of mutually exclusive tokens (e.g. "appearances" which
74
+ * cannot be used together), which may in turn utilize instance-defined ordering
75
+ * as part of that mechanism.
76
+ */
77
+ export declare class TokenListParser<CanonicalToken extends string, TokenAlias extends CanonicalToken = CanonicalToken> {
78
+ readonly canonicalTokens: readonly CanonicalToken[];
79
+ private readonly aliases;
80
+ constructor(canonicalTokens: readonly CanonicalToken[], options?: TokenListParserOptions<TokenAlias>);
81
+ parseFrom(element: Element, attributeName: TokenListAttributeName): TokenList<CanonicalToken>;
82
+ }
83
+ export type ParsedTokenList<Parser extends TokenListParser<any>> = Parser extends TokenListParser<infer CanonicalToken> ? TokenList<CanonicalToken> : never;
84
+ export {};
@@ -9,6 +9,10 @@ export interface ItemsetElement extends KnownAttributeLocalNamedElement<'itemset
9
9
  }
10
10
  export interface LabelElement extends LocalNamedElement<'label'> {
11
11
  }
12
+ export interface RepeatGroupLabelElement extends LabelElement {
13
+ getAttribute(name: 'form-definition-source'): 'repeat-group';
14
+ getAttribute(name: string): string;
15
+ }
12
16
  export interface RepeatElement extends KnownAttributeLocalNamedElement<'repeat', 'nodeset'> {
13
17
  }
14
18
  export interface ValueElement extends LocalNamedElement<'value'> {
@@ -17,5 +21,6 @@ export declare const getHintElement: (parent: Element) => HintElement | null;
17
21
  export declare const getItemElements: (parent: SelectElement) => readonly ItemElement[];
18
22
  export declare const getItemsetElement: (parent: Element) => ItemsetElement | null;
19
23
  export declare const getLabelElement: (parent: Element) => LabelElement | null;
24
+ export declare const getRepeatGroupLabelElement: (parent: Element) => RepeatGroupLabelElement | null;
20
25
  export declare const getRepeatElement: (parent: Element) => RepeatElement | null;
21
26
  export declare const getValueElement: (parent: ItemElement | ItemsetElement) => ValueElement | null;
@@ -1,6 +1,7 @@
1
1
  import { AnyChildNode } from '../../instance/hierarchy.ts';
2
2
  import { NodeID } from '../../instance/identity.ts';
3
3
  import { ChildrenState } from './createChildrenState.ts';
4
+ import { ReactiveScope } from './scope.ts';
4
5
 
5
6
  export interface EncodedParentState {
6
7
  readonly children: readonly NodeID[];
@@ -16,4 +17,4 @@ export type MaterializedChildren<BaseState extends EncodedParentState, Child ext
16
17
  *
17
18
  * @see {@link createChildrenState} for further detail.
18
19
  */
19
- export declare const materializeCurrentStateChildren: <Child extends AnyChildNode, ParentState extends EncodedParentState>(currentState: ParentState, childrenState: ChildrenState<Child>) => MaterializedChildren<ParentState, Child>;
20
+ export declare const materializeCurrentStateChildren: <Child extends AnyChildNode, ParentState extends EncodedParentState>(scope: ReactiveScope, currentState: ParentState, childrenState: ChildrenState<Child>) => MaterializedChildren<ParentState, Child>;
@@ -1,11 +1,10 @@
1
1
  import { AnyBodyElementDefinition } from '../body/BodyDefinition.ts';
2
- import { RepeatDefinition } from '../body/RepeatDefinition.ts';
3
2
  import { BindDefinition } from './BindDefinition.ts';
4
3
  import { ModelNode, NodeChildren, NodeDefaultValue, NodeDefinition, NodeDefinitionType, NodeInstances, NodeParent } from './NodeDefinition.ts';
5
4
  import { RootDefinition } from './RootDefinition.ts';
6
5
 
7
6
  export type DescendentNodeType = Exclude<NodeDefinitionType, 'root'>;
8
- type DescendentNodeBodyElement = AnyBodyElementDefinition | RepeatDefinition;
7
+ type DescendentNodeBodyElement = AnyBodyElementDefinition;
9
8
  export declare abstract class DescendentNodeDefinition<Type extends DescendentNodeType, BodyElement extends DescendentNodeBodyElement | null = DescendentNodeBodyElement | null> implements NodeDefinition<Type> {
10
9
  readonly parent: NodeParent<Type>;
11
10
  readonly bind: BindDefinition;