@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
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const identity$1 = (value) => value;
2
2
 
3
+ const XMLNS_NAMESPACE_URI$1 = "http://www.w3.org/2000/xmlns/";
3
4
  const JAVAROSA_NAMESPACE_URI$1 = "http://openrosa.org/javarosa";
4
5
  const XFORMS_NAMESPACE_URI$1 = "http://www.w3.org/2002/xforms";
5
6
 
@@ -34051,7 +34052,50 @@ const normalizeBodyRefNodesetAttributes = (body) => {
34051
34052
  }
34052
34053
  }
34053
34054
  };
34054
- const normalizeRepeatGroups = (xformDocument, body) => {
34055
+ const normalizeRepeatGroupAttributes = (group, repeat) => {
34056
+ for (const groupAttribute of group.attributes) {
34057
+ const { localName, namespaceURI, nodeName, value } = groupAttribute;
34058
+ if (
34059
+ // Don't propagate namespace declarations (which appear as attributes in
34060
+ // the browser/XML DOM, either named `xmlns` or with an `xmlns` prefix,
34061
+ // always in the XMLNS namespace).
34062
+ namespaceURI === XMLNS_NAMESPACE_URI$1 || // Don't propagate `ref`, it has been normalized as `nodeset` on the
34063
+ // repeat element.
34064
+ localName === "ref" || // TODO: this accommodates tests of this normalization process, where
34065
+ // certain nodes of interest are given an `id` attribute, and looked up
34066
+ // for the purpose of asserting what was normalized about them. It's
34067
+ // unclear if there's a generally expected behavior around the attribute.
34068
+ localName === "id"
34069
+ ) {
34070
+ continue;
34071
+ }
34072
+ if (localName === "appearance" && namespaceURI === XFORMS_NAMESPACE_URI$1 && repeat.hasAttribute(localName)) {
34073
+ const ref = group.getAttribute("ref");
34074
+ throw new Error(
34075
+ `Failed to normalize conflicting "appearances" attribute of group/repeat "${ref}"`
34076
+ );
34077
+ }
34078
+ repeat.setAttributeNS(namespaceURI, nodeName, value);
34079
+ }
34080
+ };
34081
+ const normalizeRepeatGroupLabel = (group, repeat) => {
34082
+ const groupLabel = Array.from(group.children).find((child) => {
34083
+ return child.localName === "label";
34084
+ });
34085
+ if (groupLabel == null) {
34086
+ return;
34087
+ }
34088
+ const repeatLabel = groupLabel.cloneNode(true);
34089
+ repeatLabel.setAttribute("form-definition-source", "repeat-group");
34090
+ repeat.prepend(repeatLabel);
34091
+ groupLabel.remove();
34092
+ };
34093
+ const unwrapRepeatGroup = (group, repeat) => {
34094
+ normalizeRepeatGroupAttributes(group, repeat);
34095
+ normalizeRepeatGroupLabel(group, repeat);
34096
+ group.replaceWith(repeat);
34097
+ };
34098
+ const normalizeRepeatGroups = (body) => {
34055
34099
  const repeats = body.querySelectorAll("repeat");
34056
34100
  for (const repeat of repeats) {
34057
34101
  const parent = repeat.parentElement;
@@ -34066,11 +34110,8 @@ const normalizeRepeatGroups = (xformDocument, body) => {
34066
34110
  group = parent;
34067
34111
  }
34068
34112
  }
34069
- if (group == null) {
34070
- group = xformDocument.createElementNS(XFORMS_NAMESPACE_URI$1, "group");
34071
- group.setAttribute("ref", repeatNodeset);
34072
- repeat.before(group);
34073
- group.append(repeat);
34113
+ if (group != null) {
34114
+ unwrapRepeatGroup(group, repeat);
34074
34115
  }
34075
34116
  }
34076
34117
  };
@@ -34088,7 +34129,7 @@ const parseNormalizedXForm = (sourceXML, options) => {
34088
34129
  normalizedXML = sourceXML;
34089
34130
  } else {
34090
34131
  normalizeBodyRefNodesetAttributes(body);
34091
- normalizeRepeatGroups(xformDocument, body);
34132
+ normalizeRepeatGroups(body);
34092
34133
  normalizedXML = html.outerHTML;
34093
34134
  }
34094
34135
  return {
@@ -34220,6 +34261,70 @@ class DependencyContext {
34220
34261
  }
34221
34262
  }
34222
34263
 
34264
+ const XML_XPATH_WHITESPACE_SUBPATTERN = "[\\x20\\x09\\x0D\\x0A]";
34265
+ const XML_XPATH_WHITESPACE_PATTERN = new RegExp(XML_XPATH_WHITESPACE_SUBPATTERN, "g");
34266
+ const XML_XPATH_LEADING_TRAILING_WHITESPACE_PATTERN = new RegExp(
34267
+ `^${XML_XPATH_WHITESPACE_SUBPATTERN}+|${XML_XPATH_WHITESPACE_SUBPATTERN}+$`,
34268
+ "g"
34269
+ );
34270
+ const XPATH_REPEATING_WHITESPACE_PATTERN = new RegExp(
34271
+ `${XML_XPATH_WHITESPACE_SUBPATTERN}{2,}`,
34272
+ "g"
34273
+ );
34274
+ const trimXMLXPathWhitespace = (value) => value.replaceAll(XML_XPATH_LEADING_TRAILING_WHITESPACE_PATTERN, "");
34275
+ const normalizeXMLXPathWhitespace = (value) => trimXMLXPathWhitespace(value).replaceAll(XPATH_REPEATING_WHITESPACE_PATTERN, " ");
34276
+ const xmlXPathWhitespaceSeparatedList = (value, options) => {
34277
+ if (options?.ignoreEmpty && value === "") {
34278
+ return [];
34279
+ }
34280
+ return normalizeXMLXPathWhitespace(value).split(XML_XPATH_WHITESPACE_PATTERN);
34281
+ };
34282
+
34283
+ class TokenListParser {
34284
+ constructor(canonicalTokens, options) {
34285
+ this.canonicalTokens = canonicalTokens;
34286
+ this.aliases = new Map(
34287
+ (options?.aliases ?? []).map(({ fromAlias, toCanonical }) => {
34288
+ return [fromAlias, toCanonical];
34289
+ })
34290
+ );
34291
+ }
34292
+ aliases;
34293
+ parseFrom(element, attributeName) {
34294
+ const serialized = element.getAttribute(attributeName) ?? "";
34295
+ const specified = xmlXPathWhitespaceSeparatedList(serialized, {
34296
+ ignoreEmpty: true
34297
+ });
34298
+ const { aliases } = this;
34299
+ const resolved = specified.flatMap((token) => {
34300
+ const alias = aliases.get(token);
34301
+ if (alias == null) {
34302
+ return token;
34303
+ }
34304
+ return [alias, token];
34305
+ });
34306
+ const tokens = new Set(resolved);
34307
+ return new Proxy(
34308
+ {
34309
+ [Symbol.iterator]() {
34310
+ return resolved.values();
34311
+ }
34312
+ },
34313
+ {
34314
+ get(target, property, receiver) {
34315
+ if (typeof property === "symbol") {
34316
+ return Reflect.get(target, property, receiver);
34317
+ }
34318
+ return tokens.has(property);
34319
+ },
34320
+ set() {
34321
+ return false;
34322
+ }
34323
+ }
34324
+ );
34325
+ }
34326
+ }
34327
+
34223
34328
  class BodyElementDefinition extends DependencyContext {
34224
34329
  constructor(form, parent, element) {
34225
34330
  super();
@@ -34244,17 +34349,7 @@ class BodyElementDefinition extends DependencyContext {
34244
34349
  }
34245
34350
  }
34246
34351
 
34247
- class UnsupportedBodyElementDefinition extends BodyElementDefinition {
34248
- static isCompatible() {
34249
- return true;
34250
- }
34251
- category = "UNSUPPORTED";
34252
- type = "UNSUPPORTED";
34253
- reference = null;
34254
- constructor(form, parent, element) {
34255
- super(form, parent, element);
34256
- }
34257
- }
34352
+ const structureElementAppearanceParser = new TokenListParser(["field-list", "table-list"]);
34258
34353
 
34259
34354
  const SUPPORTS_SCOPE_CHILD_SELECTOR = (() => {
34260
34355
  const parent = document.createElement("parent");
@@ -34316,7 +34411,10 @@ const hintLookup = new ScopedElementLookup(":scope > hint", "hint");
34316
34411
  const itemLookup = new ScopedElementLookup(":scope > item", "item");
34317
34412
  const itemsetLookup = new ScopedElementLookup(":scope > itemset[nodeset]", "itemset[nodeset]");
34318
34413
  const labelLookup = new ScopedElementLookup(":scope > label", "label");
34319
- const repeatLookup = new ScopedElementLookup(":scope > repeat[nodeset]", "repeat[nodeset]");
34414
+ const repeatGroupLabelLookup = new ScopedElementLookup(
34415
+ ':scope > label[form-definition-source="repeat-group"]',
34416
+ 'label[form-definition-source="repeat-group"]'
34417
+ );
34320
34418
  const valueLookup = new ScopedElementLookup(":scope > value", "value");
34321
34419
  const getHintElement = (parent) => {
34322
34420
  return hintLookup.getElement(parent);
@@ -34330,8 +34428,8 @@ const getItemsetElement = (parent) => {
34330
34428
  const getLabelElement = (parent) => {
34331
34429
  return labelLookup.getElement(parent);
34332
34430
  };
34333
- const getRepeatElement = (parent) => {
34334
- return repeatLookup.getElement(parent);
34431
+ const getRepeatGroupLabelElement = (parent) => {
34432
+ return repeatGroupLabelLookup.getElement(parent);
34335
34433
  };
34336
34434
  const getValueElement = (parent) => {
34337
34435
  return valueLookup.getElement(parent);
@@ -34639,17 +34737,6 @@ class TextElementDefinition extends BodyElementDefinition {
34639
34737
  }
34640
34738
  }
34641
34739
 
34642
- class HintDefinition extends TextElementDefinition {
34643
- static forElement(form, definition) {
34644
- const hintElement = getHintElement(definition.element);
34645
- if (hintElement == null) {
34646
- return null;
34647
- }
34648
- return new this(form, definition, hintElement);
34649
- }
34650
- type = "hint";
34651
- }
34652
-
34653
34740
  class LabelDefinition extends TextElementDefinition {
34654
34741
  static staticDefinition(form, definition) {
34655
34742
  const labelElement = getLabelElement(definition.element);
@@ -34661,6 +34748,13 @@ class LabelDefinition extends TextElementDefinition {
34661
34748
  static forControl(form, control) {
34662
34749
  return this.staticDefinition(form, control);
34663
34750
  }
34751
+ static forRepeatGroup(form, repeat) {
34752
+ const repeatGroupLabel = getRepeatGroupLabelElement(repeat.element);
34753
+ if (repeatGroupLabel == null) {
34754
+ return null;
34755
+ }
34756
+ return new this(form, repeat, repeatGroupLabel);
34757
+ }
34664
34758
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
34665
34759
  static forGroup(form, group) {
34666
34760
  return this.staticDefinition(form, group);
@@ -34678,6 +34772,68 @@ class LabelDefinition extends TextElementDefinition {
34678
34772
  type = "label";
34679
34773
  }
34680
34774
 
34775
+ class RepeatElementDefinition extends BodyElementDefinition {
34776
+ static isCompatible(localName) {
34777
+ return localName === "repeat";
34778
+ }
34779
+ category = "structure";
34780
+ type = "repeat";
34781
+ reference;
34782
+ appearances;
34783
+ label;
34784
+ // TODO: this will fall into the growing category of non-`BindExpression`
34785
+ // cases which have roughly the same design story.
34786
+ countExpression;
34787
+ isFixedCount;
34788
+ children;
34789
+ constructor(form, parent, element) {
34790
+ super(form, parent, element);
34791
+ this.label = LabelDefinition.forRepeatGroup(form, this);
34792
+ const reference = element.getAttribute("nodeset");
34793
+ if (reference == null) {
34794
+ throw new Error("Invalid repeat: missing `nodeset` reference");
34795
+ }
34796
+ this.reference = reference;
34797
+ this.appearances = structureElementAppearanceParser.parseFrom(element, "appearance");
34798
+ this.countExpression = element.getAttributeNS(JAVAROSA_NAMESPACE_URI$1, "count");
34799
+ const childElements = Array.from(element.children).filter((childElement) => {
34800
+ const { localName } = childElement;
34801
+ return localName !== "label" && localName !== "group-label";
34802
+ });
34803
+ const children = BodyDefinition.getChildElementDefinitions(form, this, element, childElements);
34804
+ this.children = children;
34805
+ const noAddRemove = element.getAttributeNS(JAVAROSA_NAMESPACE_URI$1, "noAddRemove")?.trim().replaceAll(/\s+/g, "") ?? "false()";
34806
+ this.isFixedCount = noAddRemove === "true()";
34807
+ }
34808
+ toJSON() {
34809
+ const { form, parent, ...rest } = this;
34810
+ return rest;
34811
+ }
34812
+ }
34813
+
34814
+ class UnsupportedBodyElementDefinition extends BodyElementDefinition {
34815
+ static isCompatible() {
34816
+ return true;
34817
+ }
34818
+ category = "UNSUPPORTED";
34819
+ type = "UNSUPPORTED";
34820
+ reference = null;
34821
+ constructor(form, parent, element) {
34822
+ super(form, parent, element);
34823
+ }
34824
+ }
34825
+
34826
+ class HintDefinition extends TextElementDefinition {
34827
+ static forElement(form, definition) {
34828
+ const hintElement = getHintElement(definition.element);
34829
+ if (hintElement == null) {
34830
+ return null;
34831
+ }
34832
+ return new this(form, definition, hintElement);
34833
+ }
34834
+ type = "hint";
34835
+ }
34836
+
34681
34837
  class ControlDefinition extends BodyElementDefinition {
34682
34838
  category = "control";
34683
34839
  reference;
@@ -34695,13 +34851,84 @@ class ControlDefinition extends BodyElementDefinition {
34695
34851
  }
34696
34852
  }
34697
34853
 
34854
+ const inputAppearanceParser = new TokenListParser([
34855
+ "multiline",
34856
+ "numbers",
34857
+ "url",
34858
+ "thousand-sep",
34859
+ // date (TODO: data types)
34860
+ "no-calendar",
34861
+ "month-year",
34862
+ "year",
34863
+ // date > calendars
34864
+ "ethiopian",
34865
+ "coptic",
34866
+ "islamic",
34867
+ "bikram-sambat",
34868
+ "myanmar",
34869
+ "persian",
34870
+ // geo (TODO: data types)
34871
+ "placement-map",
34872
+ "maps",
34873
+ // image/media (TODO: move to eventual `<upload>`?)
34874
+ "hidden-answer",
34875
+ "annotate",
34876
+ "draw",
34877
+ "signature",
34878
+ "new-front",
34879
+ "new",
34880
+ "front",
34881
+ // *?
34882
+ "printer",
34883
+ // Note: actual usage uses `printer:...` (like `ex:...`).
34884
+ "masked"
34885
+ ]);
34886
+
34698
34887
  class InputDefinition extends ControlDefinition {
34699
34888
  static isCompatible(localName) {
34700
34889
  return localName === "input";
34701
34890
  }
34702
34891
  type = "input";
34892
+ appearances;
34893
+ constructor(form, parent, element) {
34894
+ super(form, parent, element);
34895
+ this.appearances = inputAppearanceParser.parseFrom(element, "appearance");
34896
+ }
34703
34897
  }
34704
34898
 
34899
+ const selectAppearanceParser = new TokenListParser(
34900
+ [
34901
+ // From XLSForm Docs:
34902
+ "compact",
34903
+ "horizontal",
34904
+ "horizontal-compact",
34905
+ "label",
34906
+ "list-nolabel",
34907
+ "minimal",
34908
+ // From Collect `Appearances.kt`:
34909
+ "columns",
34910
+ "columns-1",
34911
+ "columns-2",
34912
+ "columns-3",
34913
+ "columns-4",
34914
+ "columns-5",
34915
+ // Note: Collect supports arbitrary columns-n. Technically we do too (we parse
34916
+ // out any appearance, not just those we know about). But we'll only include
34917
+ // types/defaults up to 5.
34918
+ "columns-pack",
34919
+ "autocomplete",
34920
+ // TODO: these are `<select1>` only
34921
+ "likert",
34922
+ "quick",
34923
+ "quickcompact",
34924
+ "map"
34925
+ // "quick map"
34926
+ ],
34927
+ {
34928
+ aliases: [{ fromAlias: "search", toCanonical: "autocomplete" }]
34929
+ }
34930
+ );
34931
+
34705
34932
  class ItemDefinition extends BodyElementDefinition {
34706
34933
  constructor(form, parent, element) {
34707
34934
  const valueElement = getValueElement(element);
@@ -34755,7 +34982,11 @@ class ItemsetDefinition extends BodyElementDefinition {
34755
34982
  }
34756
34983
  }
34757
34984
 
34758
- const selectLocalNames = /* @__PURE__ */ new Set(["rank", "select", "select1"]);
34985
+ const selectLocalNames = /* @__PURE__ */ new Set([
34986
+ /* 'rank', */
34987
+ "select",
34988
+ "select1"
34989
+ ]);
34759
34990
  const isSelectElement = (element, localName = element.localName) => {
34760
34991
  return selectLocalNames.has(localName);
34761
34992
  };
@@ -34768,6 +34999,7 @@ class SelectDefinition extends ControlDefinition {
34768
34999
  }
34769
35000
  type;
34770
35001
  element;
35002
+ appearances;
34771
35003
  itemset;
34772
35004
  items;
34773
35005
  constructor(form, parent, element) {
@@ -34775,8 +35007,9 @@ class SelectDefinition extends ControlDefinition {
34775
35007
  throw new Error(`Invalid select element: <${element.nodeName}>`);
34776
35008
  }
34777
35009
  super(form, parent, element);
34778
- this.element = element;
34779
35010
  this.type = element.localName;
35011
+ this.element = element;
35012
+ this.appearances = selectAppearanceParser.parseFrom(element, "appearance");
34780
35013
  const itemsetElement = getItemsetElement(element);
34781
35014
  const itemElements = getItemElements(element);
34782
35015
  if (itemsetElement == null) {
@@ -34809,22 +35042,16 @@ class UpsertableMap extends Map {
34809
35042
  }
34810
35043
 
34811
35044
  class BaseGroupDefinition extends BodyElementDefinition {
35045
+ // TODO: does this really accomplish anything? It seems highly unlikely it
35046
+ // has enough performance benefit to outweigh its memory and lookup costs.
34812
35047
  static groupTypes = new UpsertableMap();
34813
35048
  static getGroupType(localName, element) {
34814
35049
  return this.groupTypes.upsert(element, () => {
34815
35050
  if (localName !== "group") {
34816
35051
  return null;
34817
35052
  }
34818
- const ref = element.getAttribute("ref");
34819
- if (ref != null) {
34820
- const repeat = getRepeatElement(element);
34821
- if (repeat == null) {
34822
- return "logical-group";
34823
- }
34824
- if (repeat.getAttribute("nodeset") === ref) {
34825
- return "repeat-group";
34826
- }
34827
- throw new Error("Unexpected <repeat> child of unrelated <group>");
35053
+ if (element.hasAttribute("ref")) {
35054
+ return "logical-group";
34828
35055
  }
34829
35056
  const label = getLabelElement(element);
34830
35057
  if (label == null) {
@@ -34836,18 +35063,20 @@ class BaseGroupDefinition extends BodyElementDefinition {
34836
35063
  category = "structure";
34837
35064
  children;
34838
35065
  reference;
35066
+ appearances;
34839
35067
  label;
34840
35068
  constructor(form, parent, element, children) {
34841
35069
  super(form, parent, element);
34842
35070
  this.children = children ?? this.getChildren(element);
34843
35071
  this.reference = element.getAttribute("ref");
35072
+ this.appearances = structureElementAppearanceParser.parseFrom(element, "appearance");
34844
35073
  this.label = LabelDefinition.forGroup(form, this);
34845
35074
  }
34846
35075
  getChildren(element) {
34847
35076
  const { form } = this;
34848
35077
  const children = Array.from(element.children).filter((child) => {
34849
35078
  const childName = child.localName;
34850
- return childName !== "label" && childName !== "repeat";
35079
+ return childName !== "label";
34851
35080
  });
34852
35081
  return BodyDefinition.getChildElementDefinitions(form, this, element, children);
34853
35082
  }
@@ -34876,54 +35105,6 @@ class PresentationGroupDefinition extends BaseGroupDefinition {
34876
35105
  }
34877
35106
  }
34878
35107
 
34879
- class RepeatDefinition extends BodyElementDefinition {
34880
- constructor(form, groupDefinition, element) {
34881
- super(form, groupDefinition, element);
34882
- this.groupDefinition = groupDefinition;
34883
- const reference = element.getAttribute("nodeset");
34884
- if (reference == null) {
34885
- throw new Error("Invalid repeat: missing `nodeset` reference");
34886
- }
34887
- this.reference = reference;
34888
- this.countExpression = element.getAttributeNS(JAVAROSA_NAMESPACE_URI$1, "count");
34889
- this.children = groupDefinition.getChildren(element);
34890
- const noAddRemove = element.getAttributeNS(JAVAROSA_NAMESPACE_URI$1, "noAddRemove")?.trim().replaceAll(/\s+/g, "") ?? "false()";
34891
- this.isFixedCount = noAddRemove === "true()";
34892
- }
34893
- category = "structure";
34894
- type = "repeat";
34895
- reference;
34896
- // TODO: this will fall into the growing category of non-`BindExpression`
34897
- // cases which have roughly the same design story.
34898
- countExpression;
34899
- isFixedCount;
34900
- children;
34901
- toJSON() {
34902
- const { form, groupDefinition, parent, ...rest } = this;
34903
- return rest;
34904
- }
34905
- }
34906
-
34907
- class RepeatGroupDefinition extends BaseGroupDefinition {
34908
- static isCompatible(localName, element) {
34909
- return this.getGroupType(localName, element) === "repeat-group";
34910
- }
34911
- type = "repeat-group";
34912
- repeat;
34913
- get repeatChildren() {
34914
- return this.repeat.children;
34915
- }
34916
- constructor(form, parent, element) {
34917
- const repeat = getRepeatElement(element);
34918
- if (repeat == null) {
34919
- throw new Error("Invalid repeat-group");
34920
- }
34921
- super(form, parent, element);
34922
- const repeatDefinition = new RepeatDefinition(form, this, repeat);
34923
- this.repeat = repeatDefinition;
34924
- }
34925
- }
34926
-
34927
35108
  class StructuralGroupDefinition extends BaseGroupDefinition {
34928
35109
  static isCompatible(localName, element) {
34929
35110
  return this.getGroupType(localName, element) === "structural-group";
@@ -34932,7 +35113,7 @@ class StructuralGroupDefinition extends BaseGroupDefinition {
34932
35113
  }
34933
35114
 
34934
35115
  const BodyElementDefinitionConstructors = [
34935
- RepeatGroupDefinition,
35116
+ RepeatElementDefinition,
34936
35117
  LogicalGroupDefinition,
34937
35118
  PresentationGroupDefinition,
34938
35119
  StructuralGroupDefinition,
@@ -34947,12 +35128,12 @@ class BodyElementMap extends Map {
34947
35128
  mapElementsByReference(elements) {
34948
35129
  for (const element of elements) {
34949
35130
  const { reference } = element;
34950
- if (element instanceof RepeatGroupDefinition) {
35131
+ if (element instanceof RepeatElementDefinition) {
34951
35132
  if (reference == null) {
34952
- throw new Error("Missing reference for repeat/repeat group");
35133
+ throw new Error("Missing reference for repeat");
34953
35134
  }
34954
35135
  this.set(reference, element);
34955
- this.mapElementsByReference(element.repeatChildren);
35136
+ this.mapElementsByReference(element.children);
34956
35137
  }
34957
35138
  if (element instanceof LogicalGroupDefinition || element instanceof PresentationGroupDefinition || element instanceof StructuralGroupDefinition) {
34958
35139
  if (reference != null) {
@@ -34978,6 +35159,10 @@ class BodyElementMap extends Map {
34978
35159
  return Object.fromEntries(this.entries());
34979
35160
  }
34980
35161
  }
35162
+ const bodyClassParser = new TokenListParser([
35163
+ "pages"
35164
+ /*, 'theme-grid' */
35165
+ ]);
34981
35166
  class BodyDefinition extends DependencyContext {
34982
35167
  constructor(form) {
34983
35168
  super();
@@ -34985,6 +35170,7 @@ class BodyDefinition extends DependencyContext {
34985
35170
  const { body: element } = form.xformDOM;
34986
35171
  this.reference = form.rootReference;
34987
35172
  this.element = element;
35173
+ this.classes = bodyClassParser.parseFrom(element, "class");
34988
35174
  this.elements = BodyDefinition.getChildElementDefinitions(form, this, element);
34989
35175
  this.elementsByReference = new BodyElementMap(this.elements);
34990
35176
  }
@@ -35000,6 +35186,23 @@ class BodyDefinition extends DependencyContext {
35000
35186
  });
35001
35187
  }
35002
35188
  element;
35189
+ /**
35190
+ * @todo this class is already an oddity in that it's **like** an element
35191
+ * definition, but it isn't one itself. Adding this property here emphasizes
35192
+ * that awkwardness. It also extends the applicable scope where instances of
35193
+ * this class are accessed. While it's still ephemeral, it's anticipated that
35194
+ * this extension might cause some disomfort. If so, the most plausible
35195
+ * alternative is an additional refactor to:
35196
+ *
35197
+ * 1. Introduce a `BodyElementDefinition` sublass for `<h:body>`.
35198
+ * 2. Disambiguate the respective names of those, in some reasonable way.
35199
+ * 3. Add a layer of indirection between this class and that new body element
35200
+ * definition's class.
35201
+ * 4. At that point, we may as well prioritize the little bit of grunt work to
35202
+ * pass the `BodyDefinition` instance by reference rather than assigning it
35203
+ * to anything.
35204
+ */
35205
+ classes;
35003
35206
  elements;
35004
35207
  elementsByReference;
35005
35208
  // DependencyContext
@@ -35008,13 +35211,6 @@ class BodyDefinition extends DependencyContext {
35008
35211
  getBodyElement(reference) {
35009
35212
  return this.elementsByReference.get(reference) ?? null;
35010
35213
  }
35011
- getRepeatGroup(reference) {
35012
- const element = this.getBodyElement(reference);
35013
- if (element?.type === "repeat-group") {
35014
- return element;
35015
- }
35016
- return null;
35017
- }
35018
35214
  toJSON() {
35019
35215
  const { form, ...rest } = this;
35020
35216
  return rest;
@@ -35247,17 +35443,11 @@ class DescendentNodeDefinition {
35247
35443
  }
35248
35444
 
35249
35445
  class RepeatInstanceDefinition extends DescendentNodeDefinition {
35250
- constructor(sequence, node) {
35251
- const {
35252
- bind,
35253
- bodyElement: repeatGroupBodyElement,
35254
- parent: repeatSequenceParent,
35255
- root
35256
- } = sequence;
35257
- super(repeatSequenceParent, bind, repeatGroupBodyElement.repeat);
35258
- this.sequence = sequence;
35446
+ constructor(range, node) {
35447
+ const { bind, bodyElement, parent, root } = range;
35448
+ super(parent, bind, bodyElement);
35259
35449
  this.node = node;
35260
- this.nodeName = sequence.nodeName;
35450
+ this.nodeName = range.nodeName;
35261
35451
  this.children = root.buildSubtree(this);
35262
35452
  }
35263
35453
  type = "repeat-instance";
@@ -35266,7 +35456,7 @@ class RepeatInstanceDefinition extends DescendentNodeDefinition {
35266
35456
  instances = null;
35267
35457
  defaultValue = null;
35268
35458
  toJSON() {
35269
- const { bind, bodyElement, parent, root, sequence, ...rest } = this;
35459
+ const { bind, bodyElement, parent, root, ...rest } = this;
35270
35460
  return rest;
35271
35461
  }
35272
35462
  }
@@ -35305,15 +35495,9 @@ const splitInstanceNodes = (modelNodes) => {
35305
35495
  return [template, ...modelNodes];
35306
35496
  };
35307
35497
  class RepeatTemplateDefinition extends DescendentNodeDefinition {
35308
- constructor(sequence, templateNode) {
35309
- const {
35310
- bind,
35311
- bodyElement: repeatGroupBodyElement,
35312
- parent: repeatSequenceParent,
35313
- root
35314
- } = sequence;
35315
- super(repeatSequenceParent, bind, repeatGroupBodyElement.repeat);
35316
- this.sequence = sequence;
35498
+ constructor(range, templateNode) {
35499
+ const { bind, bodyElement, parent, root } = range;
35500
+ super(parent, bind, bodyElement);
35317
35501
  this.templateNode = templateNode;
35318
35502
  const node = templateNode.cloneNode(true);
35319
35503
  node.removeAttributeNS(JAVAROSA_NAMESPACE_URI$1, "template");
@@ -35321,14 +35505,14 @@ class RepeatTemplateDefinition extends DescendentNodeDefinition {
35321
35505
  this.nodeName = node.localName;
35322
35506
  this.children = root.buildSubtree(this);
35323
35507
  }
35324
- static parseModelNodes(sequence, modelNodes) {
35325
- const { bind } = sequence;
35508
+ static parseModelNodes(range, modelNodes) {
35509
+ const { bind } = range;
35326
35510
  let template = repeatTemplates.get(bind);
35327
35511
  let instanceNodes;
35328
35512
  if (template == null) {
35329
35513
  const [templateNode, ...rest] = splitInstanceNodes(modelNodes);
35330
35514
  instanceNodes = rest;
35331
- template = new this(sequence, templateNode);
35515
+ template = new this(range, templateNode);
35332
35516
  } else {
35333
35517
  const duplicateTemplate = modelNodes.find(
35334
35518
  (node) => node.hasAttributeNS(JAVAROSA_NAMESPACE_URI$1, "template")
@@ -35350,12 +35534,12 @@ class RepeatTemplateDefinition extends DescendentNodeDefinition {
35350
35534
  instances = null;
35351
35535
  defaultValue = null;
35352
35536
  toJSON() {
35353
- const { bind, bodyElement, parent, root, sequence, ...rest } = this;
35537
+ const { bind, bodyElement, parent, root, ...rest } = this;
35354
35538
  return rest;
35355
35539
  }
35356
35540
  }
35357
35541
 
35358
- class RepeatSequenceDefinition extends DescendentNodeDefinition {
35542
+ class RepeatRangeDefinition extends DescendentNodeDefinition {
35359
35543
  // TODO: if an implicit template is derived from an instance in a form
35360
35544
  // definition, should its default values (if any) be cleared? Probably!
35361
35545
  static createTemplateElement(instanceElement) {
@@ -35364,7 +35548,7 @@ class RepeatSequenceDefinition extends DescendentNodeDefinition {
35364
35548
  static createInstanceElement(templateElement) {
35365
35549
  return templateElement.cloneNode(true);
35366
35550
  }
35367
- type = "repeat-sequence";
35551
+ type = "repeat-range";
35368
35552
  template;
35369
35553
  children = null;
35370
35554
  instances;
@@ -35388,7 +35572,7 @@ class RepeatSequenceDefinition extends DescendentNodeDefinition {
35388
35572
 
35389
35573
  class SubtreeDefinition extends DescendentNodeDefinition {
35390
35574
  constructor(parent, bind, bodyElement, node) {
35391
- if (bodyElement != null && (bodyElement.category !== "structure" || bodyElement.type === "repeat-group")) {
35575
+ if (bodyElement != null && (bodyElement.category !== "structure" || bodyElement.type === "repeat")) {
35392
35576
  throw new Error(`Unexpected body element for nodeset ${bind.nodeset}`);
35393
35577
  }
35394
35578
  super(parent, bind, bodyElement);
@@ -35430,9 +35614,10 @@ class ValueNodeDefinition extends DescendentNodeDefinition {
35430
35614
  }
35431
35615
 
35432
35616
  class RootDefinition {
35433
- constructor(form, model) {
35617
+ constructor(form, model, classes) {
35434
35618
  this.form = form;
35435
35619
  this.model = model;
35620
+ this.classes = classes;
35436
35621
  const { primaryInstanceRoot } = form.xformDOM;
35437
35622
  const { localName: rootNodeName } = primaryInstanceRoot;
35438
35623
  this.nodeName = rootNodeName;
@@ -35481,13 +35666,8 @@ class RootDefinition {
35481
35666
  const bind = binds.getOrCreateBindDefinition(nodeset);
35482
35667
  const bodyElement = body.getBodyElement(nodeset);
35483
35668
  const [firstChild, ...restChildren] = children;
35484
- const repeatGroup = body.getRepeatGroup(nodeset);
35485
- if (repeatGroup != null) {
35486
- const repeatDefinition = bodyElement.repeat;
35487
- if (repeatDefinition == null) {
35488
- throw 'TODO: this is why I have hesitated to pick an "is repeat" predicate direction';
35489
- }
35490
- return new RepeatSequenceDefinition(parent, bind, repeatGroup, children);
35669
+ if (bodyElement?.type === "repeat") {
35670
+ return new RepeatRangeDefinition(parent, bind, bodyElement, children);
35491
35671
  }
35492
35672
  if (restChildren.length) {
35493
35673
  throw new Error(`Unexpected: multiple elements for non-repeat nodeset: ${nodeset}`);
@@ -35510,7 +35690,7 @@ class ModelDefinition {
35510
35690
  constructor(form) {
35511
35691
  this.form = form;
35512
35692
  this.binds = ModelBindMap.fromModel(this);
35513
- this.root = new RootDefinition(form, this);
35693
+ this.root = new RootDefinition(form, this, form.body.classes);
35514
35694
  }
35515
35695
  binds;
35516
35696
  root;
@@ -35994,11 +36174,26 @@ const createUniqueId = (() => {
35994
36174
 
35995
36175
  const createChildrenState = (parent) => {
35996
36176
  return parent.scope.runTask(() => {
35997
- const children = createSignal([]);
35998
- const [getChildren, setChildren] = children;
35999
- const childIds = createMemo(() => {
36000
- return getChildren().map((child) => child.nodeId);
36001
- });
36177
+ const baseState = createSignal([]);
36178
+ const [getChildren, baseSetChildren] = baseState;
36179
+ const ids = createSignal([]);
36180
+ const [childIds, setChildIds] = ids;
36181
+ const setChildren = (valueOrSetterCallback) => {
36182
+ let setterCallback;
36183
+ if (typeof valueOrSetterCallback === "function") {
36184
+ setterCallback = valueOrSetterCallback;
36185
+ } else {
36186
+ setterCallback = (_) => valueOrSetterCallback;
36187
+ }
36188
+ return baseSetChildren((prev) => {
36189
+ const result = setterCallback(prev);
36190
+ setChildIds(() => {
36191
+ return result.map((child) => child.nodeId);
36192
+ });
36193
+ return result;
36194
+ });
36195
+ };
36196
+ const children = [getChildren, setChildren];
36002
36197
  return {
36003
36198
  children,
36004
36199
  getChildren,
@@ -36008,13 +36203,13 @@ const createChildrenState = (parent) => {
36008
36203
  });
36009
36204
  };
36010
36205
 
36011
- const materializeCurrentStateChildren = (currentState, childrenState) => {
36206
+ const materializeCurrentStateChildren = (scope, currentState, childrenState) => {
36012
36207
  const baseState = currentState;
36013
36208
  const proxyTarget = baseState;
36014
36209
  return new Proxy(proxyTarget, {
36015
36210
  get(_, key) {
36016
36211
  if (key === "children") {
36017
- currentState.children;
36212
+ scope.runTask(() => currentState.children);
36018
36213
  const children = childrenState.getChildren();
36019
36214
  return children;
36020
36215
  }
@@ -36217,104 +36412,59 @@ const declareNodeID = (id) => {
36217
36412
  };
36218
36413
 
36219
36414
  class InstanceNode {
36220
- constructor(engineConfig, parent, definition) {
36415
+ constructor(engineConfig, parent, definition, options) {
36221
36416
  this.engineConfig = engineConfig;
36222
36417
  this.parent = parent;
36223
36418
  this.definition = definition;
36419
+ this.computeReference = options?.computeReference ?? this.computeChildStepReference;
36224
36420
  this.scope = createReactiveScope();
36225
36421
  this.engineConfig = engineConfig;
36226
36422
  this.nodeId = declareNodeID(engineConfig.createUniqueId());
36227
36423
  this.definition = definition;
36228
- const checkStateInitialized = () => this.engineState != null;
36229
- const [isStateInitialized, setStateInitialized] = createSignal(checkStateInitialized());
36230
- this.isStateInitialized = isStateInitialized;
36231
- queueMicrotask(() => {
36232
- if (checkStateInitialized()) {
36233
- setStateInitialized(true);
36234
- } else {
36235
- throw new Error("Node state was never initialized");
36236
- }
36237
- });
36238
- }
36239
- isStateInitialized;
36240
- /**
36241
- * Provides a generalized mechanism for accessing a reactive state value
36242
- * during a node's construction, while {@link engineState} is still being
36243
- * defined and thus isn't assigned.
36244
- *
36245
- * The fallback value specified in {@link options} will be returned on access
36246
- * until {@link isStateInitialized} returns true. This ensures:
36247
- *
36248
- * - a value of the expected type will be available
36249
- * - any read access will become reactive to the actual state, once it has
36250
- * been initialized and {@link engineState} is assigned
36251
- *
36252
- * @todo This is one among several chicken/egg problems encountered trying to
36253
- * support state initialization in which some aspects of the state derive from
36254
- * other aspects of it. It would be nice to dispense with this entirely. But
36255
- * if it must persist, we should also consider replacing the method with a
36256
- * direct accessor once state initialization completes, so the initialized
36257
- * check is only called until it becomes impertinent.
36258
- */
36259
- getInitializedState(key, options) {
36260
- if (this.isStateInitialized()) {
36261
- return this.engineState[key];
36262
- }
36263
- return options.uninitializedFallback;
36264
- }
36265
- /**
36266
- * @package Exposed on every node type to facilitate inheritance, as well as
36267
- * conditional behavior for value nodes.
36268
- */
36269
- get isReadonly() {
36270
- return this.getInitializedState("readonly", {
36271
- uninitializedFallback: false
36272
- });
36273
- }
36274
- /**
36275
- * @package Exposed on every node type to facilitate inheritance, as well as
36276
- * conditional behavior for value nodes.
36277
- */
36278
- get isRelevant() {
36279
- return this.getInitializedState("relevant", {
36280
- uninitializedFallback: true
36281
- });
36282
36424
  }
36283
36425
  // BaseNode: identity
36284
36426
  nodeId;
36285
36427
  // EvaluationContext *and* Subscribable: node-specific
36286
36428
  scope;
36429
+ computeReference;
36430
+ computeChildStepReference = (parent, definition) => {
36431
+ if (parent == null) {
36432
+ throw new Error(
36433
+ "Cannot compute child step reference of node without parent (was this called from `Root`?)"
36434
+ );
36435
+ }
36436
+ return `${parent.contextReference()}/${definition.nodeName}`;
36437
+ };
36287
36438
  // EvaluationContext: node-specific
36288
- get contextReference() {
36439
+ contextReference = () => {
36289
36440
  return this.computeReference(this.parent, this.definition);
36290
- }
36291
- getNodeByReference(visited, dependencyReference) {
36441
+ };
36442
+ getNodesByReference(visited, dependencyReference) {
36292
36443
  if (visited.has(this)) {
36293
- return null;
36444
+ return [];
36294
36445
  }
36295
36446
  visited.add(this);
36296
36447
  const { nodeset } = this.definition;
36297
36448
  if (dependencyReference === nodeset) {
36298
- return this;
36449
+ if (this.nodeType === "repeat-instance") {
36450
+ return [this.parent];
36451
+ }
36452
+ return [this];
36299
36453
  }
36300
36454
  if (dependencyReference.startsWith(`${nodeset}/`) || dependencyReference.startsWith(`${nodeset}[`)) {
36301
- const children = this.getChildren();
36302
- if (children == null) {
36303
- return null;
36304
- }
36305
- for (const child of children) {
36306
- const dependency = child.getNodeByReference(visited, dependencyReference);
36307
- if (dependency != null) {
36308
- return dependency;
36309
- }
36310
- }
36455
+ return this.getChildren().flatMap((child) => {
36456
+ return child.getNodesByReference(visited, dependencyReference);
36457
+ });
36311
36458
  }
36312
- return this.parent?.getNodeByReference(visited, dependencyReference) ?? null;
36459
+ return this.parent?.getNodesByReference(visited, dependencyReference) ?? [];
36313
36460
  }
36314
36461
  // EvaluationContext: node-relative
36315
- getSubscribableDependencyByReference(reference) {
36316
- const visited = /* @__PURE__ */ new WeakSet();
36317
- return this.getNodeByReference(visited, reference);
36462
+ getSubscribableDependenciesByReference(reference) {
36463
+ if (this.nodeType === "root") {
36464
+ const visited = /* @__PURE__ */ new WeakSet();
36465
+ return this.getNodesByReference(visited, reference);
36466
+ }
36467
+ return this.root.getSubscribableDependenciesByReference(reference);
36318
36468
  }
36319
36469
  // SubscribableDependency
36320
36470
  /**
@@ -36406,7 +36556,7 @@ const createComputedExpression = (context, dependentExpression) => {
36406
36556
  }
36407
36557
  const getReferencedDependencies = createMemo(() => {
36408
36558
  return dependencyReferences.flatMap((reference) => {
36409
- return context.getSubscribableDependencyByReference(reference) ?? [];
36559
+ return context.getSubscribableDependenciesByReference(reference) ?? [];
36410
36560
  });
36411
36561
  });
36412
36562
  let getDependencies;
@@ -36497,42 +36647,45 @@ const createNodeLabel = (context, definition) => {
36497
36647
  };
36498
36648
 
36499
36649
  class DescendantNode extends InstanceNode {
36500
- constructor(parent, definition) {
36501
- super(parent.engineConfig, parent, definition);
36650
+ constructor(parent, definition, options) {
36651
+ super(parent.engineConfig, parent, definition, options);
36502
36652
  this.parent = parent;
36503
36653
  this.definition = definition;
36504
36654
  const { evaluator, root } = parent;
36505
36655
  this.root = root;
36506
36656
  this.evaluator = evaluator;
36507
36657
  this.contextNode = this.initializeContextNode(parent.contextNode, definition.nodeName);
36508
- }
36658
+ const { readonly, relevant, required } = definition.bind;
36659
+ this.isSelfReadonly = createComputedExpression(this, readonly);
36660
+ this.isSelfRelevant = createComputedExpression(this, relevant);
36661
+ this.isRequired = createComputedExpression(this, required);
36662
+ }
36663
+ hasReadonlyAncestor = () => {
36664
+ const { parent } = this;
36665
+ return parent.hasReadonlyAncestor() || parent.isReadonly();
36666
+ };
36667
+ isSelfReadonly;
36668
+ isReadonly = () => {
36669
+ if (this.hasReadonlyAncestor()) {
36670
+ return true;
36671
+ }
36672
+ return this.isSelfReadonly();
36673
+ };
36674
+ hasNonRelevantAncestor = () => {
36675
+ const { parent } = this;
36676
+ return parent.hasNonRelevantAncestor() || !parent.isRelevant();
36677
+ };
36678
+ isSelfRelevant;
36679
+ isRelevant = () => {
36680
+ if (this.hasNonRelevantAncestor()) {
36681
+ return false;
36682
+ }
36683
+ return this.isSelfRelevant();
36684
+ };
36685
+ isRequired;
36509
36686
  root;
36510
36687
  evaluator;
36511
36688
  contextNode;
36512
- computeChildStepReference(parent) {
36513
- return `${parent.contextReference}/${this.definition.nodeName}`;
36514
- }
36515
- buildSharedStateSpec(parent, definition) {
36516
- return this.scope.runTask(() => {
36517
- const reference = createMemo(() => this.contextReference);
36518
- const { bind } = definition;
36519
- const selfReadonly = createComputedExpression(this, bind.readonly);
36520
- const readonly = createMemo(() => {
36521
- return parent.isReadonly || selfReadonly();
36522
- });
36523
- const selfRelevant = createComputedExpression(this, bind.relevant);
36524
- const relevant = createMemo(() => {
36525
- return parent.isRelevant && selfRelevant();
36526
- });
36527
- const required = createComputedExpression(this, bind.required);
36528
- return {
36529
- reference,
36530
- readonly,
36531
- relevant,
36532
- required
36533
- };
36534
- });
36535
- }
36536
36689
  createContextNode(parentContextNode, nodeName) {
36537
36690
  return parentContextNode.ownerDocument.createElement(nodeName);
36538
36691
  }
@@ -36595,16 +36748,21 @@ class Group extends DescendantNode {
36595
36748
  state;
36596
36749
  engineState;
36597
36750
  // GroupNode
36598
- currentState;
36599
36751
  nodeType = "group";
36752
+ appearances;
36753
+ currentState;
36600
36754
  constructor(parent, definition) {
36601
36755
  super(parent, definition);
36756
+ this.appearances = definition.bodyElement.appearances;
36602
36757
  const childrenState = createChildrenState(this);
36603
36758
  this.childrenState = childrenState;
36604
36759
  const state = createSharedNodeState(
36605
36760
  this.scope,
36606
36761
  {
36607
- ...this.buildSharedStateSpec(parent, definition),
36762
+ reference: this.contextReference,
36763
+ readonly: this.isReadonly,
36764
+ relevant: this.isRelevant,
36765
+ required: this.isRequired,
36608
36766
  label: createNodeLabel(this, definition),
36609
36767
  hint: null,
36610
36768
  children: childrenState.childIds,
@@ -36617,12 +36775,13 @@ class Group extends DescendantNode {
36617
36775
  );
36618
36776
  this.state = state;
36619
36777
  this.engineState = state.engineState;
36620
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
36778
+ this.currentState = materializeCurrentStateChildren(
36779
+ this.scope,
36780
+ state.currentState,
36781
+ childrenState
36782
+ );
36621
36783
  childrenState.setChildren(buildChildren(this));
36622
36784
  }
36623
- computeReference(parent) {
36624
- return this.computeChildStepReference(parent);
36625
- }
36626
36785
  getChildren() {
36627
36786
  return this.childrenState.getChildren();
36628
36787
  }
@@ -36647,20 +36806,30 @@ const insertAtIndex = (currentValues, insertionIndex, newValue) => {
36647
36806
 
36648
36807
  class RepeatInstance extends DescendantNode {
36649
36808
  constructor(parent, definition, options) {
36650
- super(parent, definition);
36651
- this.parent = parent;
36652
- const childrenState = createChildrenState(this);
36653
- this.childrenState = childrenState;
36654
- options.precedingPrimaryInstanceNode.after(this.contextNode);
36655
36809
  const { precedingInstance } = options;
36656
36810
  const precedingIndex = precedingInstance?.currentIndex ?? (() => -1);
36657
36811
  const initialIndex = precedingIndex() + 1;
36658
36812
  const [currentIndex, setCurrentIndex] = createSignal(initialIndex);
36813
+ super(parent, definition, {
36814
+ computeReference: () => {
36815
+ const currentPosition = currentIndex() + 1;
36816
+ return `${parent.contextReference()}[${currentPosition}]`;
36817
+ }
36818
+ });
36819
+ this.parent = parent;
36820
+ this.appearances = definition.bodyElement.appearances;
36821
+ const childrenState = createChildrenState(this);
36822
+ this.childrenState = childrenState;
36823
+ options.precedingPrimaryInstanceNode.after(this.contextNode);
36659
36824
  this.currentIndex = currentIndex;
36660
36825
  const state = createSharedNodeState(
36661
36826
  this.scope,
36662
36827
  {
36663
- ...this.buildSharedStateSpec(parent, definition),
36828
+ reference: this.contextReference,
36829
+ readonly: this.isReadonly,
36830
+ relevant: this.isRelevant,
36831
+ required: this.isRequired,
36832
+ // TODO: only-child <group><label>
36664
36833
  label: createNodeLabel(this, definition),
36665
36834
  hint: null,
36666
36835
  children: childrenState.childIds,
@@ -36673,7 +36842,11 @@ class RepeatInstance extends DescendantNode {
36673
36842
  );
36674
36843
  this.state = state;
36675
36844
  this.engineState = state.engineState;
36676
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
36845
+ this.currentState = materializeCurrentStateChildren(
36846
+ this.scope,
36847
+ state.currentState,
36848
+ childrenState
36849
+ );
36677
36850
  this.scope.runTask(() => {
36678
36851
  const computeCurrentIndex = parent.getInstanceIndex.bind(parent, this);
36679
36852
  createComputed(on(computeCurrentIndex, setCurrentIndex, { defer: true }));
@@ -36685,13 +36858,29 @@ class RepeatInstance extends DescendantNode {
36685
36858
  // InstanceNode
36686
36859
  state;
36687
36860
  engineState;
36861
+ /**
36862
+ * A repeat instance can inherit non-relevance, just like any other node. That
36863
+ * inheritance is derived from the repeat instance's parent node in the
36864
+ * primary instance XML/DOM tree (and would be semantically expected to do so
36865
+ * even if we move away from that implementation detail).
36866
+ *
36867
+ * Since {@link RepeatInstance.parent} is a {@link RepeatRange}, which is a
36868
+ * runtime data model fiction that does not exist in that hierarchy, we pass
36869
+ * this call through, allowing the {@link RepeatRange} to check the actual
36870
+ * primary instance parent node's relevance state.
36871
+ *
36872
+ * @todo Should we apply similar reasoning in {@link hasReadonlyAncestor}?
36873
+ */
36874
+ hasNonRelevantAncestor = () => {
36875
+ return this.parent.hasNonRelevantAncestor();
36876
+ };
36688
36877
  // RepeatInstanceNode
36689
36878
  nodeType = "repeat-instance";
36879
+ /**
36880
+ * @see {@link RepeatRange.appearances}
36881
+ */
36882
+ appearances;
36690
36883
  currentState;
36691
- computeReference(parent) {
36692
- const currentPosition = this.currentIndex() + 1;
36693
- return `${parent.contextReference}[${currentPosition}]`;
36694
- }
36695
36884
  initializeContextNode(parentContextNode, nodeName) {
36696
36885
  return this.createContextNode(parentContextNode, nodeName);
36697
36886
  }
@@ -36755,17 +36944,118 @@ class RepeatRange extends DescendantNode {
36755
36944
  // InstanceNode
36756
36945
  state;
36757
36946
  engineState;
36947
+ emptyRangeEvaluationContext;
36948
+ /**
36949
+ * @see {@link isSelfRelevant}
36950
+ */
36951
+ isEmptyRangeSelfRelevant;
36952
+ /**
36953
+ * A repeat range does not exist in the primary instance tree. A `relevant`
36954
+ * expression applies to each {@link RepeatInstance} child of the repeat
36955
+ * range. Determining whether a repeat range itself "is relevant" isn't a
36956
+ * concept the spec addresses, but it may be used by clients to determine
36957
+ * whether to allow interaction with the range (e.g. by adding a repeat
36958
+ * instance, or presenting the range's label when empty).
36959
+ *
36960
+ * As a naive first pass, it seems like the heuristic for this should be:
36961
+ *
36962
+ * 1. Does the repeat range have any repeat instance children?
36963
+ *
36964
+ * - If yes, go to 2.
36965
+ * - If no, go to 3.
36966
+ *
36967
+ * 2. Does one or more of those children return `true` for the node's
36968
+ * `relevant` expression (i.e. is the repeat instance "self relevant")?
36969
+ *
36970
+ * 3. Does the relevant expression return `true` for the repeat range itself
36971
+ * (where, at least for now, the context of that evaluation would be the
36972
+ * repeat range's {@link anchorNode} to ensure correct relative expressions
36973
+ * resolve correctly)?
36974
+ *
36975
+ * @todo While (3) is proactively implemented, there isn't presently a test
36976
+ * exercising it. It felt best for now to surface this for discussion in
36977
+ * review to validate that it's going in the right direction.
36978
+ *
36979
+ * @todo While (2) **is actually tested**, the tests currently in place behave
36980
+ * the same way with only the logic for (3), regardless of whether the repeat
36981
+ * range actually has any repeat instance children. It's unclear (a) if that's
36982
+ * a preferable simplification and (b) how that might affect performance (in
36983
+ * theory it could vary depending on form structure and runtime state).
36984
+ */
36985
+ isSelfRelevant = () => {
36986
+ const instances = this.childrenState.getChildren();
36987
+ if (instances.length > 0) {
36988
+ return instances.some((instance) => instance.isSelfRelevant());
36989
+ }
36990
+ return this.isEmptyRangeSelfRelevant();
36991
+ };
36758
36992
  // RepeatRangeNode
36759
36993
  nodeType = "repeat-range";
36994
+ /**
36995
+ * @todo RepeatRange*, RepeatInstance* (and RepeatTemplate*) all share the
36996
+ * same body element, and thus all share the same definition `bodyElement`. As
36997
+ * such, they also all share the same `appearances`. At time of writing,
36998
+ * `web-forms` (Vue UI package) treats a `RepeatRangeNode`...
36999
+ *
37000
+ * - ... as a group, if the node has a label (i.e.
37001
+ * `<group><label/><repeat/></group>`)
37002
+ * - ... effectively as a fragment containing only its instances, otherwise
37003
+ *
37004
+ * We now collapse `<group><repeat>` into `<repeat>`, and no longer treat
37005
+ * "repeat group" as a concept (after parsing). According to the spec, these
37006
+ * appearances **are supposed to** come from that "repeat group" in the form
37007
+ * definition. In practice, many forms do define appearances directly on a
37008
+ * repeat element. The engine currently produces an error if both are defined
37009
+ * simultaneously, but otherwise makes no distinction between appearances in
37010
+ * these form definition shapes:
37011
+ *
37012
+ * ```xml
37013
+ * <group ref="/data/rep1" appearance="...">
37014
+ * <repeat nodeset="/data/rep1"/>
37015
+ * </group>
37016
+ *
37017
+ * <group ref="/data/rep1">
37018
+ * <repeat nodeset="/data/rep1"/ appearance="...">
37019
+ * </group>
37020
+ *
37021
+ * <repeat nodeset="/data/rep1"/ appearance="...">
37022
+ * ```
37023
+ *
37024
+ * All of the above creates considerable ambiguity about where "repeat
37025
+ * appearances" should apply, under which circumstances.
37026
+ */
37027
+ appearances;
36760
37028
  currentState;
36761
37029
  constructor(parent, definition) {
36762
37030
  super(parent, definition);
37031
+ this.appearances = definition.bodyElement.appearances;
36763
37032
  const childrenState = createChildrenState(this);
36764
37033
  this.childrenState = childrenState;
37034
+ this.anchorNode = this.contextNode.ownerDocument.createComment(
37035
+ `Begin repeat range: ${definition.nodeset}`
37036
+ );
37037
+ this.contextNode.append(this.anchorNode);
37038
+ this.emptyRangeEvaluationContext = {
37039
+ scope: this.scope,
37040
+ evaluator: this.evaluator,
37041
+ root: this.root,
37042
+ contextReference: this.contextReference,
37043
+ contextNode: this.anchorNode,
37044
+ getSubscribableDependenciesByReference: (reference) => {
37045
+ return this.getSubscribableDependenciesByReference(reference);
37046
+ }
37047
+ };
37048
+ this.isEmptyRangeSelfRelevant = createComputedExpression(
37049
+ this.emptyRangeEvaluationContext,
37050
+ definition.bind.relevant
37051
+ );
36765
37052
  const state = createSharedNodeState(
36766
37053
  this.scope,
36767
37054
  {
36768
- ...this.buildSharedStateSpec(parent, definition),
37055
+ reference: this.contextReference,
37056
+ readonly: this.isReadonly,
37057
+ relevant: this.isRelevant,
37058
+ required: this.isRequired,
36769
37059
  label: createNodeLabel(this, definition),
36770
37060
  hint: null,
36771
37061
  children: childrenState.childIds,
@@ -36776,13 +37066,13 @@ class RepeatRange extends DescendantNode {
36776
37066
  clientStateFactory: this.engineConfig.stateFactory
36777
37067
  }
36778
37068
  );
36779
- this.anchorNode = this.contextNode.ownerDocument.createComment(
36780
- `Begin repeat range: ${definition.nodeset}`
36781
- );
36782
- this.contextNode.append(this.anchorNode);
36783
37069
  this.state = state;
36784
37070
  this.engineState = state.engineState;
36785
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
37071
+ this.currentState = materializeCurrentStateChildren(
37072
+ this.scope,
37073
+ state.currentState,
37074
+ childrenState
37075
+ );
36786
37076
  definition.instances.forEach((instanceDefinition, index) => {
36787
37077
  const afterIndex = index - 1;
36788
37078
  this.addInstances(afterIndex, 1, instanceDefinition);
@@ -36794,9 +37084,6 @@ class RepeatRange extends DescendantNode {
36794
37084
  initializeContextNode(parentContextNode) {
36795
37085
  return parentContextNode;
36796
37086
  }
36797
- computeReference(parent) {
36798
- return this.computeChildStepReference(parent);
36799
- }
36800
37087
  getInstanceIndex(instance) {
36801
37088
  return this.engineState.children.indexOf(instance.nodeId);
36802
37089
  }
@@ -36868,25 +37155,6 @@ class RepeatRange extends DescendantNode {
36868
37155
  }
36869
37156
  }
36870
37157
 
36871
- const XML_XPATH_WHITESPACE_SUBPATTERN = "[\\x20\\x09\\x0D\\x0A]";
36872
- const XML_XPATH_WHITESPACE_PATTERN = new RegExp(XML_XPATH_WHITESPACE_SUBPATTERN, "g");
36873
- const XML_XPATH_LEADING_TRAILING_WHITESPACE_PATTERN = new RegExp(
36874
- `^${XML_XPATH_WHITESPACE_SUBPATTERN}+|${XML_XPATH_WHITESPACE_SUBPATTERN}+$`,
36875
- "g"
36876
- );
36877
- const XPATH_REPEATING_WHITESPACE_PATTERN = new RegExp(
36878
- `${XML_XPATH_WHITESPACE_SUBPATTERN}{2,}`,
36879
- "g"
36880
- );
36881
- const trimXMLXPathWhitespace = (value) => value.replaceAll(XML_XPATH_LEADING_TRAILING_WHITESPACE_PATTERN, "");
36882
- const normalizeXMLXPathWhitespace = (value) => trimXMLXPathWhitespace(value).replaceAll(XPATH_REPEATING_WHITESPACE_PATTERN, " ");
36883
- const xmlXPathWhitespaceSeparatedList = (value, options) => {
36884
- if (value === "") {
36885
- return [];
36886
- }
36887
- return normalizeXMLXPathWhitespace(value).split(XML_XPATH_WHITESPACE_PATTERN);
36888
- };
36889
-
36890
37158
  const createSelectItemLabel = (context, definition) => {
36891
37159
  const { label, value } = definition;
36892
37160
  return createTextRange(context, "label", label, {
@@ -36915,15 +37183,14 @@ class ItemsetItemEvaluationContext {
36915
37183
  this.scope = selectField.scope;
36916
37184
  this.evaluator = selectField.evaluator;
36917
37185
  this.root = selectField.root;
37186
+ this.contextReference = selectField.contextReference;
36918
37187
  }
36919
37188
  scope;
36920
37189
  evaluator;
36921
37190
  root;
36922
- get contextReference() {
36923
- return this.selectField.contextReference;
36924
- }
36925
- getSubscribableDependencyByReference(reference) {
36926
- return this.selectField.getSubscribableDependencyByReference(reference);
37191
+ contextReference;
37192
+ getSubscribableDependenciesByReference(reference) {
37193
+ return this.selectField.getSubscribableDependenciesByReference(reference);
36927
37194
  }
36928
37195
  }
36929
37196
  const createSelectItemsetItemLabel = (context, definition, itemValue) => {
@@ -36987,7 +37254,7 @@ const createPrimaryInstanceValueState = (context, options) => {
36987
37254
  const initialValue = initialValueSource === "PRIMARY_INSTANCE" ? contextNode.textContent ?? defaultValue : defaultValue;
36988
37255
  const persistedValueState = createSignal(
36989
37256
  {
36990
- isRelevant: context.isRelevant,
37257
+ isRelevant: context.isRelevant(),
36991
37258
  value: initialValue
36992
37259
  },
36993
37260
  {
@@ -37004,7 +37271,7 @@ const createPrimaryInstanceValueState = (context, options) => {
37004
37271
  );
37005
37272
  const [persistedValue, setValueForPersistence] = persistedValueState;
37006
37273
  createComputed(() => {
37007
- const isRelevant = context.isRelevant;
37274
+ const isRelevant = context.isRelevant();
37008
37275
  setValueForPersistence((persisted) => {
37009
37276
  return {
37010
37277
  isRelevant,
@@ -37027,7 +37294,7 @@ const createPrimaryInstanceValueState = (context, options) => {
37027
37294
  });
37028
37295
  const setPrimaryInstanceValue = (value) => {
37029
37296
  const persisted = setValueForPersistence({
37030
- isRelevant: context.isRelevant,
37297
+ isRelevant: context.isRelevant(),
37031
37298
  value
37032
37299
  });
37033
37300
  return persisted.value;
@@ -37056,8 +37323,8 @@ const guardDownstreamReadonlyWrites = (context, baseState) => {
37056
37323
  }
37057
37324
  const [getValue, baseSetValue] = baseState;
37058
37325
  const setValue = (value) => {
37059
- if (context.isReadonly) {
37060
- const reference = untrack(() => context.contextReference);
37326
+ if (context.isReadonly()) {
37327
+ const reference = untrack(() => context.contextReference());
37061
37328
  throw new Error(`Cannot write to readonly field: ${reference}`);
37062
37329
  }
37063
37330
  return baseSetValue(value);
@@ -37068,7 +37335,7 @@ const createCalculation = (context, setValue, calculateDefinition) => {
37068
37335
  context.scope.runTask(() => {
37069
37336
  const calculate = createComputedExpression(context, calculateDefinition);
37070
37337
  createComputed(() => {
37071
- if (context.isRelevant) {
37338
+ if (context.isRelevant()) {
37072
37339
  const calculated = calculate();
37073
37340
  const value = context.decodeValue(calculated);
37074
37341
  setValue(value);
@@ -37101,6 +37368,7 @@ class SelectField extends DescendantNode {
37101
37368
  engineState;
37102
37369
  // SelectNode
37103
37370
  nodeType = "select";
37371
+ appearances;
37104
37372
  currentState;
37105
37373
  // ValueContext
37106
37374
  encodeValue = (runtimeValue) => {
@@ -37109,7 +37377,9 @@ class SelectField extends DescendantNode {
37109
37377
  };
37110
37378
  decodeValue = (instanceValue) => {
37111
37379
  return this.scope.runTask(() => {
37112
- const values = xmlXPathWhitespaceSeparatedList(instanceValue);
37380
+ const values = xmlXPathWhitespaceSeparatedList(instanceValue, {
37381
+ ignoreEmpty: true
37382
+ });
37113
37383
  const items = this.getSelectItemsByValue();
37114
37384
  return values.map((value) => {
37115
37385
  return items.get(value);
@@ -37121,13 +37391,17 @@ class SelectField extends DescendantNode {
37121
37391
  getValueOptions;
37122
37392
  constructor(parent, definition) {
37123
37393
  super(parent, definition);
37394
+ this.appearances = definition.bodyElement.appearances;
37124
37395
  this.selectExclusive = definition.bodyElement.type === "select1";
37125
37396
  const valueOptions = createSelectItems(this);
37126
37397
  this.getValueOptions = valueOptions;
37127
37398
  const state = createSharedNodeState(
37128
37399
  this.scope,
37129
37400
  {
37130
- ...this.buildSharedStateSpec(parent, definition),
37401
+ reference: this.contextReference,
37402
+ readonly: this.isReadonly,
37403
+ relevant: this.isRelevant,
37404
+ required: this.isRequired,
37131
37405
  label: createNodeLabel(this, definition),
37132
37406
  hint: createFieldHint(this, definition),
37133
37407
  children: null,
@@ -37149,9 +37423,6 @@ class SelectField extends DescendantNode {
37149
37423
  })
37150
37424
  );
37151
37425
  }
37152
- computeReference(parent) {
37153
- return this.computeChildStepReference(parent);
37154
- }
37155
37426
  updateSelectedItemValues(values) {
37156
37427
  const itemsByValue = untrack(() => this.getSelectItemsByValue());
37157
37428
  const items = values.flatMap((value) => {
@@ -37214,16 +37485,21 @@ class StringField extends DescendantNode {
37214
37485
  engineState;
37215
37486
  // StringNode
37216
37487
  nodeType = "string";
37488
+ appearances;
37217
37489
  currentState;
37218
37490
  // ValueContext
37219
37491
  encodeValue = identity$1;
37220
37492
  decodeValue = identity$1;
37221
37493
  constructor(parent, definition) {
37222
37494
  super(parent, definition);
37495
+ this.appearances = definition.bodyElement?.appearances ?? null;
37223
37496
  const state = createSharedNodeState(
37224
37497
  this.scope,
37225
37498
  {
37226
- ...this.buildSharedStateSpec(parent, definition),
37499
+ reference: this.contextReference,
37500
+ readonly: this.isReadonly,
37501
+ relevant: this.isRelevant,
37502
+ required: this.isRequired,
37227
37503
  label: createNodeLabel(this, definition),
37228
37504
  hint: createFieldHint(this, definition),
37229
37505
  children: null,
@@ -37238,9 +37514,6 @@ class StringField extends DescendantNode {
37238
37514
  this.engineState = state.engineState;
37239
37515
  this.currentState = state.currentState;
37240
37516
  }
37241
- computeReference(parent) {
37242
- return this.computeChildStepReference(parent);
37243
- }
37244
37517
  // InstanceNode
37245
37518
  getChildren() {
37246
37519
  return [];
@@ -37259,6 +37532,7 @@ class Subtree extends DescendantNode {
37259
37532
  engineState;
37260
37533
  // SubtreeNode
37261
37534
  nodeType = "subtree";
37535
+ appearances = null;
37262
37536
  currentState;
37263
37537
  constructor(parent, definition) {
37264
37538
  super(parent, definition);
@@ -37267,7 +37541,10 @@ class Subtree extends DescendantNode {
37267
37541
  const state = createSharedNodeState(
37268
37542
  this.scope,
37269
37543
  {
37270
- ...this.buildSharedStateSpec(parent, definition),
37544
+ reference: this.contextReference,
37545
+ readonly: this.isReadonly,
37546
+ relevant: this.isRelevant,
37547
+ required: this.isRequired,
37271
37548
  label: null,
37272
37549
  hint: null,
37273
37550
  children: childrenState.childIds,
@@ -37280,12 +37557,13 @@ class Subtree extends DescendantNode {
37280
37557
  );
37281
37558
  this.state = state;
37282
37559
  this.engineState = state.engineState;
37283
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
37560
+ this.currentState = materializeCurrentStateChildren(
37561
+ this.scope,
37562
+ state.currentState,
37563
+ childrenState
37564
+ );
37284
37565
  childrenState.setChildren(buildChildren(this));
37285
37566
  }
37286
- computeReference(parent) {
37287
- return this.computeChildStepReference(parent);
37288
- }
37289
37567
  getChildren() {
37290
37568
  return this.childrenState.getChildren();
37291
37569
  }
@@ -37304,14 +37582,20 @@ const buildChildren = (parent) => {
37304
37582
  }
37305
37583
  return new Group(parent, child);
37306
37584
  }
37307
- case "repeat-sequence": {
37585
+ case "repeat-range": {
37308
37586
  return new RepeatRange(parent, child);
37309
37587
  }
37310
37588
  case "value-node": {
37311
- if (child.bodyElement instanceof SelectDefinition) {
37312
- return new SelectField(parent, child);
37589
+ switch (child.bodyElement?.type) {
37590
+ case "select":
37591
+ case "select1":
37592
+ return new SelectField(parent, child);
37593
+ case "input":
37594
+ case void 0:
37595
+ return new StringField(parent, child);
37596
+ default:
37597
+ throw new UnreachableError(child.bodyElement);
37313
37598
  }
37314
- return new StringField(parent, child);
37315
37599
  }
37316
37600
  default: {
37317
37601
  throw new UnreachableError(child);
@@ -37350,27 +37634,24 @@ const getInitialLanguageState = (translations) => {
37350
37634
  };
37351
37635
  };
37352
37636
  class Root extends InstanceNode {
37353
- static async initialize(xformDOM, definition, engineConfig) {
37354
- const instance = new Root(xformDOM, definition, engineConfig);
37355
- await instance.formStateInitialized();
37356
- return instance;
37357
- }
37358
37637
  childrenState;
37359
37638
  // InstanceNode
37639
+ hasReadonlyAncestor = () => false;
37640
+ isReadonly = () => false;
37641
+ hasNonRelevantAncestor = () => false;
37642
+ isRelevant = () => true;
37360
37643
  state;
37361
37644
  engineState;
37362
37645
  // RootNode
37363
37646
  nodeType = "root";
37647
+ appearances = null;
37648
+ classes;
37364
37649
  currentState;
37365
37650
  instanceDOM;
37366
37651
  // BaseNode
37367
37652
  root = this;
37368
37653
  // EvaluationContext
37369
37654
  evaluator;
37370
- rootReference;
37371
- get contextReference() {
37372
- return this.rootReference;
37373
- }
37374
37655
  contextNode;
37375
37656
  // RootNode
37376
37657
  parent = null;
@@ -37380,11 +37661,13 @@ class Root extends InstanceNode {
37380
37661
  return this.engineState.activeLanguage;
37381
37662
  }
37382
37663
  constructor(xformDOM, definition, engineConfig) {
37383
- super(engineConfig, null, definition);
37664
+ const reference = definition.nodeset;
37665
+ super(engineConfig, null, definition, {
37666
+ computeReference: () => reference
37667
+ });
37668
+ this.classes = definition.classes;
37384
37669
  const childrenState = createChildrenState(this);
37385
37670
  this.childrenState = childrenState;
37386
- const reference = definition.nodeset;
37387
- this.rootReference = reference;
37388
37671
  const instanceDOM = xformDOM.createInstance();
37389
37672
  const evaluator = instanceDOM.primaryInstanceEvaluator;
37390
37673
  const { translations } = evaluator;
@@ -37409,7 +37692,11 @@ class Root extends InstanceNode {
37409
37692
  );
37410
37693
  this.state = state;
37411
37694
  this.engineState = state.engineState;
37412
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
37695
+ this.currentState = materializeCurrentStateChildren(
37696
+ this.scope,
37697
+ state.currentState,
37698
+ childrenState
37699
+ );
37413
37700
  const contextNode = instanceDOM.xformDocument.createElement(definition.nodeName);
37414
37701
  instanceDOM.primaryInstanceRoot.replaceWith(contextNode);
37415
37702
  this.evaluator = evaluator;
@@ -37418,37 +37705,6 @@ class Root extends InstanceNode {
37418
37705
  this.languages = languages;
37419
37706
  childrenState.setChildren(buildChildren(this));
37420
37707
  }
37421
- /**
37422
- * Waits until form state is fully initialized.
37423
- *
37424
- * As much as possible, all instance state computations are implemented so
37425
- * that they complete synchronously.
37426
- *
37427
- * There is currently one exception: because instance nodes may form
37428
- * computation dependencies into their descendants as well as their ancestors,
37429
- * there is an allowance **during form initialization only** to account for
37430
- * this chicken/egg scenario. Note that this allowance is intentionally,
37431
- * strictly limited: if form state initialization is not resolved within a
37432
- * single microtask tick we throw/reject.
37433
- *
37434
- * All subsequent computations are always performed synchronously (and we will
37435
- * use tests to validate this, by utilizing the synchronously returned `Root`
37436
- * state from client-facing write interfaces).
37437
- */
37438
- async formStateInitialized() {
37439
- await new Promise((resolve) => {
37440
- queueMicrotask(resolve);
37441
- });
37442
- if (!this.isStateInitialized()) {
37443
- throw new Error(
37444
- "Form state initialization failed to complete in a single frame. Has some aspect of reactive computation been made asynchronous by mistake?"
37445
- );
37446
- }
37447
- }
37448
- // InstanceNode
37449
- computeReference(_parent, definition) {
37450
- return definition.nodeset;
37451
- }
37452
37708
  getChildren() {
37453
37709
  return this.childrenState.getChildren();
37454
37710
  }
@@ -37482,7 +37738,7 @@ const initializeForm$1 = async (input, options = {}) => {
37482
37738
  const engineConfig = buildInstanceConfig(options.config);
37483
37739
  const sourceXML = await retrieveSourceXMLResource(input, engineConfig);
37484
37740
  const form = new XFormDefinition(sourceXML);
37485
- return Root.initialize(form.xformDOM, form.model.root, engineConfig);
37741
+ return new Root(form.xformDOM, form.model.root, engineConfig);
37486
37742
  };
37487
37743
 
37488
37744
  const initializeForm = initializeForm$1;