@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/solid.js CHANGED
@@ -1,7 +1,8 @@
1
- import { createUniqueId as createUniqueId$1, createSignal, createMemo, createComputed, untrack, createRoot, getOwner, runWithOwner, on } from 'solid-js';
1
+ import { createUniqueId as createUniqueId$1, createSignal, createComputed, untrack, createRoot, getOwner, runWithOwner, createMemo, on } from 'solid-js';
2
2
 
3
3
  const identity$1 = (value) => value;
4
4
 
5
+ const XMLNS_NAMESPACE_URI$1 = "http://www.w3.org/2000/xmlns/";
5
6
  const JAVAROSA_NAMESPACE_URI$1 = "http://openrosa.org/javarosa";
6
7
  const XFORMS_NAMESPACE_URI$1 = "http://www.w3.org/2002/xforms";
7
8
 
@@ -34053,7 +34054,50 @@ const normalizeBodyRefNodesetAttributes = (body) => {
34053
34054
  }
34054
34055
  }
34055
34056
  };
34056
- const normalizeRepeatGroups = (xformDocument, body) => {
34057
+ const normalizeRepeatGroupAttributes = (group, repeat) => {
34058
+ for (const groupAttribute of group.attributes) {
34059
+ const { localName, namespaceURI, nodeName, value } = groupAttribute;
34060
+ if (
34061
+ // Don't propagate namespace declarations (which appear as attributes in
34062
+ // the browser/XML DOM, either named `xmlns` or with an `xmlns` prefix,
34063
+ // always in the XMLNS namespace).
34064
+ namespaceURI === XMLNS_NAMESPACE_URI$1 || // Don't propagate `ref`, it has been normalized as `nodeset` on the
34065
+ // repeat element.
34066
+ localName === "ref" || // TODO: this accommodates tests of this normalization process, where
34067
+ // certain nodes of interest are given an `id` attribute, and looked up
34068
+ // for the purpose of asserting what was normalized about them. It's
34069
+ // unclear if there's a generally expected behavior around the attribute.
34070
+ localName === "id"
34071
+ ) {
34072
+ continue;
34073
+ }
34074
+ if (localName === "appearance" && namespaceURI === XFORMS_NAMESPACE_URI$1 && repeat.hasAttribute(localName)) {
34075
+ const ref = group.getAttribute("ref");
34076
+ throw new Error(
34077
+ `Failed to normalize conflicting "appearances" attribute of group/repeat "${ref}"`
34078
+ );
34079
+ }
34080
+ repeat.setAttributeNS(namespaceURI, nodeName, value);
34081
+ }
34082
+ };
34083
+ const normalizeRepeatGroupLabel = (group, repeat) => {
34084
+ const groupLabel = Array.from(group.children).find((child) => {
34085
+ return child.localName === "label";
34086
+ });
34087
+ if (groupLabel == null) {
34088
+ return;
34089
+ }
34090
+ const repeatLabel = groupLabel.cloneNode(true);
34091
+ repeatLabel.setAttribute("form-definition-source", "repeat-group");
34092
+ repeat.prepend(repeatLabel);
34093
+ groupLabel.remove();
34094
+ };
34095
+ const unwrapRepeatGroup = (group, repeat) => {
34096
+ normalizeRepeatGroupAttributes(group, repeat);
34097
+ normalizeRepeatGroupLabel(group, repeat);
34098
+ group.replaceWith(repeat);
34099
+ };
34100
+ const normalizeRepeatGroups = (body) => {
34057
34101
  const repeats = body.querySelectorAll("repeat");
34058
34102
  for (const repeat of repeats) {
34059
34103
  const parent = repeat.parentElement;
@@ -34068,11 +34112,8 @@ const normalizeRepeatGroups = (xformDocument, body) => {
34068
34112
  group = parent;
34069
34113
  }
34070
34114
  }
34071
- if (group == null) {
34072
- group = xformDocument.createElementNS(XFORMS_NAMESPACE_URI$1, "group");
34073
- group.setAttribute("ref", repeatNodeset);
34074
- repeat.before(group);
34075
- group.append(repeat);
34115
+ if (group != null) {
34116
+ unwrapRepeatGroup(group, repeat);
34076
34117
  }
34077
34118
  }
34078
34119
  };
@@ -34090,7 +34131,7 @@ const parseNormalizedXForm = (sourceXML, options) => {
34090
34131
  normalizedXML = sourceXML;
34091
34132
  } else {
34092
34133
  normalizeBodyRefNodesetAttributes(body);
34093
- normalizeRepeatGroups(xformDocument, body);
34134
+ normalizeRepeatGroups(body);
34094
34135
  normalizedXML = html.outerHTML;
34095
34136
  }
34096
34137
  return {
@@ -34222,6 +34263,70 @@ class DependencyContext {
34222
34263
  }
34223
34264
  }
34224
34265
 
34266
+ const XML_XPATH_WHITESPACE_SUBPATTERN = "[\\x20\\x09\\x0D\\x0A]";
34267
+ const XML_XPATH_WHITESPACE_PATTERN = new RegExp(XML_XPATH_WHITESPACE_SUBPATTERN, "g");
34268
+ const XML_XPATH_LEADING_TRAILING_WHITESPACE_PATTERN = new RegExp(
34269
+ `^${XML_XPATH_WHITESPACE_SUBPATTERN}+|${XML_XPATH_WHITESPACE_SUBPATTERN}+$`,
34270
+ "g"
34271
+ );
34272
+ const XPATH_REPEATING_WHITESPACE_PATTERN = new RegExp(
34273
+ `${XML_XPATH_WHITESPACE_SUBPATTERN}{2,}`,
34274
+ "g"
34275
+ );
34276
+ const trimXMLXPathWhitespace = (value) => value.replaceAll(XML_XPATH_LEADING_TRAILING_WHITESPACE_PATTERN, "");
34277
+ const normalizeXMLXPathWhitespace = (value) => trimXMLXPathWhitespace(value).replaceAll(XPATH_REPEATING_WHITESPACE_PATTERN, " ");
34278
+ const xmlXPathWhitespaceSeparatedList = (value, options) => {
34279
+ if (options?.ignoreEmpty && value === "") {
34280
+ return [];
34281
+ }
34282
+ return normalizeXMLXPathWhitespace(value).split(XML_XPATH_WHITESPACE_PATTERN);
34283
+ };
34284
+
34285
+ class TokenListParser {
34286
+ constructor(canonicalTokens, options) {
34287
+ this.canonicalTokens = canonicalTokens;
34288
+ this.aliases = new Map(
34289
+ (options?.aliases ?? []).map(({ fromAlias, toCanonical }) => {
34290
+ return [fromAlias, toCanonical];
34291
+ })
34292
+ );
34293
+ }
34294
+ aliases;
34295
+ parseFrom(element, attributeName) {
34296
+ const serialized = element.getAttribute(attributeName) ?? "";
34297
+ const specified = xmlXPathWhitespaceSeparatedList(serialized, {
34298
+ ignoreEmpty: true
34299
+ });
34300
+ const { aliases } = this;
34301
+ const resolved = specified.flatMap((token) => {
34302
+ const alias = aliases.get(token);
34303
+ if (alias == null) {
34304
+ return token;
34305
+ }
34306
+ return [alias, token];
34307
+ });
34308
+ const tokens = new Set(resolved);
34309
+ return new Proxy(
34310
+ {
34311
+ [Symbol.iterator]() {
34312
+ return resolved.values();
34313
+ }
34314
+ },
34315
+ {
34316
+ get(target, property, receiver) {
34317
+ if (typeof property === "symbol") {
34318
+ return Reflect.get(target, property, receiver);
34319
+ }
34320
+ return tokens.has(property);
34321
+ },
34322
+ set() {
34323
+ return false;
34324
+ }
34325
+ }
34326
+ );
34327
+ }
34328
+ }
34329
+
34225
34330
  class BodyElementDefinition extends DependencyContext {
34226
34331
  constructor(form, parent, element) {
34227
34332
  super();
@@ -34246,17 +34351,7 @@ class BodyElementDefinition extends DependencyContext {
34246
34351
  }
34247
34352
  }
34248
34353
 
34249
- class UnsupportedBodyElementDefinition extends BodyElementDefinition {
34250
- static isCompatible() {
34251
- return true;
34252
- }
34253
- category = "UNSUPPORTED";
34254
- type = "UNSUPPORTED";
34255
- reference = null;
34256
- constructor(form, parent, element) {
34257
- super(form, parent, element);
34258
- }
34259
- }
34354
+ const structureElementAppearanceParser = new TokenListParser(["field-list", "table-list"]);
34260
34355
 
34261
34356
  const SUPPORTS_SCOPE_CHILD_SELECTOR = (() => {
34262
34357
  const parent = document.createElement("parent");
@@ -34318,7 +34413,10 @@ const hintLookup = new ScopedElementLookup(":scope > hint", "hint");
34318
34413
  const itemLookup = new ScopedElementLookup(":scope > item", "item");
34319
34414
  const itemsetLookup = new ScopedElementLookup(":scope > itemset[nodeset]", "itemset[nodeset]");
34320
34415
  const labelLookup = new ScopedElementLookup(":scope > label", "label");
34321
- const repeatLookup = new ScopedElementLookup(":scope > repeat[nodeset]", "repeat[nodeset]");
34416
+ const repeatGroupLabelLookup = new ScopedElementLookup(
34417
+ ':scope > label[form-definition-source="repeat-group"]',
34418
+ 'label[form-definition-source="repeat-group"]'
34419
+ );
34322
34420
  const valueLookup = new ScopedElementLookup(":scope > value", "value");
34323
34421
  const getHintElement = (parent) => {
34324
34422
  return hintLookup.getElement(parent);
@@ -34332,8 +34430,8 @@ const getItemsetElement = (parent) => {
34332
34430
  const getLabelElement = (parent) => {
34333
34431
  return labelLookup.getElement(parent);
34334
34432
  };
34335
- const getRepeatElement = (parent) => {
34336
- return repeatLookup.getElement(parent);
34433
+ const getRepeatGroupLabelElement = (parent) => {
34434
+ return repeatGroupLabelLookup.getElement(parent);
34337
34435
  };
34338
34436
  const getValueElement = (parent) => {
34339
34437
  return valueLookup.getElement(parent);
@@ -34641,17 +34739,6 @@ class TextElementDefinition extends BodyElementDefinition {
34641
34739
  }
34642
34740
  }
34643
34741
 
34644
- class HintDefinition extends TextElementDefinition {
34645
- static forElement(form, definition) {
34646
- const hintElement = getHintElement(definition.element);
34647
- if (hintElement == null) {
34648
- return null;
34649
- }
34650
- return new this(form, definition, hintElement);
34651
- }
34652
- type = "hint";
34653
- }
34654
-
34655
34742
  class LabelDefinition extends TextElementDefinition {
34656
34743
  static staticDefinition(form, definition) {
34657
34744
  const labelElement = getLabelElement(definition.element);
@@ -34663,6 +34750,13 @@ class LabelDefinition extends TextElementDefinition {
34663
34750
  static forControl(form, control) {
34664
34751
  return this.staticDefinition(form, control);
34665
34752
  }
34753
+ static forRepeatGroup(form, repeat) {
34754
+ const repeatGroupLabel = getRepeatGroupLabelElement(repeat.element);
34755
+ if (repeatGroupLabel == null) {
34756
+ return null;
34757
+ }
34758
+ return new this(form, repeat, repeatGroupLabel);
34759
+ }
34666
34760
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
34667
34761
  static forGroup(form, group) {
34668
34762
  return this.staticDefinition(form, group);
@@ -34680,6 +34774,68 @@ class LabelDefinition extends TextElementDefinition {
34680
34774
  type = "label";
34681
34775
  }
34682
34776
 
34777
+ class RepeatElementDefinition extends BodyElementDefinition {
34778
+ static isCompatible(localName) {
34779
+ return localName === "repeat";
34780
+ }
34781
+ category = "structure";
34782
+ type = "repeat";
34783
+ reference;
34784
+ appearances;
34785
+ label;
34786
+ // TODO: this will fall into the growing category of non-`BindExpression`
34787
+ // cases which have roughly the same design story.
34788
+ countExpression;
34789
+ isFixedCount;
34790
+ children;
34791
+ constructor(form, parent, element) {
34792
+ super(form, parent, element);
34793
+ this.label = LabelDefinition.forRepeatGroup(form, this);
34794
+ const reference = element.getAttribute("nodeset");
34795
+ if (reference == null) {
34796
+ throw new Error("Invalid repeat: missing `nodeset` reference");
34797
+ }
34798
+ this.reference = reference;
34799
+ this.appearances = structureElementAppearanceParser.parseFrom(element, "appearance");
34800
+ this.countExpression = element.getAttributeNS(JAVAROSA_NAMESPACE_URI$1, "count");
34801
+ const childElements = Array.from(element.children).filter((childElement) => {
34802
+ const { localName } = childElement;
34803
+ return localName !== "label" && localName !== "group-label";
34804
+ });
34805
+ const children = BodyDefinition.getChildElementDefinitions(form, this, element, childElements);
34806
+ this.children = children;
34807
+ const noAddRemove = element.getAttributeNS(JAVAROSA_NAMESPACE_URI$1, "noAddRemove")?.trim().replaceAll(/\s+/g, "") ?? "false()";
34808
+ this.isFixedCount = noAddRemove === "true()";
34809
+ }
34810
+ toJSON() {
34811
+ const { form, parent, ...rest } = this;
34812
+ return rest;
34813
+ }
34814
+ }
34815
+
34816
+ class UnsupportedBodyElementDefinition extends BodyElementDefinition {
34817
+ static isCompatible() {
34818
+ return true;
34819
+ }
34820
+ category = "UNSUPPORTED";
34821
+ type = "UNSUPPORTED";
34822
+ reference = null;
34823
+ constructor(form, parent, element) {
34824
+ super(form, parent, element);
34825
+ }
34826
+ }
34827
+
34828
+ class HintDefinition extends TextElementDefinition {
34829
+ static forElement(form, definition) {
34830
+ const hintElement = getHintElement(definition.element);
34831
+ if (hintElement == null) {
34832
+ return null;
34833
+ }
34834
+ return new this(form, definition, hintElement);
34835
+ }
34836
+ type = "hint";
34837
+ }
34838
+
34683
34839
  class ControlDefinition extends BodyElementDefinition {
34684
34840
  category = "control";
34685
34841
  reference;
@@ -34697,13 +34853,84 @@ class ControlDefinition extends BodyElementDefinition {
34697
34853
  }
34698
34854
  }
34699
34855
 
34856
+ const inputAppearanceParser = new TokenListParser([
34857
+ "multiline",
34858
+ "numbers",
34859
+ "url",
34860
+ "thousand-sep",
34861
+ // date (TODO: data types)
34862
+ "no-calendar",
34863
+ "month-year",
34864
+ "year",
34865
+ // date > calendars
34866
+ "ethiopian",
34867
+ "coptic",
34868
+ "islamic",
34869
+ "bikram-sambat",
34870
+ "myanmar",
34871
+ "persian",
34872
+ // geo (TODO: data types)
34873
+ "placement-map",
34874
+ "maps",
34875
+ // image/media (TODO: move to eventual `<upload>`?)
34876
+ "hidden-answer",
34877
+ "annotate",
34878
+ "draw",
34879
+ "signature",
34880
+ "new-front",
34881
+ "new",
34882
+ "front",
34883
+ // *?
34884
+ "printer",
34885
+ // Note: actual usage uses `printer:...` (like `ex:...`).
34886
+ "masked"
34887
+ ]);
34888
+
34700
34889
  class InputDefinition extends ControlDefinition {
34701
34890
  static isCompatible(localName) {
34702
34891
  return localName === "input";
34703
34892
  }
34704
34893
  type = "input";
34894
+ appearances;
34895
+ constructor(form, parent, element) {
34896
+ super(form, parent, element);
34897
+ this.appearances = inputAppearanceParser.parseFrom(element, "appearance");
34898
+ }
34705
34899
  }
34706
34900
 
34901
+ const selectAppearanceParser = new TokenListParser(
34902
+ [
34903
+ // From XLSForm Docs:
34904
+ "compact",
34905
+ "horizontal",
34906
+ "horizontal-compact",
34907
+ "label",
34908
+ "list-nolabel",
34909
+ "minimal",
34910
+ // From Collect `Appearances.kt`:
34911
+ "columns",
34912
+ "columns-1",
34913
+ "columns-2",
34914
+ "columns-3",
34915
+ "columns-4",
34916
+ "columns-5",
34917
+ // Note: Collect supports arbitrary columns-n. Technically we do too (we parse
34918
+ // out any appearance, not just those we know about). But we'll only include
34919
+ // types/defaults up to 5.
34920
+ "columns-pack",
34921
+ "autocomplete",
34922
+ // TODO: these are `<select1>` only
34923
+ "likert",
34924
+ "quick",
34925
+ "quickcompact",
34926
+ "map"
34927
+ // "quick map"
34928
+ ],
34929
+ {
34930
+ aliases: [{ fromAlias: "search", toCanonical: "autocomplete" }]
34931
+ }
34932
+ );
34933
+
34707
34934
  class ItemDefinition extends BodyElementDefinition {
34708
34935
  constructor(form, parent, element) {
34709
34936
  const valueElement = getValueElement(element);
@@ -34757,7 +34984,11 @@ class ItemsetDefinition extends BodyElementDefinition {
34757
34984
  }
34758
34985
  }
34759
34986
 
34760
- const selectLocalNames = /* @__PURE__ */ new Set(["rank", "select", "select1"]);
34987
+ const selectLocalNames = /* @__PURE__ */ new Set([
34988
+ /* 'rank', */
34989
+ "select",
34990
+ "select1"
34991
+ ]);
34761
34992
  const isSelectElement = (element, localName = element.localName) => {
34762
34993
  return selectLocalNames.has(localName);
34763
34994
  };
@@ -34770,6 +35001,7 @@ class SelectDefinition extends ControlDefinition {
34770
35001
  }
34771
35002
  type;
34772
35003
  element;
35004
+ appearances;
34773
35005
  itemset;
34774
35006
  items;
34775
35007
  constructor(form, parent, element) {
@@ -34777,8 +35009,9 @@ class SelectDefinition extends ControlDefinition {
34777
35009
  throw new Error(`Invalid select element: <${element.nodeName}>`);
34778
35010
  }
34779
35011
  super(form, parent, element);
34780
- this.element = element;
34781
35012
  this.type = element.localName;
35013
+ this.element = element;
35014
+ this.appearances = selectAppearanceParser.parseFrom(element, "appearance");
34782
35015
  const itemsetElement = getItemsetElement(element);
34783
35016
  const itemElements = getItemElements(element);
34784
35017
  if (itemsetElement == null) {
@@ -34811,22 +35044,16 @@ class UpsertableMap extends Map {
34811
35044
  }
34812
35045
 
34813
35046
  class BaseGroupDefinition extends BodyElementDefinition {
35047
+ // TODO: does this really accomplish anything? It seems highly unlikely it
35048
+ // has enough performance benefit to outweigh its memory and lookup costs.
34814
35049
  static groupTypes = new UpsertableMap();
34815
35050
  static getGroupType(localName, element) {
34816
35051
  return this.groupTypes.upsert(element, () => {
34817
35052
  if (localName !== "group") {
34818
35053
  return null;
34819
35054
  }
34820
- const ref = element.getAttribute("ref");
34821
- if (ref != null) {
34822
- const repeat = getRepeatElement(element);
34823
- if (repeat == null) {
34824
- return "logical-group";
34825
- }
34826
- if (repeat.getAttribute("nodeset") === ref) {
34827
- return "repeat-group";
34828
- }
34829
- throw new Error("Unexpected <repeat> child of unrelated <group>");
35055
+ if (element.hasAttribute("ref")) {
35056
+ return "logical-group";
34830
35057
  }
34831
35058
  const label = getLabelElement(element);
34832
35059
  if (label == null) {
@@ -34838,18 +35065,20 @@ class BaseGroupDefinition extends BodyElementDefinition {
34838
35065
  category = "structure";
34839
35066
  children;
34840
35067
  reference;
35068
+ appearances;
34841
35069
  label;
34842
35070
  constructor(form, parent, element, children) {
34843
35071
  super(form, parent, element);
34844
35072
  this.children = children ?? this.getChildren(element);
34845
35073
  this.reference = element.getAttribute("ref");
35074
+ this.appearances = structureElementAppearanceParser.parseFrom(element, "appearance");
34846
35075
  this.label = LabelDefinition.forGroup(form, this);
34847
35076
  }
34848
35077
  getChildren(element) {
34849
35078
  const { form } = this;
34850
35079
  const children = Array.from(element.children).filter((child) => {
34851
35080
  const childName = child.localName;
34852
- return childName !== "label" && childName !== "repeat";
35081
+ return childName !== "label";
34853
35082
  });
34854
35083
  return BodyDefinition.getChildElementDefinitions(form, this, element, children);
34855
35084
  }
@@ -34878,54 +35107,6 @@ class PresentationGroupDefinition extends BaseGroupDefinition {
34878
35107
  }
34879
35108
  }
34880
35109
 
34881
- class RepeatDefinition extends BodyElementDefinition {
34882
- constructor(form, groupDefinition, element) {
34883
- super(form, groupDefinition, element);
34884
- this.groupDefinition = groupDefinition;
34885
- const reference = element.getAttribute("nodeset");
34886
- if (reference == null) {
34887
- throw new Error("Invalid repeat: missing `nodeset` reference");
34888
- }
34889
- this.reference = reference;
34890
- this.countExpression = element.getAttributeNS(JAVAROSA_NAMESPACE_URI$1, "count");
34891
- this.children = groupDefinition.getChildren(element);
34892
- const noAddRemove = element.getAttributeNS(JAVAROSA_NAMESPACE_URI$1, "noAddRemove")?.trim().replaceAll(/\s+/g, "") ?? "false()";
34893
- this.isFixedCount = noAddRemove === "true()";
34894
- }
34895
- category = "structure";
34896
- type = "repeat";
34897
- reference;
34898
- // TODO: this will fall into the growing category of non-`BindExpression`
34899
- // cases which have roughly the same design story.
34900
- countExpression;
34901
- isFixedCount;
34902
- children;
34903
- toJSON() {
34904
- const { form, groupDefinition, parent, ...rest } = this;
34905
- return rest;
34906
- }
34907
- }
34908
-
34909
- class RepeatGroupDefinition extends BaseGroupDefinition {
34910
- static isCompatible(localName, element) {
34911
- return this.getGroupType(localName, element) === "repeat-group";
34912
- }
34913
- type = "repeat-group";
34914
- repeat;
34915
- get repeatChildren() {
34916
- return this.repeat.children;
34917
- }
34918
- constructor(form, parent, element) {
34919
- const repeat = getRepeatElement(element);
34920
- if (repeat == null) {
34921
- throw new Error("Invalid repeat-group");
34922
- }
34923
- super(form, parent, element);
34924
- const repeatDefinition = new RepeatDefinition(form, this, repeat);
34925
- this.repeat = repeatDefinition;
34926
- }
34927
- }
34928
-
34929
35110
  class StructuralGroupDefinition extends BaseGroupDefinition {
34930
35111
  static isCompatible(localName, element) {
34931
35112
  return this.getGroupType(localName, element) === "structural-group";
@@ -34934,7 +35115,7 @@ class StructuralGroupDefinition extends BaseGroupDefinition {
34934
35115
  }
34935
35116
 
34936
35117
  const BodyElementDefinitionConstructors = [
34937
- RepeatGroupDefinition,
35118
+ RepeatElementDefinition,
34938
35119
  LogicalGroupDefinition,
34939
35120
  PresentationGroupDefinition,
34940
35121
  StructuralGroupDefinition,
@@ -34949,12 +35130,12 @@ class BodyElementMap extends Map {
34949
35130
  mapElementsByReference(elements) {
34950
35131
  for (const element of elements) {
34951
35132
  const { reference } = element;
34952
- if (element instanceof RepeatGroupDefinition) {
35133
+ if (element instanceof RepeatElementDefinition) {
34953
35134
  if (reference == null) {
34954
- throw new Error("Missing reference for repeat/repeat group");
35135
+ throw new Error("Missing reference for repeat");
34955
35136
  }
34956
35137
  this.set(reference, element);
34957
- this.mapElementsByReference(element.repeatChildren);
35138
+ this.mapElementsByReference(element.children);
34958
35139
  }
34959
35140
  if (element instanceof LogicalGroupDefinition || element instanceof PresentationGroupDefinition || element instanceof StructuralGroupDefinition) {
34960
35141
  if (reference != null) {
@@ -34980,6 +35161,10 @@ class BodyElementMap extends Map {
34980
35161
  return Object.fromEntries(this.entries());
34981
35162
  }
34982
35163
  }
35164
+ const bodyClassParser = new TokenListParser([
35165
+ "pages"
35166
+ /*, 'theme-grid' */
35167
+ ]);
34983
35168
  class BodyDefinition extends DependencyContext {
34984
35169
  constructor(form) {
34985
35170
  super();
@@ -34987,6 +35172,7 @@ class BodyDefinition extends DependencyContext {
34987
35172
  const { body: element } = form.xformDOM;
34988
35173
  this.reference = form.rootReference;
34989
35174
  this.element = element;
35175
+ this.classes = bodyClassParser.parseFrom(element, "class");
34990
35176
  this.elements = BodyDefinition.getChildElementDefinitions(form, this, element);
34991
35177
  this.elementsByReference = new BodyElementMap(this.elements);
34992
35178
  }
@@ -35002,6 +35188,23 @@ class BodyDefinition extends DependencyContext {
35002
35188
  });
35003
35189
  }
35004
35190
  element;
35191
+ /**
35192
+ * @todo this class is already an oddity in that it's **like** an element
35193
+ * definition, but it isn't one itself. Adding this property here emphasizes
35194
+ * that awkwardness. It also extends the applicable scope where instances of
35195
+ * this class are accessed. While it's still ephemeral, it's anticipated that
35196
+ * this extension might cause some disomfort. If so, the most plausible
35197
+ * alternative is an additional refactor to:
35198
+ *
35199
+ * 1. Introduce a `BodyElementDefinition` sublass for `<h:body>`.
35200
+ * 2. Disambiguate the respective names of those, in some reasonable way.
35201
+ * 3. Add a layer of indirection between this class and that new body element
35202
+ * definition's class.
35203
+ * 4. At that point, we may as well prioritize the little bit of grunt work to
35204
+ * pass the `BodyDefinition` instance by reference rather than assigning it
35205
+ * to anything.
35206
+ */
35207
+ classes;
35005
35208
  elements;
35006
35209
  elementsByReference;
35007
35210
  // DependencyContext
@@ -35010,13 +35213,6 @@ class BodyDefinition extends DependencyContext {
35010
35213
  getBodyElement(reference) {
35011
35214
  return this.elementsByReference.get(reference) ?? null;
35012
35215
  }
35013
- getRepeatGroup(reference) {
35014
- const element = this.getBodyElement(reference);
35015
- if (element?.type === "repeat-group") {
35016
- return element;
35017
- }
35018
- return null;
35019
- }
35020
35216
  toJSON() {
35021
35217
  const { form, ...rest } = this;
35022
35218
  return rest;
@@ -35249,17 +35445,11 @@ class DescendentNodeDefinition {
35249
35445
  }
35250
35446
 
35251
35447
  class RepeatInstanceDefinition extends DescendentNodeDefinition {
35252
- constructor(sequence, node) {
35253
- const {
35254
- bind,
35255
- bodyElement: repeatGroupBodyElement,
35256
- parent: repeatSequenceParent,
35257
- root
35258
- } = sequence;
35259
- super(repeatSequenceParent, bind, repeatGroupBodyElement.repeat);
35260
- this.sequence = sequence;
35448
+ constructor(range, node) {
35449
+ const { bind, bodyElement, parent, root } = range;
35450
+ super(parent, bind, bodyElement);
35261
35451
  this.node = node;
35262
- this.nodeName = sequence.nodeName;
35452
+ this.nodeName = range.nodeName;
35263
35453
  this.children = root.buildSubtree(this);
35264
35454
  }
35265
35455
  type = "repeat-instance";
@@ -35268,7 +35458,7 @@ class RepeatInstanceDefinition extends DescendentNodeDefinition {
35268
35458
  instances = null;
35269
35459
  defaultValue = null;
35270
35460
  toJSON() {
35271
- const { bind, bodyElement, parent, root, sequence, ...rest } = this;
35461
+ const { bind, bodyElement, parent, root, ...rest } = this;
35272
35462
  return rest;
35273
35463
  }
35274
35464
  }
@@ -35307,15 +35497,9 @@ const splitInstanceNodes = (modelNodes) => {
35307
35497
  return [template, ...modelNodes];
35308
35498
  };
35309
35499
  class RepeatTemplateDefinition extends DescendentNodeDefinition {
35310
- constructor(sequence, templateNode) {
35311
- const {
35312
- bind,
35313
- bodyElement: repeatGroupBodyElement,
35314
- parent: repeatSequenceParent,
35315
- root
35316
- } = sequence;
35317
- super(repeatSequenceParent, bind, repeatGroupBodyElement.repeat);
35318
- this.sequence = sequence;
35500
+ constructor(range, templateNode) {
35501
+ const { bind, bodyElement, parent, root } = range;
35502
+ super(parent, bind, bodyElement);
35319
35503
  this.templateNode = templateNode;
35320
35504
  const node = templateNode.cloneNode(true);
35321
35505
  node.removeAttributeNS(JAVAROSA_NAMESPACE_URI$1, "template");
@@ -35323,14 +35507,14 @@ class RepeatTemplateDefinition extends DescendentNodeDefinition {
35323
35507
  this.nodeName = node.localName;
35324
35508
  this.children = root.buildSubtree(this);
35325
35509
  }
35326
- static parseModelNodes(sequence, modelNodes) {
35327
- const { bind } = sequence;
35510
+ static parseModelNodes(range, modelNodes) {
35511
+ const { bind } = range;
35328
35512
  let template = repeatTemplates.get(bind);
35329
35513
  let instanceNodes;
35330
35514
  if (template == null) {
35331
35515
  const [templateNode, ...rest] = splitInstanceNodes(modelNodes);
35332
35516
  instanceNodes = rest;
35333
- template = new this(sequence, templateNode);
35517
+ template = new this(range, templateNode);
35334
35518
  } else {
35335
35519
  const duplicateTemplate = modelNodes.find(
35336
35520
  (node) => node.hasAttributeNS(JAVAROSA_NAMESPACE_URI$1, "template")
@@ -35352,12 +35536,12 @@ class RepeatTemplateDefinition extends DescendentNodeDefinition {
35352
35536
  instances = null;
35353
35537
  defaultValue = null;
35354
35538
  toJSON() {
35355
- const { bind, bodyElement, parent, root, sequence, ...rest } = this;
35539
+ const { bind, bodyElement, parent, root, ...rest } = this;
35356
35540
  return rest;
35357
35541
  }
35358
35542
  }
35359
35543
 
35360
- class RepeatSequenceDefinition extends DescendentNodeDefinition {
35544
+ class RepeatRangeDefinition extends DescendentNodeDefinition {
35361
35545
  // TODO: if an implicit template is derived from an instance in a form
35362
35546
  // definition, should its default values (if any) be cleared? Probably!
35363
35547
  static createTemplateElement(instanceElement) {
@@ -35366,7 +35550,7 @@ class RepeatSequenceDefinition extends DescendentNodeDefinition {
35366
35550
  static createInstanceElement(templateElement) {
35367
35551
  return templateElement.cloneNode(true);
35368
35552
  }
35369
- type = "repeat-sequence";
35553
+ type = "repeat-range";
35370
35554
  template;
35371
35555
  children = null;
35372
35556
  instances;
@@ -35390,7 +35574,7 @@ class RepeatSequenceDefinition extends DescendentNodeDefinition {
35390
35574
 
35391
35575
  class SubtreeDefinition extends DescendentNodeDefinition {
35392
35576
  constructor(parent, bind, bodyElement, node) {
35393
- if (bodyElement != null && (bodyElement.category !== "structure" || bodyElement.type === "repeat-group")) {
35577
+ if (bodyElement != null && (bodyElement.category !== "structure" || bodyElement.type === "repeat")) {
35394
35578
  throw new Error(`Unexpected body element for nodeset ${bind.nodeset}`);
35395
35579
  }
35396
35580
  super(parent, bind, bodyElement);
@@ -35432,9 +35616,10 @@ class ValueNodeDefinition extends DescendentNodeDefinition {
35432
35616
  }
35433
35617
 
35434
35618
  class RootDefinition {
35435
- constructor(form, model) {
35619
+ constructor(form, model, classes) {
35436
35620
  this.form = form;
35437
35621
  this.model = model;
35622
+ this.classes = classes;
35438
35623
  const { primaryInstanceRoot } = form.xformDOM;
35439
35624
  const { localName: rootNodeName } = primaryInstanceRoot;
35440
35625
  this.nodeName = rootNodeName;
@@ -35483,13 +35668,8 @@ class RootDefinition {
35483
35668
  const bind = binds.getOrCreateBindDefinition(nodeset);
35484
35669
  const bodyElement = body.getBodyElement(nodeset);
35485
35670
  const [firstChild, ...restChildren] = children;
35486
- const repeatGroup = body.getRepeatGroup(nodeset);
35487
- if (repeatGroup != null) {
35488
- const repeatDefinition = bodyElement.repeat;
35489
- if (repeatDefinition == null) {
35490
- throw 'TODO: this is why I have hesitated to pick an "is repeat" predicate direction';
35491
- }
35492
- return new RepeatSequenceDefinition(parent, bind, repeatGroup, children);
35671
+ if (bodyElement?.type === "repeat") {
35672
+ return new RepeatRangeDefinition(parent, bind, bodyElement, children);
35493
35673
  }
35494
35674
  if (restChildren.length) {
35495
35675
  throw new Error(`Unexpected: multiple elements for non-repeat nodeset: ${nodeset}`);
@@ -35512,7 +35692,7 @@ class ModelDefinition {
35512
35692
  constructor(form) {
35513
35693
  this.form = form;
35514
35694
  this.binds = ModelBindMap.fromModel(this);
35515
- this.root = new RootDefinition(form, this);
35695
+ this.root = new RootDefinition(form, this, form.body.classes);
35516
35696
  }
35517
35697
  binds;
35518
35698
  root;
@@ -35642,11 +35822,26 @@ const createUniqueId = (() => {
35642
35822
 
35643
35823
  const createChildrenState = (parent) => {
35644
35824
  return parent.scope.runTask(() => {
35645
- const children = createSignal([]);
35646
- const [getChildren, setChildren] = children;
35647
- const childIds = createMemo(() => {
35648
- return getChildren().map((child) => child.nodeId);
35649
- });
35825
+ const baseState = createSignal([]);
35826
+ const [getChildren, baseSetChildren] = baseState;
35827
+ const ids = createSignal([]);
35828
+ const [childIds, setChildIds] = ids;
35829
+ const setChildren = (valueOrSetterCallback) => {
35830
+ let setterCallback;
35831
+ if (typeof valueOrSetterCallback === "function") {
35832
+ setterCallback = valueOrSetterCallback;
35833
+ } else {
35834
+ setterCallback = (_) => valueOrSetterCallback;
35835
+ }
35836
+ return baseSetChildren((prev) => {
35837
+ const result = setterCallback(prev);
35838
+ setChildIds(() => {
35839
+ return result.map((child) => child.nodeId);
35840
+ });
35841
+ return result;
35842
+ });
35843
+ };
35844
+ const children = [getChildren, setChildren];
35650
35845
  return {
35651
35846
  children,
35652
35847
  getChildren,
@@ -35656,13 +35851,13 @@ const createChildrenState = (parent) => {
35656
35851
  });
35657
35852
  };
35658
35853
 
35659
- const materializeCurrentStateChildren = (currentState, childrenState) => {
35854
+ const materializeCurrentStateChildren = (scope, currentState, childrenState) => {
35660
35855
  const baseState = currentState;
35661
35856
  const proxyTarget = baseState;
35662
35857
  return new Proxy(proxyTarget, {
35663
35858
  get(_, key) {
35664
35859
  if (key === "children") {
35665
- currentState.children;
35860
+ scope.runTask(() => currentState.children);
35666
35861
  const children = childrenState.getChildren();
35667
35862
  return children;
35668
35863
  }
@@ -35865,104 +36060,59 @@ const declareNodeID = (id) => {
35865
36060
  };
35866
36061
 
35867
36062
  class InstanceNode {
35868
- constructor(engineConfig, parent, definition) {
36063
+ constructor(engineConfig, parent, definition, options) {
35869
36064
  this.engineConfig = engineConfig;
35870
36065
  this.parent = parent;
35871
36066
  this.definition = definition;
36067
+ this.computeReference = options?.computeReference ?? this.computeChildStepReference;
35872
36068
  this.scope = createReactiveScope();
35873
36069
  this.engineConfig = engineConfig;
35874
36070
  this.nodeId = declareNodeID(engineConfig.createUniqueId());
35875
36071
  this.definition = definition;
35876
- const checkStateInitialized = () => this.engineState != null;
35877
- const [isStateInitialized, setStateInitialized] = createSignal(checkStateInitialized());
35878
- this.isStateInitialized = isStateInitialized;
35879
- queueMicrotask(() => {
35880
- if (checkStateInitialized()) {
35881
- setStateInitialized(true);
35882
- } else {
35883
- throw new Error("Node state was never initialized");
35884
- }
35885
- });
35886
- }
35887
- isStateInitialized;
35888
- /**
35889
- * Provides a generalized mechanism for accessing a reactive state value
35890
- * during a node's construction, while {@link engineState} is still being
35891
- * defined and thus isn't assigned.
35892
- *
35893
- * The fallback value specified in {@link options} will be returned on access
35894
- * until {@link isStateInitialized} returns true. This ensures:
35895
- *
35896
- * - a value of the expected type will be available
35897
- * - any read access will become reactive to the actual state, once it has
35898
- * been initialized and {@link engineState} is assigned
35899
- *
35900
- * @todo This is one among several chicken/egg problems encountered trying to
35901
- * support state initialization in which some aspects of the state derive from
35902
- * other aspects of it. It would be nice to dispense with this entirely. But
35903
- * if it must persist, we should also consider replacing the method with a
35904
- * direct accessor once state initialization completes, so the initialized
35905
- * check is only called until it becomes impertinent.
35906
- */
35907
- getInitializedState(key, options) {
35908
- if (this.isStateInitialized()) {
35909
- return this.engineState[key];
35910
- }
35911
- return options.uninitializedFallback;
35912
- }
35913
- /**
35914
- * @package Exposed on every node type to facilitate inheritance, as well as
35915
- * conditional behavior for value nodes.
35916
- */
35917
- get isReadonly() {
35918
- return this.getInitializedState("readonly", {
35919
- uninitializedFallback: false
35920
- });
35921
- }
35922
- /**
35923
- * @package Exposed on every node type to facilitate inheritance, as well as
35924
- * conditional behavior for value nodes.
35925
- */
35926
- get isRelevant() {
35927
- return this.getInitializedState("relevant", {
35928
- uninitializedFallback: true
35929
- });
35930
36072
  }
35931
36073
  // BaseNode: identity
35932
36074
  nodeId;
35933
36075
  // EvaluationContext *and* Subscribable: node-specific
35934
36076
  scope;
36077
+ computeReference;
36078
+ computeChildStepReference = (parent, definition) => {
36079
+ if (parent == null) {
36080
+ throw new Error(
36081
+ "Cannot compute child step reference of node without parent (was this called from `Root`?)"
36082
+ );
36083
+ }
36084
+ return `${parent.contextReference()}/${definition.nodeName}`;
36085
+ };
35935
36086
  // EvaluationContext: node-specific
35936
- get contextReference() {
36087
+ contextReference = () => {
35937
36088
  return this.computeReference(this.parent, this.definition);
35938
- }
35939
- getNodeByReference(visited, dependencyReference) {
36089
+ };
36090
+ getNodesByReference(visited, dependencyReference) {
35940
36091
  if (visited.has(this)) {
35941
- return null;
36092
+ return [];
35942
36093
  }
35943
36094
  visited.add(this);
35944
36095
  const { nodeset } = this.definition;
35945
36096
  if (dependencyReference === nodeset) {
35946
- return this;
36097
+ if (this.nodeType === "repeat-instance") {
36098
+ return [this.parent];
36099
+ }
36100
+ return [this];
35947
36101
  }
35948
36102
  if (dependencyReference.startsWith(`${nodeset}/`) || dependencyReference.startsWith(`${nodeset}[`)) {
35949
- const children = this.getChildren();
35950
- if (children == null) {
35951
- return null;
35952
- }
35953
- for (const child of children) {
35954
- const dependency = child.getNodeByReference(visited, dependencyReference);
35955
- if (dependency != null) {
35956
- return dependency;
35957
- }
35958
- }
36103
+ return this.getChildren().flatMap((child) => {
36104
+ return child.getNodesByReference(visited, dependencyReference);
36105
+ });
35959
36106
  }
35960
- return this.parent?.getNodeByReference(visited, dependencyReference) ?? null;
36107
+ return this.parent?.getNodesByReference(visited, dependencyReference) ?? [];
35961
36108
  }
35962
36109
  // EvaluationContext: node-relative
35963
- getSubscribableDependencyByReference(reference) {
35964
- const visited = /* @__PURE__ */ new WeakSet();
35965
- return this.getNodeByReference(visited, reference);
36110
+ getSubscribableDependenciesByReference(reference) {
36111
+ if (this.nodeType === "root") {
36112
+ const visited = /* @__PURE__ */ new WeakSet();
36113
+ return this.getNodesByReference(visited, reference);
36114
+ }
36115
+ return this.root.getSubscribableDependenciesByReference(reference);
35966
36116
  }
35967
36117
  // SubscribableDependency
35968
36118
  /**
@@ -36054,7 +36204,7 @@ const createComputedExpression = (context, dependentExpression) => {
36054
36204
  }
36055
36205
  const getReferencedDependencies = createMemo(() => {
36056
36206
  return dependencyReferences.flatMap((reference) => {
36057
- return context.getSubscribableDependencyByReference(reference) ?? [];
36207
+ return context.getSubscribableDependenciesByReference(reference) ?? [];
36058
36208
  });
36059
36209
  });
36060
36210
  let getDependencies;
@@ -36145,42 +36295,45 @@ const createNodeLabel = (context, definition) => {
36145
36295
  };
36146
36296
 
36147
36297
  class DescendantNode extends InstanceNode {
36148
- constructor(parent, definition) {
36149
- super(parent.engineConfig, parent, definition);
36298
+ constructor(parent, definition, options) {
36299
+ super(parent.engineConfig, parent, definition, options);
36150
36300
  this.parent = parent;
36151
36301
  this.definition = definition;
36152
36302
  const { evaluator, root } = parent;
36153
36303
  this.root = root;
36154
36304
  this.evaluator = evaluator;
36155
36305
  this.contextNode = this.initializeContextNode(parent.contextNode, definition.nodeName);
36156
- }
36306
+ const { readonly, relevant, required } = definition.bind;
36307
+ this.isSelfReadonly = createComputedExpression(this, readonly);
36308
+ this.isSelfRelevant = createComputedExpression(this, relevant);
36309
+ this.isRequired = createComputedExpression(this, required);
36310
+ }
36311
+ hasReadonlyAncestor = () => {
36312
+ const { parent } = this;
36313
+ return parent.hasReadonlyAncestor() || parent.isReadonly();
36314
+ };
36315
+ isSelfReadonly;
36316
+ isReadonly = () => {
36317
+ if (this.hasReadonlyAncestor()) {
36318
+ return true;
36319
+ }
36320
+ return this.isSelfReadonly();
36321
+ };
36322
+ hasNonRelevantAncestor = () => {
36323
+ const { parent } = this;
36324
+ return parent.hasNonRelevantAncestor() || !parent.isRelevant();
36325
+ };
36326
+ isSelfRelevant;
36327
+ isRelevant = () => {
36328
+ if (this.hasNonRelevantAncestor()) {
36329
+ return false;
36330
+ }
36331
+ return this.isSelfRelevant();
36332
+ };
36333
+ isRequired;
36157
36334
  root;
36158
36335
  evaluator;
36159
36336
  contextNode;
36160
- computeChildStepReference(parent) {
36161
- return `${parent.contextReference}/${this.definition.nodeName}`;
36162
- }
36163
- buildSharedStateSpec(parent, definition) {
36164
- return this.scope.runTask(() => {
36165
- const reference = createMemo(() => this.contextReference);
36166
- const { bind } = definition;
36167
- const selfReadonly = createComputedExpression(this, bind.readonly);
36168
- const readonly = createMemo(() => {
36169
- return parent.isReadonly || selfReadonly();
36170
- });
36171
- const selfRelevant = createComputedExpression(this, bind.relevant);
36172
- const relevant = createMemo(() => {
36173
- return parent.isRelevant && selfRelevant();
36174
- });
36175
- const required = createComputedExpression(this, bind.required);
36176
- return {
36177
- reference,
36178
- readonly,
36179
- relevant,
36180
- required
36181
- };
36182
- });
36183
- }
36184
36337
  createContextNode(parentContextNode, nodeName) {
36185
36338
  return parentContextNode.ownerDocument.createElement(nodeName);
36186
36339
  }
@@ -36243,16 +36396,21 @@ class Group extends DescendantNode {
36243
36396
  state;
36244
36397
  engineState;
36245
36398
  // GroupNode
36246
- currentState;
36247
36399
  nodeType = "group";
36400
+ appearances;
36401
+ currentState;
36248
36402
  constructor(parent, definition) {
36249
36403
  super(parent, definition);
36404
+ this.appearances = definition.bodyElement.appearances;
36250
36405
  const childrenState = createChildrenState(this);
36251
36406
  this.childrenState = childrenState;
36252
36407
  const state = createSharedNodeState(
36253
36408
  this.scope,
36254
36409
  {
36255
- ...this.buildSharedStateSpec(parent, definition),
36410
+ reference: this.contextReference,
36411
+ readonly: this.isReadonly,
36412
+ relevant: this.isRelevant,
36413
+ required: this.isRequired,
36256
36414
  label: createNodeLabel(this, definition),
36257
36415
  hint: null,
36258
36416
  children: childrenState.childIds,
@@ -36265,12 +36423,13 @@ class Group extends DescendantNode {
36265
36423
  );
36266
36424
  this.state = state;
36267
36425
  this.engineState = state.engineState;
36268
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
36426
+ this.currentState = materializeCurrentStateChildren(
36427
+ this.scope,
36428
+ state.currentState,
36429
+ childrenState
36430
+ );
36269
36431
  childrenState.setChildren(buildChildren(this));
36270
36432
  }
36271
- computeReference(parent) {
36272
- return this.computeChildStepReference(parent);
36273
- }
36274
36433
  getChildren() {
36275
36434
  return this.childrenState.getChildren();
36276
36435
  }
@@ -36295,20 +36454,30 @@ const insertAtIndex = (currentValues, insertionIndex, newValue) => {
36295
36454
 
36296
36455
  class RepeatInstance extends DescendantNode {
36297
36456
  constructor(parent, definition, options) {
36298
- super(parent, definition);
36299
- this.parent = parent;
36300
- const childrenState = createChildrenState(this);
36301
- this.childrenState = childrenState;
36302
- options.precedingPrimaryInstanceNode.after(this.contextNode);
36303
36457
  const { precedingInstance } = options;
36304
36458
  const precedingIndex = precedingInstance?.currentIndex ?? (() => -1);
36305
36459
  const initialIndex = precedingIndex() + 1;
36306
36460
  const [currentIndex, setCurrentIndex] = createSignal(initialIndex);
36461
+ super(parent, definition, {
36462
+ computeReference: () => {
36463
+ const currentPosition = currentIndex() + 1;
36464
+ return `${parent.contextReference()}[${currentPosition}]`;
36465
+ }
36466
+ });
36467
+ this.parent = parent;
36468
+ this.appearances = definition.bodyElement.appearances;
36469
+ const childrenState = createChildrenState(this);
36470
+ this.childrenState = childrenState;
36471
+ options.precedingPrimaryInstanceNode.after(this.contextNode);
36307
36472
  this.currentIndex = currentIndex;
36308
36473
  const state = createSharedNodeState(
36309
36474
  this.scope,
36310
36475
  {
36311
- ...this.buildSharedStateSpec(parent, definition),
36476
+ reference: this.contextReference,
36477
+ readonly: this.isReadonly,
36478
+ relevant: this.isRelevant,
36479
+ required: this.isRequired,
36480
+ // TODO: only-child <group><label>
36312
36481
  label: createNodeLabel(this, definition),
36313
36482
  hint: null,
36314
36483
  children: childrenState.childIds,
@@ -36321,7 +36490,11 @@ class RepeatInstance extends DescendantNode {
36321
36490
  );
36322
36491
  this.state = state;
36323
36492
  this.engineState = state.engineState;
36324
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
36493
+ this.currentState = materializeCurrentStateChildren(
36494
+ this.scope,
36495
+ state.currentState,
36496
+ childrenState
36497
+ );
36325
36498
  this.scope.runTask(() => {
36326
36499
  const computeCurrentIndex = parent.getInstanceIndex.bind(parent, this);
36327
36500
  createComputed(on(computeCurrentIndex, setCurrentIndex, { defer: true }));
@@ -36333,13 +36506,29 @@ class RepeatInstance extends DescendantNode {
36333
36506
  // InstanceNode
36334
36507
  state;
36335
36508
  engineState;
36509
+ /**
36510
+ * A repeat instance can inherit non-relevance, just like any other node. That
36511
+ * inheritance is derived from the repeat instance's parent node in the
36512
+ * primary instance XML/DOM tree (and would be semantically expected to do so
36513
+ * even if we move away from that implementation detail).
36514
+ *
36515
+ * Since {@link RepeatInstance.parent} is a {@link RepeatRange}, which is a
36516
+ * runtime data model fiction that does not exist in that hierarchy, we pass
36517
+ * this call through, allowing the {@link RepeatRange} to check the actual
36518
+ * primary instance parent node's relevance state.
36519
+ *
36520
+ * @todo Should we apply similar reasoning in {@link hasReadonlyAncestor}?
36521
+ */
36522
+ hasNonRelevantAncestor = () => {
36523
+ return this.parent.hasNonRelevantAncestor();
36524
+ };
36336
36525
  // RepeatInstanceNode
36337
36526
  nodeType = "repeat-instance";
36527
+ /**
36528
+ * @see {@link RepeatRange.appearances}
36529
+ */
36530
+ appearances;
36338
36531
  currentState;
36339
- computeReference(parent) {
36340
- const currentPosition = this.currentIndex() + 1;
36341
- return `${parent.contextReference}[${currentPosition}]`;
36342
- }
36343
36532
  initializeContextNode(parentContextNode, nodeName) {
36344
36533
  return this.createContextNode(parentContextNode, nodeName);
36345
36534
  }
@@ -36403,17 +36592,118 @@ class RepeatRange extends DescendantNode {
36403
36592
  // InstanceNode
36404
36593
  state;
36405
36594
  engineState;
36595
+ emptyRangeEvaluationContext;
36596
+ /**
36597
+ * @see {@link isSelfRelevant}
36598
+ */
36599
+ isEmptyRangeSelfRelevant;
36600
+ /**
36601
+ * A repeat range does not exist in the primary instance tree. A `relevant`
36602
+ * expression applies to each {@link RepeatInstance} child of the repeat
36603
+ * range. Determining whether a repeat range itself "is relevant" isn't a
36604
+ * concept the spec addresses, but it may be used by clients to determine
36605
+ * whether to allow interaction with the range (e.g. by adding a repeat
36606
+ * instance, or presenting the range's label when empty).
36607
+ *
36608
+ * As a naive first pass, it seems like the heuristic for this should be:
36609
+ *
36610
+ * 1. Does the repeat range have any repeat instance children?
36611
+ *
36612
+ * - If yes, go to 2.
36613
+ * - If no, go to 3.
36614
+ *
36615
+ * 2. Does one or more of those children return `true` for the node's
36616
+ * `relevant` expression (i.e. is the repeat instance "self relevant")?
36617
+ *
36618
+ * 3. Does the relevant expression return `true` for the repeat range itself
36619
+ * (where, at least for now, the context of that evaluation would be the
36620
+ * repeat range's {@link anchorNode} to ensure correct relative expressions
36621
+ * resolve correctly)?
36622
+ *
36623
+ * @todo While (3) is proactively implemented, there isn't presently a test
36624
+ * exercising it. It felt best for now to surface this for discussion in
36625
+ * review to validate that it's going in the right direction.
36626
+ *
36627
+ * @todo While (2) **is actually tested**, the tests currently in place behave
36628
+ * the same way with only the logic for (3), regardless of whether the repeat
36629
+ * range actually has any repeat instance children. It's unclear (a) if that's
36630
+ * a preferable simplification and (b) how that might affect performance (in
36631
+ * theory it could vary depending on form structure and runtime state).
36632
+ */
36633
+ isSelfRelevant = () => {
36634
+ const instances = this.childrenState.getChildren();
36635
+ if (instances.length > 0) {
36636
+ return instances.some((instance) => instance.isSelfRelevant());
36637
+ }
36638
+ return this.isEmptyRangeSelfRelevant();
36639
+ };
36406
36640
  // RepeatRangeNode
36407
36641
  nodeType = "repeat-range";
36642
+ /**
36643
+ * @todo RepeatRange*, RepeatInstance* (and RepeatTemplate*) all share the
36644
+ * same body element, and thus all share the same definition `bodyElement`. As
36645
+ * such, they also all share the same `appearances`. At time of writing,
36646
+ * `web-forms` (Vue UI package) treats a `RepeatRangeNode`...
36647
+ *
36648
+ * - ... as a group, if the node has a label (i.e.
36649
+ * `<group><label/><repeat/></group>`)
36650
+ * - ... effectively as a fragment containing only its instances, otherwise
36651
+ *
36652
+ * We now collapse `<group><repeat>` into `<repeat>`, and no longer treat
36653
+ * "repeat group" as a concept (after parsing). According to the spec, these
36654
+ * appearances **are supposed to** come from that "repeat group" in the form
36655
+ * definition. In practice, many forms do define appearances directly on a
36656
+ * repeat element. The engine currently produces an error if both are defined
36657
+ * simultaneously, but otherwise makes no distinction between appearances in
36658
+ * these form definition shapes:
36659
+ *
36660
+ * ```xml
36661
+ * <group ref="/data/rep1" appearance="...">
36662
+ * <repeat nodeset="/data/rep1"/>
36663
+ * </group>
36664
+ *
36665
+ * <group ref="/data/rep1">
36666
+ * <repeat nodeset="/data/rep1"/ appearance="...">
36667
+ * </group>
36668
+ *
36669
+ * <repeat nodeset="/data/rep1"/ appearance="...">
36670
+ * ```
36671
+ *
36672
+ * All of the above creates considerable ambiguity about where "repeat
36673
+ * appearances" should apply, under which circumstances.
36674
+ */
36675
+ appearances;
36408
36676
  currentState;
36409
36677
  constructor(parent, definition) {
36410
36678
  super(parent, definition);
36679
+ this.appearances = definition.bodyElement.appearances;
36411
36680
  const childrenState = createChildrenState(this);
36412
36681
  this.childrenState = childrenState;
36682
+ this.anchorNode = this.contextNode.ownerDocument.createComment(
36683
+ `Begin repeat range: ${definition.nodeset}`
36684
+ );
36685
+ this.contextNode.append(this.anchorNode);
36686
+ this.emptyRangeEvaluationContext = {
36687
+ scope: this.scope,
36688
+ evaluator: this.evaluator,
36689
+ root: this.root,
36690
+ contextReference: this.contextReference,
36691
+ contextNode: this.anchorNode,
36692
+ getSubscribableDependenciesByReference: (reference) => {
36693
+ return this.getSubscribableDependenciesByReference(reference);
36694
+ }
36695
+ };
36696
+ this.isEmptyRangeSelfRelevant = createComputedExpression(
36697
+ this.emptyRangeEvaluationContext,
36698
+ definition.bind.relevant
36699
+ );
36413
36700
  const state = createSharedNodeState(
36414
36701
  this.scope,
36415
36702
  {
36416
- ...this.buildSharedStateSpec(parent, definition),
36703
+ reference: this.contextReference,
36704
+ readonly: this.isReadonly,
36705
+ relevant: this.isRelevant,
36706
+ required: this.isRequired,
36417
36707
  label: createNodeLabel(this, definition),
36418
36708
  hint: null,
36419
36709
  children: childrenState.childIds,
@@ -36424,13 +36714,13 @@ class RepeatRange extends DescendantNode {
36424
36714
  clientStateFactory: this.engineConfig.stateFactory
36425
36715
  }
36426
36716
  );
36427
- this.anchorNode = this.contextNode.ownerDocument.createComment(
36428
- `Begin repeat range: ${definition.nodeset}`
36429
- );
36430
- this.contextNode.append(this.anchorNode);
36431
36717
  this.state = state;
36432
36718
  this.engineState = state.engineState;
36433
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
36719
+ this.currentState = materializeCurrentStateChildren(
36720
+ this.scope,
36721
+ state.currentState,
36722
+ childrenState
36723
+ );
36434
36724
  definition.instances.forEach((instanceDefinition, index) => {
36435
36725
  const afterIndex = index - 1;
36436
36726
  this.addInstances(afterIndex, 1, instanceDefinition);
@@ -36442,9 +36732,6 @@ class RepeatRange extends DescendantNode {
36442
36732
  initializeContextNode(parentContextNode) {
36443
36733
  return parentContextNode;
36444
36734
  }
36445
- computeReference(parent) {
36446
- return this.computeChildStepReference(parent);
36447
- }
36448
36735
  getInstanceIndex(instance) {
36449
36736
  return this.engineState.children.indexOf(instance.nodeId);
36450
36737
  }
@@ -36516,25 +36803,6 @@ class RepeatRange extends DescendantNode {
36516
36803
  }
36517
36804
  }
36518
36805
 
36519
- const XML_XPATH_WHITESPACE_SUBPATTERN = "[\\x20\\x09\\x0D\\x0A]";
36520
- const XML_XPATH_WHITESPACE_PATTERN = new RegExp(XML_XPATH_WHITESPACE_SUBPATTERN, "g");
36521
- const XML_XPATH_LEADING_TRAILING_WHITESPACE_PATTERN = new RegExp(
36522
- `^${XML_XPATH_WHITESPACE_SUBPATTERN}+|${XML_XPATH_WHITESPACE_SUBPATTERN}+$`,
36523
- "g"
36524
- );
36525
- const XPATH_REPEATING_WHITESPACE_PATTERN = new RegExp(
36526
- `${XML_XPATH_WHITESPACE_SUBPATTERN}{2,}`,
36527
- "g"
36528
- );
36529
- const trimXMLXPathWhitespace = (value) => value.replaceAll(XML_XPATH_LEADING_TRAILING_WHITESPACE_PATTERN, "");
36530
- const normalizeXMLXPathWhitespace = (value) => trimXMLXPathWhitespace(value).replaceAll(XPATH_REPEATING_WHITESPACE_PATTERN, " ");
36531
- const xmlXPathWhitespaceSeparatedList = (value, options) => {
36532
- if (value === "") {
36533
- return [];
36534
- }
36535
- return normalizeXMLXPathWhitespace(value).split(XML_XPATH_WHITESPACE_PATTERN);
36536
- };
36537
-
36538
36806
  const createSelectItemLabel = (context, definition) => {
36539
36807
  const { label, value } = definition;
36540
36808
  return createTextRange(context, "label", label, {
@@ -36563,15 +36831,14 @@ class ItemsetItemEvaluationContext {
36563
36831
  this.scope = selectField.scope;
36564
36832
  this.evaluator = selectField.evaluator;
36565
36833
  this.root = selectField.root;
36834
+ this.contextReference = selectField.contextReference;
36566
36835
  }
36567
36836
  scope;
36568
36837
  evaluator;
36569
36838
  root;
36570
- get contextReference() {
36571
- return this.selectField.contextReference;
36572
- }
36573
- getSubscribableDependencyByReference(reference) {
36574
- return this.selectField.getSubscribableDependencyByReference(reference);
36839
+ contextReference;
36840
+ getSubscribableDependenciesByReference(reference) {
36841
+ return this.selectField.getSubscribableDependenciesByReference(reference);
36575
36842
  }
36576
36843
  }
36577
36844
  const createSelectItemsetItemLabel = (context, definition, itemValue) => {
@@ -36635,7 +36902,7 @@ const createPrimaryInstanceValueState = (context, options) => {
36635
36902
  const initialValue = initialValueSource === "PRIMARY_INSTANCE" ? contextNode.textContent ?? defaultValue : defaultValue;
36636
36903
  const persistedValueState = createSignal(
36637
36904
  {
36638
- isRelevant: context.isRelevant,
36905
+ isRelevant: context.isRelevant(),
36639
36906
  value: initialValue
36640
36907
  },
36641
36908
  {
@@ -36652,7 +36919,7 @@ const createPrimaryInstanceValueState = (context, options) => {
36652
36919
  );
36653
36920
  const [persistedValue, setValueForPersistence] = persistedValueState;
36654
36921
  createComputed(() => {
36655
- const isRelevant = context.isRelevant;
36922
+ const isRelevant = context.isRelevant();
36656
36923
  setValueForPersistence((persisted) => {
36657
36924
  return {
36658
36925
  isRelevant,
@@ -36675,7 +36942,7 @@ const createPrimaryInstanceValueState = (context, options) => {
36675
36942
  });
36676
36943
  const setPrimaryInstanceValue = (value) => {
36677
36944
  const persisted = setValueForPersistence({
36678
- isRelevant: context.isRelevant,
36945
+ isRelevant: context.isRelevant(),
36679
36946
  value
36680
36947
  });
36681
36948
  return persisted.value;
@@ -36704,8 +36971,8 @@ const guardDownstreamReadonlyWrites = (context, baseState) => {
36704
36971
  }
36705
36972
  const [getValue, baseSetValue] = baseState;
36706
36973
  const setValue = (value) => {
36707
- if (context.isReadonly) {
36708
- const reference = untrack(() => context.contextReference);
36974
+ if (context.isReadonly()) {
36975
+ const reference = untrack(() => context.contextReference());
36709
36976
  throw new Error(`Cannot write to readonly field: ${reference}`);
36710
36977
  }
36711
36978
  return baseSetValue(value);
@@ -36716,7 +36983,7 @@ const createCalculation = (context, setValue, calculateDefinition) => {
36716
36983
  context.scope.runTask(() => {
36717
36984
  const calculate = createComputedExpression(context, calculateDefinition);
36718
36985
  createComputed(() => {
36719
- if (context.isRelevant) {
36986
+ if (context.isRelevant()) {
36720
36987
  const calculated = calculate();
36721
36988
  const value = context.decodeValue(calculated);
36722
36989
  setValue(value);
@@ -36749,6 +37016,7 @@ class SelectField extends DescendantNode {
36749
37016
  engineState;
36750
37017
  // SelectNode
36751
37018
  nodeType = "select";
37019
+ appearances;
36752
37020
  currentState;
36753
37021
  // ValueContext
36754
37022
  encodeValue = (runtimeValue) => {
@@ -36757,7 +37025,9 @@ class SelectField extends DescendantNode {
36757
37025
  };
36758
37026
  decodeValue = (instanceValue) => {
36759
37027
  return this.scope.runTask(() => {
36760
- const values = xmlXPathWhitespaceSeparatedList(instanceValue);
37028
+ const values = xmlXPathWhitespaceSeparatedList(instanceValue, {
37029
+ ignoreEmpty: true
37030
+ });
36761
37031
  const items = this.getSelectItemsByValue();
36762
37032
  return values.map((value) => {
36763
37033
  return items.get(value);
@@ -36769,13 +37039,17 @@ class SelectField extends DescendantNode {
36769
37039
  getValueOptions;
36770
37040
  constructor(parent, definition) {
36771
37041
  super(parent, definition);
37042
+ this.appearances = definition.bodyElement.appearances;
36772
37043
  this.selectExclusive = definition.bodyElement.type === "select1";
36773
37044
  const valueOptions = createSelectItems(this);
36774
37045
  this.getValueOptions = valueOptions;
36775
37046
  const state = createSharedNodeState(
36776
37047
  this.scope,
36777
37048
  {
36778
- ...this.buildSharedStateSpec(parent, definition),
37049
+ reference: this.contextReference,
37050
+ readonly: this.isReadonly,
37051
+ relevant: this.isRelevant,
37052
+ required: this.isRequired,
36779
37053
  label: createNodeLabel(this, definition),
36780
37054
  hint: createFieldHint(this, definition),
36781
37055
  children: null,
@@ -36797,9 +37071,6 @@ class SelectField extends DescendantNode {
36797
37071
  })
36798
37072
  );
36799
37073
  }
36800
- computeReference(parent) {
36801
- return this.computeChildStepReference(parent);
36802
- }
36803
37074
  updateSelectedItemValues(values) {
36804
37075
  const itemsByValue = untrack(() => this.getSelectItemsByValue());
36805
37076
  const items = values.flatMap((value) => {
@@ -36862,16 +37133,21 @@ class StringField extends DescendantNode {
36862
37133
  engineState;
36863
37134
  // StringNode
36864
37135
  nodeType = "string";
37136
+ appearances;
36865
37137
  currentState;
36866
37138
  // ValueContext
36867
37139
  encodeValue = identity$1;
36868
37140
  decodeValue = identity$1;
36869
37141
  constructor(parent, definition) {
36870
37142
  super(parent, definition);
37143
+ this.appearances = definition.bodyElement?.appearances ?? null;
36871
37144
  const state = createSharedNodeState(
36872
37145
  this.scope,
36873
37146
  {
36874
- ...this.buildSharedStateSpec(parent, definition),
37147
+ reference: this.contextReference,
37148
+ readonly: this.isReadonly,
37149
+ relevant: this.isRelevant,
37150
+ required: this.isRequired,
36875
37151
  label: createNodeLabel(this, definition),
36876
37152
  hint: createFieldHint(this, definition),
36877
37153
  children: null,
@@ -36886,9 +37162,6 @@ class StringField extends DescendantNode {
36886
37162
  this.engineState = state.engineState;
36887
37163
  this.currentState = state.currentState;
36888
37164
  }
36889
- computeReference(parent) {
36890
- return this.computeChildStepReference(parent);
36891
- }
36892
37165
  // InstanceNode
36893
37166
  getChildren() {
36894
37167
  return [];
@@ -36907,6 +37180,7 @@ class Subtree extends DescendantNode {
36907
37180
  engineState;
36908
37181
  // SubtreeNode
36909
37182
  nodeType = "subtree";
37183
+ appearances = null;
36910
37184
  currentState;
36911
37185
  constructor(parent, definition) {
36912
37186
  super(parent, definition);
@@ -36915,7 +37189,10 @@ class Subtree extends DescendantNode {
36915
37189
  const state = createSharedNodeState(
36916
37190
  this.scope,
36917
37191
  {
36918
- ...this.buildSharedStateSpec(parent, definition),
37192
+ reference: this.contextReference,
37193
+ readonly: this.isReadonly,
37194
+ relevant: this.isRelevant,
37195
+ required: this.isRequired,
36919
37196
  label: null,
36920
37197
  hint: null,
36921
37198
  children: childrenState.childIds,
@@ -36928,12 +37205,13 @@ class Subtree extends DescendantNode {
36928
37205
  );
36929
37206
  this.state = state;
36930
37207
  this.engineState = state.engineState;
36931
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
37208
+ this.currentState = materializeCurrentStateChildren(
37209
+ this.scope,
37210
+ state.currentState,
37211
+ childrenState
37212
+ );
36932
37213
  childrenState.setChildren(buildChildren(this));
36933
37214
  }
36934
- computeReference(parent) {
36935
- return this.computeChildStepReference(parent);
36936
- }
36937
37215
  getChildren() {
36938
37216
  return this.childrenState.getChildren();
36939
37217
  }
@@ -36952,14 +37230,20 @@ const buildChildren = (parent) => {
36952
37230
  }
36953
37231
  return new Group(parent, child);
36954
37232
  }
36955
- case "repeat-sequence": {
37233
+ case "repeat-range": {
36956
37234
  return new RepeatRange(parent, child);
36957
37235
  }
36958
37236
  case "value-node": {
36959
- if (child.bodyElement instanceof SelectDefinition) {
36960
- return new SelectField(parent, child);
37237
+ switch (child.bodyElement?.type) {
37238
+ case "select":
37239
+ case "select1":
37240
+ return new SelectField(parent, child);
37241
+ case "input":
37242
+ case void 0:
37243
+ return new StringField(parent, child);
37244
+ default:
37245
+ throw new UnreachableError(child.bodyElement);
36961
37246
  }
36962
- return new StringField(parent, child);
36963
37247
  }
36964
37248
  default: {
36965
37249
  throw new UnreachableError(child);
@@ -36998,27 +37282,24 @@ const getInitialLanguageState = (translations) => {
36998
37282
  };
36999
37283
  };
37000
37284
  class Root extends InstanceNode {
37001
- static async initialize(xformDOM, definition, engineConfig) {
37002
- const instance = new Root(xformDOM, definition, engineConfig);
37003
- await instance.formStateInitialized();
37004
- return instance;
37005
- }
37006
37285
  childrenState;
37007
37286
  // InstanceNode
37287
+ hasReadonlyAncestor = () => false;
37288
+ isReadonly = () => false;
37289
+ hasNonRelevantAncestor = () => false;
37290
+ isRelevant = () => true;
37008
37291
  state;
37009
37292
  engineState;
37010
37293
  // RootNode
37011
37294
  nodeType = "root";
37295
+ appearances = null;
37296
+ classes;
37012
37297
  currentState;
37013
37298
  instanceDOM;
37014
37299
  // BaseNode
37015
37300
  root = this;
37016
37301
  // EvaluationContext
37017
37302
  evaluator;
37018
- rootReference;
37019
- get contextReference() {
37020
- return this.rootReference;
37021
- }
37022
37303
  contextNode;
37023
37304
  // RootNode
37024
37305
  parent = null;
@@ -37028,11 +37309,13 @@ class Root extends InstanceNode {
37028
37309
  return this.engineState.activeLanguage;
37029
37310
  }
37030
37311
  constructor(xformDOM, definition, engineConfig) {
37031
- super(engineConfig, null, definition);
37312
+ const reference = definition.nodeset;
37313
+ super(engineConfig, null, definition, {
37314
+ computeReference: () => reference
37315
+ });
37316
+ this.classes = definition.classes;
37032
37317
  const childrenState = createChildrenState(this);
37033
37318
  this.childrenState = childrenState;
37034
- const reference = definition.nodeset;
37035
- this.rootReference = reference;
37036
37319
  const instanceDOM = xformDOM.createInstance();
37037
37320
  const evaluator = instanceDOM.primaryInstanceEvaluator;
37038
37321
  const { translations } = evaluator;
@@ -37057,7 +37340,11 @@ class Root extends InstanceNode {
37057
37340
  );
37058
37341
  this.state = state;
37059
37342
  this.engineState = state.engineState;
37060
- this.currentState = materializeCurrentStateChildren(state.currentState, childrenState);
37343
+ this.currentState = materializeCurrentStateChildren(
37344
+ this.scope,
37345
+ state.currentState,
37346
+ childrenState
37347
+ );
37061
37348
  const contextNode = instanceDOM.xformDocument.createElement(definition.nodeName);
37062
37349
  instanceDOM.primaryInstanceRoot.replaceWith(contextNode);
37063
37350
  this.evaluator = evaluator;
@@ -37066,37 +37353,6 @@ class Root extends InstanceNode {
37066
37353
  this.languages = languages;
37067
37354
  childrenState.setChildren(buildChildren(this));
37068
37355
  }
37069
- /**
37070
- * Waits until form state is fully initialized.
37071
- *
37072
- * As much as possible, all instance state computations are implemented so
37073
- * that they complete synchronously.
37074
- *
37075
- * There is currently one exception: because instance nodes may form
37076
- * computation dependencies into their descendants as well as their ancestors,
37077
- * there is an allowance **during form initialization only** to account for
37078
- * this chicken/egg scenario. Note that this allowance is intentionally,
37079
- * strictly limited: if form state initialization is not resolved within a
37080
- * single microtask tick we throw/reject.
37081
- *
37082
- * All subsequent computations are always performed synchronously (and we will
37083
- * use tests to validate this, by utilizing the synchronously returned `Root`
37084
- * state from client-facing write interfaces).
37085
- */
37086
- async formStateInitialized() {
37087
- await new Promise((resolve) => {
37088
- queueMicrotask(resolve);
37089
- });
37090
- if (!this.isStateInitialized()) {
37091
- throw new Error(
37092
- "Form state initialization failed to complete in a single frame. Has some aspect of reactive computation been made asynchronous by mistake?"
37093
- );
37094
- }
37095
- }
37096
- // InstanceNode
37097
- computeReference(_parent, definition) {
37098
- return definition.nodeset;
37099
- }
37100
37356
  getChildren() {
37101
37357
  return this.childrenState.getChildren();
37102
37358
  }
@@ -37130,7 +37386,7 @@ const initializeForm$1 = async (input, options = {}) => {
37130
37386
  const engineConfig = buildInstanceConfig(options.config);
37131
37387
  const sourceXML = await retrieveSourceXMLResource(input, engineConfig);
37132
37388
  const form = new XFormDefinition(sourceXML);
37133
- return Root.initialize(form.xformDOM, form.model.root, engineConfig);
37389
+ return new Root(form.xformDOM, form.model.root, engineConfig);
37134
37390
  };
37135
37391
 
37136
37392
  const initializeForm = initializeForm$1;