@optionfactory/fml 9.0.0-rc3 → 9.0.0-rc30

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 (44) hide show
  1. package/README.md +16 -88
  2. package/dist/client-errors.iife.js +40 -49
  3. package/dist/client-errors.iife.js.map +1 -1
  4. package/dist/client-errors.iife.min.js +1 -1
  5. package/dist/client-errors.iife.min.js.map +1 -1
  6. package/dist/custom-elements.json +483 -50
  7. package/dist/fml.css +25 -20
  8. package/dist/fml.css.map +1 -1
  9. package/dist/fml.iife.js +5688 -2542
  10. package/dist/fml.iife.js.map +1 -1
  11. package/dist/fml.iife.min.js +1 -1
  12. package/dist/fml.iife.min.js.map +1 -1
  13. package/dist/ftl.d.mts +398 -227
  14. package/dist/ftl.iife.js +682 -1016
  15. package/dist/ftl.iife.js.map +1 -1
  16. package/dist/ftl.iife.min.js +1 -1
  17. package/dist/ftl.iife.min.js.map +1 -1
  18. package/dist/ftl.min.mjs +1 -1
  19. package/dist/ftl.min.mjs.map +1 -1
  20. package/dist/ftl.mjs +682 -1016
  21. package/dist/ftl.mjs.map +1 -1
  22. package/dist/ful.css +25 -20
  23. package/dist/ful.css.map +1 -1
  24. package/dist/ful.d.mts +3043 -546
  25. package/dist/ful.iife.js +4875 -1387
  26. package/dist/ful.iife.js.map +1 -1
  27. package/dist/ful.iife.min.js +1 -1
  28. package/dist/ful.iife.min.js.map +1 -1
  29. package/dist/ful.min.mjs +1 -1
  30. package/dist/ful.min.mjs.map +1 -1
  31. package/dist/ful.mjs +4873 -1389
  32. package/dist/ful.mjs.map +1 -1
  33. package/dist/httpc.d.mts +143 -123
  34. package/dist/httpc.iife.js +163 -175
  35. package/dist/httpc.iife.js.map +1 -1
  36. package/dist/httpc.iife.min.js +1 -1
  37. package/dist/httpc.iife.min.js.map +1 -1
  38. package/dist/httpc.min.mjs +1 -1
  39. package/dist/httpc.min.mjs.map +1 -1
  40. package/dist/httpc.mjs +163 -175
  41. package/dist/httpc.mjs.map +1 -1
  42. package/dist/vscode.html-custom-data.json +234 -38
  43. package/dist/web-types.json +489 -50
  44. package/package.json +2 -2
package/dist/ftl.mjs CHANGED
@@ -1,10 +1,6 @@
1
1
  /**
2
- * A Map bounded by entry count, evicting in insertion order: not an LRU (a hit
3
- * does not refresh an entry), just a cap keeping unbounded key spaces (parsed
4
- * expressions, compiled masks, formatter instances) from growing forever. The
5
- * capacity is sized so eviction never happens on a sane page: hitting it means
6
- * dynamically generated keys, where FIFO's worst case (evicting a hot entry)
7
- * costs one recomputation.
2
+ * A Map bounded by entry count, evicting the oldest entry when full. A hit does
3
+ * not refresh an entry: eviction follows insertion order, not use.
8
4
  */
9
5
  class BoundedCache {
10
6
  #max;
@@ -33,6 +29,7 @@ class BoundedCache {
33
29
  this.#entries.set(key, value);
34
30
  return value;
35
31
  }
32
+ /** @returns {number} the number of entries held */
36
33
  get size() {
37
34
  return this.#entries.size;
38
35
  }
@@ -71,14 +68,10 @@ const nodes = {
71
68
  },
72
69
  };
73
70
 
74
- // @generated by Peggy 5.1.0.
71
+ // @generated by Peggy 5.1.1.
75
72
  //
76
73
  // https://peggyjs.org/
77
74
 
78
- //keyword classification lives here, not in the grammar's ordered choice: the
79
- //identifier rule consumes the whole word and the action decides, so no keyword
80
- //can grab a longer identifier's prefix. A Map, not a plain object: `in` on the
81
- //latter would classify inherited names such as `constructor` as keywords
82
75
  const keywords = new Map([
83
76
  ['true', true],
84
77
  ['false', false],
@@ -141,10 +134,9 @@ class peg$SyntaxError extends SyntaxError {
141
134
  return s;
142
135
  }
143
136
 
144
- function literalEscape(s) {
137
+ function escape(s, special) {
145
138
  return unicodeEscape(s
146
- .replace(/\\/g, "\\\\")
147
- .replace(/"/g, "\\\"")
139
+ .replace(special, "\\$&")
148
140
  .replace(/\0/g, "\\0")
149
141
  .replace(/\t/g, "\\t")
150
142
  .replace(/\n/g, "\\n")
@@ -153,18 +145,12 @@ class peg$SyntaxError extends SyntaxError {
153
145
  .replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch)));
154
146
  }
155
147
 
148
+ function literalEscape(s) {
149
+ return escape(s, /[\\"]/g);
150
+ }
151
+
156
152
  function classEscape(s) {
157
- return unicodeEscape(s
158
- .replace(/\\/g, "\\\\")
159
- .replace(/\]/g, "\\]")
160
- .replace(/\^/g, "\\^")
161
- .replace(/-/g, "\\-")
162
- .replace(/\0/g, "\\0")
163
- .replace(/\t/g, "\\t")
164
- .replace(/\n/g, "\\n")
165
- .replace(/\r/g, "\\r")
166
- .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch))
167
- .replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch)));
153
+ return escape(s, /[\\\]^-]/g);
168
154
  }
169
155
 
170
156
  const DESCRIBE_EXPECTATION_FNS = {
@@ -282,14 +268,6 @@ function peg$parse(input, options) {
282
268
  const peg$c53 = "<=";
283
269
  const peg$c54 = "<";
284
270
 
285
- const peg$r0 = /^[^\\{]/;
286
- const peg$r1 = /^[0-9]/;
287
- const peg$r2 = /^[^']/;
288
- const peg$r3 = /^[^"]/;
289
- const peg$r4 = /^[^\\`{]/;
290
- const peg$r5 = /^[a-zA-Z$_]/;
291
- const peg$r6 = /^[a-zA-Z$_0-9]/;
292
- const peg$r7 = /^[ \t\n\r]/;
293
271
 
294
272
  const peg$e0 = peg$literalExpectation("{{{{", false);
295
273
  const peg$e1 = peg$literalExpectation("}}}}", false);
@@ -447,9 +425,10 @@ function peg$parse(input, options) {
447
425
  let peg$maxFailExpected = options.peg$maxFailExpected || [];
448
426
  let peg$silentFails = options.peg$silentFails | 0;
449
427
 
450
- let peg$resultsCache = {};
428
+ let peg$resultsCache = new Map();
451
429
 
452
430
  let peg$result;
431
+ let peg$charCode;
453
432
 
454
433
  if (options.startRule) {
455
434
  if (!(options.startRule in peg$startRuleFunctions)) {
@@ -568,7 +547,7 @@ function peg$parse(input, options) {
568
547
  let s0, s1;
569
548
 
570
549
  const key = peg$currPos * 38 + 0;
571
- const cached = peg$resultsCache[key];
550
+ const cached = peg$resultsCache.get(key);
572
551
 
573
552
  if (cached) {
574
553
  peg$currPos = cached.nextPos;
@@ -577,13 +556,13 @@ function peg$parse(input, options) {
577
556
  }
578
557
 
579
558
  s0 = [];
580
- s1 = peg$parseTemplatedExpression();
581
- while (s1 !== peg$FAILED) {
582
- s0.push(s1);
559
+ for (;;) {
583
560
  s1 = peg$parseTemplatedExpression();
561
+ if (s1 === peg$FAILED) { break; }
562
+ s0.push(s1);
584
563
  }
585
564
 
586
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
565
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
587
566
 
588
567
  return s0;
589
568
  }
@@ -592,7 +571,7 @@ function peg$parse(input, options) {
592
571
  let s0, s1, s2, s3, s4;
593
572
 
594
573
  const key = peg$currPos * 38 + 1;
595
- const cached = peg$resultsCache[key];
574
+ const cached = peg$resultsCache.get(key);
596
575
 
597
576
  if (cached) {
598
577
  peg$currPos = cached.nextPos;
@@ -688,7 +667,7 @@ function peg$parse(input, options) {
688
667
  }
689
668
  }
690
669
 
691
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
670
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
692
671
 
693
672
  return s0;
694
673
  }
@@ -697,7 +676,7 @@ function peg$parse(input, options) {
697
676
  let s0;
698
677
 
699
678
  const key = peg$currPos * 38 + 2;
700
- const cached = peg$resultsCache[key];
679
+ const cached = peg$resultsCache.get(key);
701
680
 
702
681
  if (cached) {
703
682
  peg$currPos = cached.nextPos;
@@ -713,7 +692,7 @@ function peg$parse(input, options) {
713
692
  if (peg$silentFails === 0) { peg$fail(peg$e0); }
714
693
  }
715
694
 
716
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
695
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
717
696
 
718
697
  return s0;
719
698
  }
@@ -722,7 +701,7 @@ function peg$parse(input, options) {
722
701
  let s0;
723
702
 
724
703
  const key = peg$currPos * 38 + 3;
725
- const cached = peg$resultsCache[key];
704
+ const cached = peg$resultsCache.get(key);
726
705
 
727
706
  if (cached) {
728
707
  peg$currPos = cached.nextPos;
@@ -738,7 +717,7 @@ function peg$parse(input, options) {
738
717
  if (peg$silentFails === 0) { peg$fail(peg$e1); }
739
718
  }
740
719
 
741
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
720
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
742
721
 
743
722
  return s0;
744
723
  }
@@ -747,7 +726,7 @@ function peg$parse(input, options) {
747
726
  let s0;
748
727
 
749
728
  const key = peg$currPos * 38 + 4;
750
- const cached = peg$resultsCache[key];
729
+ const cached = peg$resultsCache.get(key);
751
730
 
752
731
  if (cached) {
753
732
  peg$currPos = cached.nextPos;
@@ -763,7 +742,7 @@ function peg$parse(input, options) {
763
742
  if (peg$silentFails === 0) { peg$fail(peg$e2); }
764
743
  }
765
744
 
766
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
745
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
767
746
 
768
747
  return s0;
769
748
  }
@@ -772,7 +751,7 @@ function peg$parse(input, options) {
772
751
  let s0;
773
752
 
774
753
  const key = peg$currPos * 38 + 5;
775
- const cached = peg$resultsCache[key];
754
+ const cached = peg$resultsCache.get(key);
776
755
 
777
756
  if (cached) {
778
757
  peg$currPos = cached.nextPos;
@@ -788,7 +767,7 @@ function peg$parse(input, options) {
788
767
  if (peg$silentFails === 0) { peg$fail(peg$e3); }
789
768
  }
790
769
 
791
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
770
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
792
771
 
793
772
  return s0;
794
773
  }
@@ -797,7 +776,7 @@ function peg$parse(input, options) {
797
776
  let s0;
798
777
 
799
778
  const key = peg$currPos * 38 + 6;
800
- const cached = peg$resultsCache[key];
779
+ const cached = peg$resultsCache.get(key);
801
780
 
802
781
  if (cached) {
803
782
  peg$currPos = cached.nextPos;
@@ -813,7 +792,7 @@ function peg$parse(input, options) {
813
792
  if (peg$silentFails === 0) { peg$fail(peg$e4); }
814
793
  }
815
794
 
816
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
795
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
817
796
 
818
797
  return s0;
819
798
  }
@@ -822,7 +801,7 @@ function peg$parse(input, options) {
822
801
  let s0;
823
802
 
824
803
  const key = peg$currPos * 38 + 7;
825
- const cached = peg$resultsCache[key];
804
+ const cached = peg$resultsCache.get(key);
826
805
 
827
806
  if (cached) {
828
807
  peg$currPos = cached.nextPos;
@@ -838,7 +817,7 @@ function peg$parse(input, options) {
838
817
  if (peg$silentFails === 0) { peg$fail(peg$e5); }
839
818
  }
840
819
 
841
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
820
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
842
821
 
843
822
  return s0;
844
823
  }
@@ -847,7 +826,7 @@ function peg$parse(input, options) {
847
826
  let s0, s1, s2, s3, s4, s5, s6;
848
827
 
849
828
  const key = peg$currPos * 38 + 8;
850
- const cached = peg$resultsCache[key];
829
+ const cached = peg$resultsCache.get(key);
851
830
 
852
831
  if (cached) {
853
832
  peg$currPos = cached.nextPos;
@@ -857,211 +836,104 @@ function peg$parse(input, options) {
857
836
 
858
837
  s0 = peg$currPos;
859
838
  s1 = [];
860
- s2 = peg$currPos;
861
- s3 = [];
862
- s4 = input.charAt(peg$currPos);
863
- if (peg$r0.test(s4)) {
864
- peg$currPos++;
865
- } else {
866
- s4 = peg$FAILED;
867
- if (peg$silentFails === 0) { peg$fail(peg$e6); }
868
- }
869
- if (s4 !== peg$FAILED) {
870
- while (s4 !== peg$FAILED) {
871
- s3.push(s4);
839
+ for (;;) {
840
+ s2 = peg$currPos;
841
+ s3 = [];
842
+ for (;;) {
872
843
  s4 = input.charAt(peg$currPos);
873
- if (peg$r0.test(s4)) {
844
+ if (s4 !== "" && !(peg$charCode = s4.charCodeAt(0), peg$charCode === 92 || peg$charCode === 123)) {
874
845
  peg$currPos++;
875
846
  } else {
876
847
  s4 = peg$FAILED;
877
848
  if (peg$silentFails === 0) { peg$fail(peg$e6); }
878
849
  }
850
+ if (s4 === peg$FAILED) { break; }
851
+ s3.push(s4);
879
852
  }
880
- } else {
881
- s3 = peg$FAILED;
882
- }
883
- if (s3 !== peg$FAILED) {
884
- s2 = input.substring(s2, peg$currPos);
885
- } else {
886
- s2 = s3;
887
- }
888
- if (s2 === peg$FAILED) {
889
- s2 = peg$currPos;
890
- if (input.charCodeAt(peg$currPos) === 92) {
891
- s3 = peg$c7;
892
- peg$currPos++;
893
- } else {
853
+ if (s3.length < 1) {
894
854
  s3 = peg$FAILED;
895
- if (peg$silentFails === 0) { peg$fail(peg$e7); }
896
855
  }
897
856
  if (s3 !== peg$FAILED) {
898
- if (input.length > peg$currPos) {
899
- s4 = input.charAt(peg$currPos);
900
- peg$currPos++;
901
- } else {
902
- s4 = peg$FAILED;
903
- if (peg$silentFails === 0) { peg$fail(peg$e8); }
904
- }
905
- if (s4 !== peg$FAILED) {
906
- s2 = peg$f4(s4);
907
- } else {
908
- peg$currPos = s2;
909
- s2 = peg$FAILED;
910
- }
857
+ s2 = input.substring(s2, peg$currPos);
911
858
  } else {
912
- peg$currPos = s2;
913
- s2 = peg$FAILED;
859
+ s2 = s3;
914
860
  }
915
861
  if (s2 === peg$FAILED) {
916
862
  s2 = peg$currPos;
917
- s3 = peg$currPos;
918
- if (input.charCodeAt(peg$currPos) === 123) {
919
- s4 = peg$c9;
863
+ if (input.charCodeAt(peg$currPos) === 92) {
864
+ s3 = peg$c7;
920
865
  peg$currPos++;
921
866
  } else {
922
- s4 = peg$FAILED;
923
- if (peg$silentFails === 0) { peg$fail(peg$e9); }
867
+ s3 = peg$FAILED;
868
+ if (peg$silentFails === 0) { peg$fail(peg$e7); }
924
869
  }
925
- if (s4 !== peg$FAILED) {
926
- s5 = peg$currPos;
927
- peg$silentFails++;
928
- if (input.charCodeAt(peg$currPos) === 123) {
929
- s6 = peg$c9;
870
+ if (s3 !== peg$FAILED) {
871
+ if (input.length > peg$currPos) {
872
+ s4 = input.charAt(peg$currPos);
930
873
  peg$currPos++;
931
874
  } else {
932
- s6 = peg$FAILED;
933
- if (peg$silentFails === 0) { peg$fail(peg$e9); }
934
- }
935
- peg$silentFails--;
936
- if (s6 === peg$FAILED) {
937
- s5 = undefined;
938
- } else {
939
- peg$currPos = s5;
940
- s5 = peg$FAILED;
875
+ s4 = peg$FAILED;
876
+ if (peg$silentFails === 0) { peg$fail(peg$e8); }
941
877
  }
942
- if (s5 !== peg$FAILED) {
943
- s4 = [s4, s5];
944
- s3 = s4;
878
+ if (s4 !== peg$FAILED) {
879
+ s2 = peg$f4(s4);
945
880
  } else {
946
- peg$currPos = s3;
947
- s3 = peg$FAILED;
948
- }
949
- } else {
950
- peg$currPos = s3;
951
- s3 = peg$FAILED;
952
- }
953
- if (s3 !== peg$FAILED) {
954
- s2 = input.substring(s2, peg$currPos);
955
- } else {
956
- s2 = s3;
957
- }
958
- }
959
- }
960
- if (s2 !== peg$FAILED) {
961
- while (s2 !== peg$FAILED) {
962
- s1.push(s2);
963
- s2 = peg$currPos;
964
- s3 = [];
965
- s4 = input.charAt(peg$currPos);
966
- if (peg$r0.test(s4)) {
967
- peg$currPos++;
968
- } else {
969
- s4 = peg$FAILED;
970
- if (peg$silentFails === 0) { peg$fail(peg$e6); }
971
- }
972
- if (s4 !== peg$FAILED) {
973
- while (s4 !== peg$FAILED) {
974
- s3.push(s4);
975
- s4 = input.charAt(peg$currPos);
976
- if (peg$r0.test(s4)) {
977
- peg$currPos++;
978
- } else {
979
- s4 = peg$FAILED;
980
- if (peg$silentFails === 0) { peg$fail(peg$e6); }
981
- }
881
+ peg$currPos = s2;
882
+ s2 = peg$FAILED;
982
883
  }
983
884
  } else {
984
- s3 = peg$FAILED;
985
- }
986
- if (s3 !== peg$FAILED) {
987
- s2 = input.substring(s2, peg$currPos);
988
- } else {
989
- s2 = s3;
885
+ peg$currPos = s2;
886
+ s2 = peg$FAILED;
990
887
  }
991
888
  if (s2 === peg$FAILED) {
992
889
  s2 = peg$currPos;
993
- if (input.charCodeAt(peg$currPos) === 92) {
994
- s3 = peg$c7;
890
+ s3 = peg$currPos;
891
+ if (input.charCodeAt(peg$currPos) === 123) {
892
+ s4 = peg$c9;
995
893
  peg$currPos++;
996
894
  } else {
997
- s3 = peg$FAILED;
998
- if (peg$silentFails === 0) { peg$fail(peg$e7); }
999
- }
1000
- if (s3 !== peg$FAILED) {
1001
- if (input.length > peg$currPos) {
1002
- s4 = input.charAt(peg$currPos);
1003
- peg$currPos++;
1004
- } else {
1005
- s4 = peg$FAILED;
1006
- if (peg$silentFails === 0) { peg$fail(peg$e8); }
1007
- }
1008
- if (s4 !== peg$FAILED) {
1009
- s2 = peg$f4(s4);
1010
- } else {
1011
- peg$currPos = s2;
1012
- s2 = peg$FAILED;
1013
- }
1014
- } else {
1015
- peg$currPos = s2;
1016
- s2 = peg$FAILED;
895
+ s4 = peg$FAILED;
896
+ if (peg$silentFails === 0) { peg$fail(peg$e9); }
1017
897
  }
1018
- if (s2 === peg$FAILED) {
1019
- s2 = peg$currPos;
1020
- s3 = peg$currPos;
898
+ if (s4 !== peg$FAILED) {
899
+ s5 = peg$currPos;
900
+ peg$silentFails++;
1021
901
  if (input.charCodeAt(peg$currPos) === 123) {
1022
- s4 = peg$c9;
902
+ s6 = peg$c9;
1023
903
  peg$currPos++;
1024
904
  } else {
1025
- s4 = peg$FAILED;
905
+ s6 = peg$FAILED;
1026
906
  if (peg$silentFails === 0) { peg$fail(peg$e9); }
1027
907
  }
1028
- if (s4 !== peg$FAILED) {
1029
- s5 = peg$currPos;
1030
- peg$silentFails++;
1031
- if (input.charCodeAt(peg$currPos) === 123) {
1032
- s6 = peg$c9;
1033
- peg$currPos++;
1034
- } else {
1035
- s6 = peg$FAILED;
1036
- if (peg$silentFails === 0) { peg$fail(peg$e9); }
1037
- }
1038
- peg$silentFails--;
1039
- if (s6 === peg$FAILED) {
1040
- s5 = undefined;
1041
- } else {
1042
- peg$currPos = s5;
1043
- s5 = peg$FAILED;
1044
- }
1045
- if (s5 !== peg$FAILED) {
1046
- s4 = [s4, s5];
1047
- s3 = s4;
1048
- } else {
1049
- peg$currPos = s3;
1050
- s3 = peg$FAILED;
1051
- }
908
+ peg$silentFails--;
909
+ if (s6 === peg$FAILED) {
910
+ s5 = undefined;
1052
911
  } else {
1053
- peg$currPos = s3;
1054
- s3 = peg$FAILED;
912
+ peg$currPos = s5;
913
+ s5 = peg$FAILED;
1055
914
  }
1056
- if (s3 !== peg$FAILED) {
1057
- s2 = input.substring(s2, peg$currPos);
915
+ if (s5 !== peg$FAILED) {
916
+ s4 = [s4, s5];
917
+ s3 = s4;
1058
918
  } else {
1059
- s2 = s3;
919
+ peg$currPos = s3;
920
+ s3 = peg$FAILED;
1060
921
  }
922
+ } else {
923
+ peg$currPos = s3;
924
+ s3 = peg$FAILED;
925
+ }
926
+ if (s3 !== peg$FAILED) {
927
+ s2 = input.substring(s2, peg$currPos);
928
+ } else {
929
+ s2 = s3;
1061
930
  }
1062
931
  }
1063
932
  }
1064
- } else {
933
+ if (s2 === peg$FAILED) { break; }
934
+ s1.push(s2);
935
+ }
936
+ if (s1.length < 1) {
1065
937
  s1 = peg$FAILED;
1066
938
  }
1067
939
  if (s1 !== peg$FAILED) {
@@ -1069,7 +941,7 @@ function peg$parse(input, options) {
1069
941
  }
1070
942
  s0 = s1;
1071
943
 
1072
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
944
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1073
945
 
1074
946
  return s0;
1075
947
  }
@@ -1078,7 +950,7 @@ function peg$parse(input, options) {
1078
950
  let s0, s2;
1079
951
 
1080
952
  const key = peg$currPos * 38 + 9;
1081
- const cached = peg$resultsCache[key];
953
+ const cached = peg$resultsCache.get(key);
1082
954
 
1083
955
  if (cached) {
1084
956
  peg$currPos = cached.nextPos;
@@ -1097,7 +969,7 @@ function peg$parse(input, options) {
1097
969
  s0 = peg$FAILED;
1098
970
  }
1099
971
 
1100
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
972
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1101
973
 
1102
974
  return s0;
1103
975
  }
@@ -1106,7 +978,7 @@ function peg$parse(input, options) {
1106
978
  let s0, s1, s3, s5, s7, s9;
1107
979
 
1108
980
  const key = peg$currPos * 38 + 10;
1109
- const cached = peg$resultsCache[key];
981
+ const cached = peg$resultsCache.get(key);
1110
982
 
1111
983
  if (cached) {
1112
984
  peg$currPos = cached.nextPos;
@@ -1166,7 +1038,7 @@ function peg$parse(input, options) {
1166
1038
  s0 = peg$parseElvisExpression();
1167
1039
  }
1168
1040
 
1169
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1041
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1170
1042
 
1171
1043
  return s0;
1172
1044
  }
@@ -1175,7 +1047,7 @@ function peg$parse(input, options) {
1175
1047
  let s0, s1, s3, s5;
1176
1048
 
1177
1049
  const key = peg$currPos * 38 + 11;
1178
- const cached = peg$resultsCache[key];
1050
+ const cached = peg$resultsCache.get(key);
1179
1051
 
1180
1052
  if (cached) {
1181
1053
  peg$currPos = cached.nextPos;
@@ -1215,7 +1087,7 @@ function peg$parse(input, options) {
1215
1087
  s0 = peg$parseNullCoalescingExpression();
1216
1088
  }
1217
1089
 
1218
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1090
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1219
1091
 
1220
1092
  return s0;
1221
1093
  }
@@ -1224,7 +1096,7 @@ function peg$parse(input, options) {
1224
1096
  let s0, s1, s3, s5;
1225
1097
 
1226
1098
  const key = peg$currPos * 38 + 12;
1227
- const cached = peg$resultsCache[key];
1099
+ const cached = peg$resultsCache.get(key);
1228
1100
 
1229
1101
  if (cached) {
1230
1102
  peg$currPos = cached.nextPos;
@@ -1264,7 +1136,7 @@ function peg$parse(input, options) {
1264
1136
  s0 = peg$parseOrExpression();
1265
1137
  }
1266
1138
 
1267
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1139
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1268
1140
 
1269
1141
  return s0;
1270
1142
  }
@@ -1273,7 +1145,7 @@ function peg$parse(input, options) {
1273
1145
  let s0, s1, s2, s3, s5, s7;
1274
1146
 
1275
1147
  const key = peg$currPos * 38 + 13;
1276
- const cached = peg$resultsCache[key];
1148
+ const cached = peg$resultsCache.get(key);
1277
1149
 
1278
1150
  if (cached) {
1279
1151
  peg$currPos = cached.nextPos;
@@ -1285,30 +1157,7 @@ function peg$parse(input, options) {
1285
1157
  s1 = peg$parseAndExpression();
1286
1158
  if (s1 !== peg$FAILED) {
1287
1159
  s2 = [];
1288
- s3 = peg$currPos;
1289
- peg$parse_();
1290
- if (input.substr(peg$currPos, 2) === peg$c22) {
1291
- s5 = peg$c22;
1292
- peg$currPos += 2;
1293
- } else {
1294
- s5 = peg$FAILED;
1295
- if (peg$silentFails === 0) { peg$fail(peg$e14); }
1296
- }
1297
- if (s5 !== peg$FAILED) {
1298
- peg$parse_();
1299
- s7 = peg$parseAndExpression();
1300
- if (s7 !== peg$FAILED) {
1301
- s3 = s7;
1302
- } else {
1303
- peg$currPos = s3;
1304
- s3 = peg$FAILED;
1305
- }
1306
- } else {
1307
- peg$currPos = s3;
1308
- s3 = peg$FAILED;
1309
- }
1310
- while (s3 !== peg$FAILED) {
1311
- s2.push(s3);
1160
+ for (;;) {
1312
1161
  s3 = peg$currPos;
1313
1162
  peg$parse_();
1314
1163
  if (input.substr(peg$currPos, 2) === peg$c22) {
@@ -1331,6 +1180,8 @@ function peg$parse(input, options) {
1331
1180
  peg$currPos = s3;
1332
1181
  s3 = peg$FAILED;
1333
1182
  }
1183
+ if (s3 === peg$FAILED) { break; }
1184
+ s2.push(s3);
1334
1185
  }
1335
1186
  s0 = peg$f10(s1, s2);
1336
1187
  } else {
@@ -1338,7 +1189,7 @@ function peg$parse(input, options) {
1338
1189
  s0 = peg$FAILED;
1339
1190
  }
1340
1191
 
1341
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1192
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1342
1193
 
1343
1194
  return s0;
1344
1195
  }
@@ -1347,7 +1198,7 @@ function peg$parse(input, options) {
1347
1198
  let s0, s1, s2, s3, s5, s7;
1348
1199
 
1349
1200
  const key = peg$currPos * 38 + 14;
1350
- const cached = peg$resultsCache[key];
1201
+ const cached = peg$resultsCache.get(key);
1351
1202
 
1352
1203
  if (cached) {
1353
1204
  peg$currPos = cached.nextPos;
@@ -1359,30 +1210,7 @@ function peg$parse(input, options) {
1359
1210
  s1 = peg$parseEqExpression();
1360
1211
  if (s1 !== peg$FAILED) {
1361
1212
  s2 = [];
1362
- s3 = peg$currPos;
1363
- peg$parse_();
1364
- if (input.substr(peg$currPos, 2) === peg$c24) {
1365
- s5 = peg$c24;
1366
- peg$currPos += 2;
1367
- } else {
1368
- s5 = peg$FAILED;
1369
- if (peg$silentFails === 0) { peg$fail(peg$e15); }
1370
- }
1371
- if (s5 !== peg$FAILED) {
1372
- peg$parse_();
1373
- s7 = peg$parseEqExpression();
1374
- if (s7 !== peg$FAILED) {
1375
- s3 = s7;
1376
- } else {
1377
- peg$currPos = s3;
1378
- s3 = peg$FAILED;
1379
- }
1380
- } else {
1381
- peg$currPos = s3;
1382
- s3 = peg$FAILED;
1383
- }
1384
- while (s3 !== peg$FAILED) {
1385
- s2.push(s3);
1213
+ for (;;) {
1386
1214
  s3 = peg$currPos;
1387
1215
  peg$parse_();
1388
1216
  if (input.substr(peg$currPos, 2) === peg$c24) {
@@ -1405,6 +1233,8 @@ function peg$parse(input, options) {
1405
1233
  peg$currPos = s3;
1406
1234
  s3 = peg$FAILED;
1407
1235
  }
1236
+ if (s3 === peg$FAILED) { break; }
1237
+ s2.push(s3);
1408
1238
  }
1409
1239
  s0 = peg$f11(s1, s2);
1410
1240
  } else {
@@ -1412,7 +1242,7 @@ function peg$parse(input, options) {
1412
1242
  s0 = peg$FAILED;
1413
1243
  }
1414
1244
 
1415
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1245
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1416
1246
 
1417
1247
  return s0;
1418
1248
  }
@@ -1421,7 +1251,7 @@ function peg$parse(input, options) {
1421
1251
  let s0, s1, s2, s3, s5, s7;
1422
1252
 
1423
1253
  const key = peg$currPos * 38 + 15;
1424
- const cached = peg$resultsCache[key];
1254
+ const cached = peg$resultsCache.get(key);
1425
1255
 
1426
1256
  if (cached) {
1427
1257
  peg$currPos = cached.nextPos;
@@ -1433,24 +1263,7 @@ function peg$parse(input, options) {
1433
1263
  s1 = peg$parseRelExpression();
1434
1264
  if (s1 !== peg$FAILED) {
1435
1265
  s2 = [];
1436
- s3 = peg$currPos;
1437
- peg$parse_();
1438
- s5 = peg$parseEqualityOp();
1439
- if (s5 !== peg$FAILED) {
1440
- peg$parse_();
1441
- s7 = peg$parseRelExpression();
1442
- if (s7 !== peg$FAILED) {
1443
- s3 = peg$f12(s1, s5, s7);
1444
- } else {
1445
- peg$currPos = s3;
1446
- s3 = peg$FAILED;
1447
- }
1448
- } else {
1449
- peg$currPos = s3;
1450
- s3 = peg$FAILED;
1451
- }
1452
- while (s3 !== peg$FAILED) {
1453
- s2.push(s3);
1266
+ for (;;) {
1454
1267
  s3 = peg$currPos;
1455
1268
  peg$parse_();
1456
1269
  s5 = peg$parseEqualityOp();
@@ -1467,6 +1280,8 @@ function peg$parse(input, options) {
1467
1280
  peg$currPos = s3;
1468
1281
  s3 = peg$FAILED;
1469
1282
  }
1283
+ if (s3 === peg$FAILED) { break; }
1284
+ s2.push(s3);
1470
1285
  }
1471
1286
  s0 = peg$f13(s1, s2);
1472
1287
  } else {
@@ -1474,7 +1289,7 @@ function peg$parse(input, options) {
1474
1289
  s0 = peg$FAILED;
1475
1290
  }
1476
1291
 
1477
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1292
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1478
1293
 
1479
1294
  return s0;
1480
1295
  }
@@ -1483,7 +1298,7 @@ function peg$parse(input, options) {
1483
1298
  let s0, s1, s2, s3, s5, s7;
1484
1299
 
1485
1300
  const key = peg$currPos * 38 + 16;
1486
- const cached = peg$resultsCache[key];
1301
+ const cached = peg$resultsCache.get(key);
1487
1302
 
1488
1303
  if (cached) {
1489
1304
  peg$currPos = cached.nextPos;
@@ -1495,24 +1310,7 @@ function peg$parse(input, options) {
1495
1310
  s1 = peg$parseNotExpression();
1496
1311
  if (s1 !== peg$FAILED) {
1497
1312
  s2 = [];
1498
- s3 = peg$currPos;
1499
- peg$parse_();
1500
- s5 = peg$parseRelationalOp();
1501
- if (s5 !== peg$FAILED) {
1502
- peg$parse_();
1503
- s7 = peg$parseNotExpression();
1504
- if (s7 !== peg$FAILED) {
1505
- s3 = peg$f14(s1, s5, s7);
1506
- } else {
1507
- peg$currPos = s3;
1508
- s3 = peg$FAILED;
1509
- }
1510
- } else {
1511
- peg$currPos = s3;
1512
- s3 = peg$FAILED;
1513
- }
1514
- while (s3 !== peg$FAILED) {
1515
- s2.push(s3);
1313
+ for (;;) {
1516
1314
  s3 = peg$currPos;
1517
1315
  peg$parse_();
1518
1316
  s5 = peg$parseRelationalOp();
@@ -1529,6 +1327,8 @@ function peg$parse(input, options) {
1529
1327
  peg$currPos = s3;
1530
1328
  s3 = peg$FAILED;
1531
1329
  }
1330
+ if (s3 === peg$FAILED) { break; }
1331
+ s2.push(s3);
1532
1332
  }
1533
1333
  s0 = peg$f15(s1, s2);
1534
1334
  } else {
@@ -1536,7 +1336,7 @@ function peg$parse(input, options) {
1536
1336
  s0 = peg$FAILED;
1537
1337
  }
1538
1338
 
1539
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1339
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1540
1340
 
1541
1341
  return s0;
1542
1342
  }
@@ -1545,7 +1345,7 @@ function peg$parse(input, options) {
1545
1345
  let s0, s1, s3;
1546
1346
 
1547
1347
  const key = peg$currPos * 38 + 17;
1548
- const cached = peg$resultsCache[key];
1348
+ const cached = peg$resultsCache.get(key);
1549
1349
 
1550
1350
  if (cached) {
1551
1351
  peg$currPos = cached.nextPos;
@@ -1578,7 +1378,7 @@ function peg$parse(input, options) {
1578
1378
  s0 = peg$parseAccess();
1579
1379
  }
1580
1380
 
1581
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1381
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1582
1382
 
1583
1383
  return s0;
1584
1384
  }
@@ -1587,7 +1387,7 @@ function peg$parse(input, options) {
1587
1387
  let s0, s1, s3, s4, s5;
1588
1388
 
1589
1389
  const key = peg$currPos * 38 + 18;
1590
- const cached = peg$resultsCache[key];
1390
+ const cached = peg$resultsCache.get(key);
1591
1391
 
1592
1392
  if (cached) {
1593
1393
  peg$currPos = cached.nextPos;
@@ -1600,29 +1400,20 @@ function peg$parse(input, options) {
1600
1400
  if (s1 !== peg$FAILED) {
1601
1401
  peg$parse_();
1602
1402
  s3 = [];
1603
- s4 = peg$currPos;
1604
- s5 = peg$parseAccessExpression();
1605
- if (s5 !== peg$FAILED) {
1606
- peg$parse_();
1607
- s4 = s5;
1608
- } else {
1609
- peg$currPos = s4;
1610
- s4 = peg$FAILED;
1611
- }
1612
- if (s4 !== peg$FAILED) {
1613
- while (s4 !== peg$FAILED) {
1614
- s3.push(s4);
1615
- s4 = peg$currPos;
1616
- s5 = peg$parseAccessExpression();
1617
- if (s5 !== peg$FAILED) {
1618
- peg$parse_();
1619
- s4 = s5;
1620
- } else {
1621
- peg$currPos = s4;
1622
- s4 = peg$FAILED;
1623
- }
1403
+ for (;;) {
1404
+ s4 = peg$currPos;
1405
+ s5 = peg$parseAccessExpression();
1406
+ if (s5 !== peg$FAILED) {
1407
+ peg$parse_();
1408
+ s4 = s5;
1409
+ } else {
1410
+ peg$currPos = s4;
1411
+ s4 = peg$FAILED;
1624
1412
  }
1625
- } else {
1413
+ if (s4 === peg$FAILED) { break; }
1414
+ s3.push(s4);
1415
+ }
1416
+ if (s3.length < 1) {
1626
1417
  s3 = peg$FAILED;
1627
1418
  }
1628
1419
  if (s3 !== peg$FAILED) {
@@ -1639,7 +1430,7 @@ function peg$parse(input, options) {
1639
1430
  s0 = peg$parseGroupingExpression();
1640
1431
  }
1641
1432
 
1642
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1433
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1643
1434
 
1644
1435
  return s0;
1645
1436
  }
@@ -1648,7 +1439,7 @@ function peg$parse(input, options) {
1648
1439
  let s0;
1649
1440
 
1650
1441
  const key = peg$currPos * 38 + 19;
1651
- const cached = peg$resultsCache[key];
1442
+ const cached = peg$resultsCache.get(key);
1652
1443
 
1653
1444
  if (cached) {
1654
1445
  peg$currPos = cached.nextPos;
@@ -1664,7 +1455,7 @@ function peg$parse(input, options) {
1664
1455
  }
1665
1456
  }
1666
1457
 
1667
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1458
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1668
1459
 
1669
1460
  return s0;
1670
1461
  }
@@ -1673,7 +1464,7 @@ function peg$parse(input, options) {
1673
1464
  let s0, s1, s3;
1674
1465
 
1675
1466
  const key = peg$currPos * 38 + 20;
1676
- const cached = peg$resultsCache[key];
1467
+ const cached = peg$resultsCache.get(key);
1677
1468
 
1678
1469
  if (cached) {
1679
1470
  peg$currPos = cached.nextPos;
@@ -1718,7 +1509,7 @@ function peg$parse(input, options) {
1718
1509
  if (peg$silentFails === 0) { peg$fail(peg$e17); }
1719
1510
  }
1720
1511
 
1721
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1512
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1722
1513
 
1723
1514
  return s0;
1724
1515
  }
@@ -1727,7 +1518,7 @@ function peg$parse(input, options) {
1727
1518
  let s0, s1, s2, s4, s6;
1728
1519
 
1729
1520
  const key = peg$currPos * 38 + 21;
1730
- const cached = peg$resultsCache[key];
1521
+ const cached = peg$resultsCache.get(key);
1731
1522
 
1732
1523
  if (cached) {
1733
1524
  peg$currPos = cached.nextPos;
@@ -1786,7 +1577,7 @@ function peg$parse(input, options) {
1786
1577
  if (peg$silentFails === 0) { peg$fail(peg$e20); }
1787
1578
  }
1788
1579
 
1789
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1580
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1790
1581
 
1791
1582
  return s0;
1792
1583
  }
@@ -1795,7 +1586,7 @@ function peg$parse(input, options) {
1795
1586
  let s0, s1, s2, s4, s5, s6, s7, s9;
1796
1587
 
1797
1588
  const key = peg$currPos * 38 + 22;
1798
- const cached = peg$resultsCache[key];
1589
+ const cached = peg$resultsCache.get(key);
1799
1590
 
1800
1591
  if (cached) {
1801
1592
  peg$currPos = cached.nextPos;
@@ -1837,30 +1628,7 @@ function peg$parse(input, options) {
1837
1628
  s4 = null;
1838
1629
  }
1839
1630
  s5 = [];
1840
- s6 = peg$currPos;
1841
- if (input.charCodeAt(peg$currPos) === 44) {
1842
- s7 = peg$c34;
1843
- peg$currPos++;
1844
- } else {
1845
- s7 = peg$FAILED;
1846
- if (peg$silentFails === 0) { peg$fail(peg$e25); }
1847
- }
1848
- if (s7 !== peg$FAILED) {
1849
- peg$parse_();
1850
- s9 = peg$parseTernaryExpression();
1851
- if (s9 !== peg$FAILED) {
1852
- peg$parse_();
1853
- s6 = s9;
1854
- } else {
1855
- peg$currPos = s6;
1856
- s6 = peg$FAILED;
1857
- }
1858
- } else {
1859
- peg$currPos = s6;
1860
- s6 = peg$FAILED;
1861
- }
1862
- while (s6 !== peg$FAILED) {
1863
- s5.push(s6);
1631
+ for (;;) {
1864
1632
  s6 = peg$currPos;
1865
1633
  if (input.charCodeAt(peg$currPos) === 44) {
1866
1634
  s7 = peg$c34;
@@ -1883,6 +1651,8 @@ function peg$parse(input, options) {
1883
1651
  peg$currPos = s6;
1884
1652
  s6 = peg$FAILED;
1885
1653
  }
1654
+ if (s6 === peg$FAILED) { break; }
1655
+ s5.push(s6);
1886
1656
  }
1887
1657
  if (input.charCodeAt(peg$currPos) === 41) {
1888
1658
  s6 = peg$c36;
@@ -1907,7 +1677,7 @@ function peg$parse(input, options) {
1907
1677
  if (peg$silentFails === 0) { peg$fail(peg$e23); }
1908
1678
  }
1909
1679
 
1910
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1680
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1911
1681
 
1912
1682
  return s0;
1913
1683
  }
@@ -1916,7 +1686,7 @@ function peg$parse(input, options) {
1916
1686
  let s0, s1, s3, s5;
1917
1687
 
1918
1688
  const key = peg$currPos * 38 + 23;
1919
- const cached = peg$resultsCache[key];
1689
+ const cached = peg$resultsCache.get(key);
1920
1690
 
1921
1691
  if (cached) {
1922
1692
  peg$currPos = cached.nextPos;
@@ -1968,7 +1738,7 @@ function peg$parse(input, options) {
1968
1738
  }
1969
1739
  }
1970
1740
 
1971
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1741
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
1972
1742
 
1973
1743
  return s0;
1974
1744
  }
@@ -1977,7 +1747,7 @@ function peg$parse(input, options) {
1977
1747
  let s0, s1, s2, s4, s5, s6, s7, s9;
1978
1748
 
1979
1749
  const key = peg$currPos * 38 + 24;
1980
- const cached = peg$resultsCache[key];
1750
+ const cached = peg$resultsCache.get(key);
1981
1751
 
1982
1752
  if (cached) {
1983
1753
  peg$currPos = cached.nextPos;
@@ -2011,30 +1781,7 @@ function peg$parse(input, options) {
2011
1781
  s4 = null;
2012
1782
  }
2013
1783
  s5 = [];
2014
- s6 = peg$currPos;
2015
- if (input.charCodeAt(peg$currPos) === 44) {
2016
- s7 = peg$c34;
2017
- peg$currPos++;
2018
- } else {
2019
- s7 = peg$FAILED;
2020
- if (peg$silentFails === 0) { peg$fail(peg$e25); }
2021
- }
2022
- if (s7 !== peg$FAILED) {
2023
- peg$parse_();
2024
- s9 = peg$parseTernaryExpression();
2025
- if (s9 !== peg$FAILED) {
2026
- peg$parse_();
2027
- s6 = s9;
2028
- } else {
2029
- peg$currPos = s6;
2030
- s6 = peg$FAILED;
2031
- }
2032
- } else {
2033
- peg$currPos = s6;
2034
- s6 = peg$FAILED;
2035
- }
2036
- while (s6 !== peg$FAILED) {
2037
- s5.push(s6);
1784
+ for (;;) {
2038
1785
  s6 = peg$currPos;
2039
1786
  if (input.charCodeAt(peg$currPos) === 44) {
2040
1787
  s7 = peg$c34;
@@ -2057,6 +1804,8 @@ function peg$parse(input, options) {
2057
1804
  peg$currPos = s6;
2058
1805
  s6 = peg$FAILED;
2059
1806
  }
1807
+ if (s6 === peg$FAILED) { break; }
1808
+ s5.push(s6);
2060
1809
  }
2061
1810
  if (input.charCodeAt(peg$currPos) === 41) {
2062
1811
  s6 = peg$c36;
@@ -2085,7 +1834,7 @@ function peg$parse(input, options) {
2085
1834
  if (peg$silentFails === 0) { peg$fail(peg$e27); }
2086
1835
  }
2087
1836
 
2088
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1837
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
2089
1838
 
2090
1839
  return s0;
2091
1840
  }
@@ -2094,7 +1843,7 @@ function peg$parse(input, options) {
2094
1843
  let s0;
2095
1844
 
2096
1845
  const key = peg$currPos * 38 + 25;
2097
- const cached = peg$resultsCache[key];
1846
+ const cached = peg$resultsCache.get(key);
2098
1847
 
2099
1848
  if (cached) {
2100
1849
  peg$currPos = cached.nextPos;
@@ -2113,7 +1862,7 @@ function peg$parse(input, options) {
2113
1862
  }
2114
1863
  }
2115
1864
 
2116
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
1865
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
2117
1866
 
2118
1867
  return s0;
2119
1868
  }
@@ -2122,7 +1871,7 @@ function peg$parse(input, options) {
2122
1871
  let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
2123
1872
 
2124
1873
  const key = peg$currPos * 38 + 26;
2125
- const cached = peg$resultsCache[key];
1874
+ const cached = peg$resultsCache.get(key);
2126
1875
 
2127
1876
  if (cached) {
2128
1877
  peg$currPos = cached.nextPos;
@@ -2146,25 +1895,18 @@ function peg$parse(input, options) {
2146
1895
  }
2147
1896
  s4 = peg$currPos;
2148
1897
  s5 = [];
2149
- s6 = input.charAt(peg$currPos);
2150
- if (peg$r1.test(s6)) {
2151
- peg$currPos++;
2152
- } else {
2153
- s6 = peg$FAILED;
2154
- if (peg$silentFails === 0) { peg$fail(peg$e30); }
2155
- }
2156
- if (s6 !== peg$FAILED) {
2157
- while (s6 !== peg$FAILED) {
2158
- s5.push(s6);
2159
- s6 = input.charAt(peg$currPos);
2160
- if (peg$r1.test(s6)) {
2161
- peg$currPos++;
2162
- } else {
2163
- s6 = peg$FAILED;
2164
- if (peg$silentFails === 0) { peg$fail(peg$e30); }
2165
- }
1898
+ for (;;) {
1899
+ s6 = input.charAt(peg$currPos);
1900
+ if ((peg$charCode = s6.charCodeAt(0), (peg$charCode >= 48 && peg$charCode <= 57))) {
1901
+ peg$currPos++;
1902
+ } else {
1903
+ s6 = peg$FAILED;
1904
+ if (peg$silentFails === 0) { peg$fail(peg$e30); }
2166
1905
  }
2167
- } else {
1906
+ if (s6 === peg$FAILED) { break; }
1907
+ s5.push(s6);
1908
+ }
1909
+ if (s5.length < 1) {
2168
1910
  s5 = peg$FAILED;
2169
1911
  }
2170
1912
  if (s5 !== peg$FAILED) {
@@ -2178,22 +1920,16 @@ function peg$parse(input, options) {
2178
1920
  }
2179
1921
  if (s7 !== peg$FAILED) {
2180
1922
  s8 = [];
2181
- s9 = input.charAt(peg$currPos);
2182
- if (peg$r1.test(s9)) {
2183
- peg$currPos++;
2184
- } else {
2185
- s9 = peg$FAILED;
2186
- if (peg$silentFails === 0) { peg$fail(peg$e30); }
2187
- }
2188
- while (s9 !== peg$FAILED) {
2189
- s8.push(s9);
1923
+ for (;;) {
2190
1924
  s9 = input.charAt(peg$currPos);
2191
- if (peg$r1.test(s9)) {
1925
+ if ((peg$charCode = s9.charCodeAt(0), (peg$charCode >= 48 && peg$charCode <= 57))) {
2192
1926
  peg$currPos++;
2193
1927
  } else {
2194
1928
  s9 = peg$FAILED;
2195
1929
  if (peg$silentFails === 0) { peg$fail(peg$e30); }
2196
1930
  }
1931
+ if (s9 === peg$FAILED) { break; }
1932
+ s8.push(s9);
2197
1933
  }
2198
1934
  s7 = [s7, s8];
2199
1935
  s6 = s7;
@@ -2221,25 +1957,18 @@ function peg$parse(input, options) {
2221
1957
  }
2222
1958
  if (s5 !== peg$FAILED) {
2223
1959
  s6 = [];
2224
- s7 = input.charAt(peg$currPos);
2225
- if (peg$r1.test(s7)) {
2226
- peg$currPos++;
2227
- } else {
2228
- s7 = peg$FAILED;
2229
- if (peg$silentFails === 0) { peg$fail(peg$e30); }
2230
- }
2231
- if (s7 !== peg$FAILED) {
2232
- while (s7 !== peg$FAILED) {
2233
- s6.push(s7);
2234
- s7 = input.charAt(peg$currPos);
2235
- if (peg$r1.test(s7)) {
2236
- peg$currPos++;
2237
- } else {
2238
- s7 = peg$FAILED;
2239
- if (peg$silentFails === 0) { peg$fail(peg$e30); }
2240
- }
1960
+ for (;;) {
1961
+ s7 = input.charAt(peg$currPos);
1962
+ if ((peg$charCode = s7.charCodeAt(0), (peg$charCode >= 48 && peg$charCode <= 57))) {
1963
+ peg$currPos++;
1964
+ } else {
1965
+ s7 = peg$FAILED;
1966
+ if (peg$silentFails === 0) { peg$fail(peg$e30); }
2241
1967
  }
2242
- } else {
1968
+ if (s7 === peg$FAILED) { break; }
1969
+ s6.push(s7);
1970
+ }
1971
+ if (s6.length < 1) {
2243
1972
  s6 = peg$FAILED;
2244
1973
  }
2245
1974
  if (s6 !== peg$FAILED) {
@@ -2276,7 +2005,7 @@ function peg$parse(input, options) {
2276
2005
  if (peg$silentFails === 0) { peg$fail(peg$e28); }
2277
2006
  }
2278
2007
 
2279
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2008
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
2280
2009
 
2281
2010
  return s0;
2282
2011
  }
@@ -2285,7 +2014,7 @@ function peg$parse(input, options) {
2285
2014
  let s0, s1, s2, s3, s4;
2286
2015
 
2287
2016
  const key = peg$currPos * 38 + 27;
2288
- const cached = peg$resultsCache[key];
2017
+ const cached = peg$resultsCache.get(key);
2289
2018
 
2290
2019
  if (cached) {
2291
2020
  peg$currPos = cached.nextPos;
@@ -2305,22 +2034,16 @@ function peg$parse(input, options) {
2305
2034
  if (s1 !== peg$FAILED) {
2306
2035
  s2 = peg$currPos;
2307
2036
  s3 = [];
2308
- s4 = input.charAt(peg$currPos);
2309
- if (peg$r2.test(s4)) {
2310
- peg$currPos++;
2311
- } else {
2312
- s4 = peg$FAILED;
2313
- if (peg$silentFails === 0) { peg$fail(peg$e33); }
2314
- }
2315
- while (s4 !== peg$FAILED) {
2316
- s3.push(s4);
2037
+ for (;;) {
2317
2038
  s4 = input.charAt(peg$currPos);
2318
- if (peg$r2.test(s4)) {
2039
+ if (s4 !== "" && !(peg$charCode = s4.charCodeAt(0), peg$charCode === 39)) {
2319
2040
  peg$currPos++;
2320
2041
  } else {
2321
2042
  s4 = peg$FAILED;
2322
2043
  if (peg$silentFails === 0) { peg$fail(peg$e33); }
2323
2044
  }
2045
+ if (s4 === peg$FAILED) { break; }
2046
+ s3.push(s4);
2324
2047
  }
2325
2048
  s2 = input.substring(s2, peg$currPos);
2326
2049
  if (input.charCodeAt(peg$currPos) === 39) {
@@ -2352,22 +2075,16 @@ function peg$parse(input, options) {
2352
2075
  if (s1 !== peg$FAILED) {
2353
2076
  s2 = peg$currPos;
2354
2077
  s3 = [];
2355
- s4 = input.charAt(peg$currPos);
2356
- if (peg$r3.test(s4)) {
2357
- peg$currPos++;
2358
- } else {
2359
- s4 = peg$FAILED;
2360
- if (peg$silentFails === 0) { peg$fail(peg$e35); }
2361
- }
2362
- while (s4 !== peg$FAILED) {
2363
- s3.push(s4);
2078
+ for (;;) {
2364
2079
  s4 = input.charAt(peg$currPos);
2365
- if (peg$r3.test(s4)) {
2080
+ if (s4 !== "" && !(peg$charCode = s4.charCodeAt(0), peg$charCode === 34)) {
2366
2081
  peg$currPos++;
2367
2082
  } else {
2368
2083
  s4 = peg$FAILED;
2369
2084
  if (peg$silentFails === 0) { peg$fail(peg$e35); }
2370
2085
  }
2086
+ if (s4 === peg$FAILED) { break; }
2087
+ s3.push(s4);
2371
2088
  }
2372
2089
  s2 = input.substring(s2, peg$currPos);
2373
2090
  if (input.charCodeAt(peg$currPos) === 34) {
@@ -2398,16 +2115,13 @@ function peg$parse(input, options) {
2398
2115
  }
2399
2116
  if (s1 !== peg$FAILED) {
2400
2117
  s2 = [];
2401
- s3 = peg$parseTemplateStringExpression();
2402
- if (s3 === peg$FAILED) {
2403
- s3 = peg$parseTemplateStringLiteral();
2404
- }
2405
- while (s3 !== peg$FAILED) {
2406
- s2.push(s3);
2118
+ for (;;) {
2407
2119
  s3 = peg$parseTemplateStringExpression();
2408
2120
  if (s3 === peg$FAILED) {
2409
2121
  s3 = peg$parseTemplateStringLiteral();
2410
2122
  }
2123
+ if (s3 === peg$FAILED) { break; }
2124
+ s2.push(s3);
2411
2125
  }
2412
2126
  if (input.charCodeAt(peg$currPos) === 96) {
2413
2127
  s3 = peg$c42;
@@ -2434,7 +2148,7 @@ function peg$parse(input, options) {
2434
2148
  if (peg$silentFails === 0) { peg$fail(peg$e31); }
2435
2149
  }
2436
2150
 
2437
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2151
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
2438
2152
 
2439
2153
  return s0;
2440
2154
  }
@@ -2443,7 +2157,7 @@ function peg$parse(input, options) {
2443
2157
  let s0, s1, s3, s5;
2444
2158
 
2445
2159
  const key = peg$currPos * 38 + 28;
2446
- const cached = peg$resultsCache[key];
2160
+ const cached = peg$resultsCache.get(key);
2447
2161
 
2448
2162
  if (cached) {
2449
2163
  peg$currPos = cached.nextPos;
@@ -2492,7 +2206,7 @@ function peg$parse(input, options) {
2492
2206
  if (peg$silentFails === 0) { peg$fail(peg$e37); }
2493
2207
  }
2494
2208
 
2495
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2209
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
2496
2210
 
2497
2211
  return s0;
2498
2212
  }
@@ -2501,7 +2215,7 @@ function peg$parse(input, options) {
2501
2215
  let s0, s1, s2, s3, s4;
2502
2216
 
2503
2217
  const key = peg$currPos * 38 + 29;
2504
- const cached = peg$resultsCache[key];
2218
+ const cached = peg$resultsCache.get(key);
2505
2219
 
2506
2220
  if (cached) {
2507
2221
  peg$currPos = cached.nextPos;
@@ -2512,123 +2226,60 @@ function peg$parse(input, options) {
2512
2226
  peg$silentFails++;
2513
2227
  s0 = peg$currPos;
2514
2228
  s1 = [];
2515
- s2 = peg$currPos;
2516
- s3 = [];
2517
- s4 = input.charAt(peg$currPos);
2518
- if (peg$r4.test(s4)) {
2519
- peg$currPos++;
2520
- } else {
2521
- s4 = peg$FAILED;
2522
- if (peg$silentFails === 0) { peg$fail(peg$e40); }
2523
- }
2524
- if (s4 !== peg$FAILED) {
2525
- while (s4 !== peg$FAILED) {
2526
- s3.push(s4);
2527
- s4 = input.charAt(peg$currPos);
2528
- if (peg$r4.test(s4)) {
2529
- peg$currPos++;
2530
- } else {
2531
- s4 = peg$FAILED;
2532
- if (peg$silentFails === 0) { peg$fail(peg$e40); }
2533
- }
2534
- }
2535
- } else {
2536
- s3 = peg$FAILED;
2537
- }
2538
- if (s3 !== peg$FAILED) {
2539
- s2 = input.substring(s2, peg$currPos);
2540
- } else {
2541
- s2 = s3;
2542
- }
2543
- if (s2 === peg$FAILED) {
2229
+ for (;;) {
2544
2230
  s2 = peg$currPos;
2545
- if (input.charCodeAt(peg$currPos) === 92) {
2546
- s3 = peg$c7;
2547
- peg$currPos++;
2548
- } else {
2549
- s3 = peg$FAILED;
2550
- if (peg$silentFails === 0) { peg$fail(peg$e7); }
2551
- }
2552
- if (s3 !== peg$FAILED) {
2553
- if (input.length > peg$currPos) {
2554
- s4 = input.charAt(peg$currPos);
2231
+ s3 = [];
2232
+ for (;;) {
2233
+ s4 = input.charAt(peg$currPos);
2234
+ if (s4 !== "" && !(peg$charCode = s4.charCodeAt(0), peg$charCode === 92 || peg$charCode === 96 || peg$charCode === 123)) {
2555
2235
  peg$currPos++;
2556
2236
  } else {
2557
2237
  s4 = peg$FAILED;
2558
- if (peg$silentFails === 0) { peg$fail(peg$e8); }
2559
- }
2560
- if (s4 !== peg$FAILED) {
2561
- s2 = peg$f28(s4);
2562
- } else {
2563
- peg$currPos = s2;
2564
- s2 = peg$FAILED;
2238
+ if (peg$silentFails === 0) { peg$fail(peg$e40); }
2565
2239
  }
2240
+ if (s4 === peg$FAILED) { break; }
2241
+ s3.push(s4);
2242
+ }
2243
+ if (s3.length < 1) {
2244
+ s3 = peg$FAILED;
2245
+ }
2246
+ if (s3 !== peg$FAILED) {
2247
+ s2 = input.substring(s2, peg$currPos);
2566
2248
  } else {
2567
- peg$currPos = s2;
2568
- s2 = peg$FAILED;
2249
+ s2 = s3;
2569
2250
  }
2570
- }
2571
- if (s2 !== peg$FAILED) {
2572
- while (s2 !== peg$FAILED) {
2573
- s1.push(s2);
2251
+ if (s2 === peg$FAILED) {
2574
2252
  s2 = peg$currPos;
2575
- s3 = [];
2576
- s4 = input.charAt(peg$currPos);
2577
- if (peg$r4.test(s4)) {
2253
+ if (input.charCodeAt(peg$currPos) === 92) {
2254
+ s3 = peg$c7;
2578
2255
  peg$currPos++;
2579
- } else {
2580
- s4 = peg$FAILED;
2581
- if (peg$silentFails === 0) { peg$fail(peg$e40); }
2582
- }
2583
- if (s4 !== peg$FAILED) {
2584
- while (s4 !== peg$FAILED) {
2585
- s3.push(s4);
2586
- s4 = input.charAt(peg$currPos);
2587
- if (peg$r4.test(s4)) {
2588
- peg$currPos++;
2589
- } else {
2590
- s4 = peg$FAILED;
2591
- if (peg$silentFails === 0) { peg$fail(peg$e40); }
2592
- }
2593
- }
2594
2256
  } else {
2595
2257
  s3 = peg$FAILED;
2258
+ if (peg$silentFails === 0) { peg$fail(peg$e7); }
2596
2259
  }
2597
2260
  if (s3 !== peg$FAILED) {
2598
- s2 = input.substring(s2, peg$currPos);
2599
- } else {
2600
- s2 = s3;
2601
- }
2602
- if (s2 === peg$FAILED) {
2603
- s2 = peg$currPos;
2604
- if (input.charCodeAt(peg$currPos) === 92) {
2605
- s3 = peg$c7;
2261
+ if (input.length > peg$currPos) {
2262
+ s4 = input.charAt(peg$currPos);
2606
2263
  peg$currPos++;
2607
2264
  } else {
2608
- s3 = peg$FAILED;
2609
- if (peg$silentFails === 0) { peg$fail(peg$e7); }
2265
+ s4 = peg$FAILED;
2266
+ if (peg$silentFails === 0) { peg$fail(peg$e8); }
2610
2267
  }
2611
- if (s3 !== peg$FAILED) {
2612
- if (input.length > peg$currPos) {
2613
- s4 = input.charAt(peg$currPos);
2614
- peg$currPos++;
2615
- } else {
2616
- s4 = peg$FAILED;
2617
- if (peg$silentFails === 0) { peg$fail(peg$e8); }
2618
- }
2619
- if (s4 !== peg$FAILED) {
2620
- s2 = peg$f28(s4);
2621
- } else {
2622
- peg$currPos = s2;
2623
- s2 = peg$FAILED;
2624
- }
2268
+ if (s4 !== peg$FAILED) {
2269
+ s2 = peg$f28(s4);
2625
2270
  } else {
2626
2271
  peg$currPos = s2;
2627
2272
  s2 = peg$FAILED;
2628
2273
  }
2274
+ } else {
2275
+ peg$currPos = s2;
2276
+ s2 = peg$FAILED;
2629
2277
  }
2630
2278
  }
2631
- } else {
2279
+ if (s2 === peg$FAILED) { break; }
2280
+ s1.push(s2);
2281
+ }
2282
+ if (s1.length < 1) {
2632
2283
  s1 = peg$FAILED;
2633
2284
  }
2634
2285
  if (s1 !== peg$FAILED) {
@@ -2655,7 +2306,7 @@ function peg$parse(input, options) {
2655
2306
  if (peg$silentFails === 0) { peg$fail(peg$e39); }
2656
2307
  }
2657
2308
 
2658
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2309
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
2659
2310
 
2660
2311
  return s0;
2661
2312
  }
@@ -2664,7 +2315,7 @@ function peg$parse(input, options) {
2664
2315
  let s0, s1, s3, s5, s6, s7, s9;
2665
2316
 
2666
2317
  const key = peg$currPos * 38 + 30;
2667
- const cached = peg$resultsCache[key];
2318
+ const cached = peg$resultsCache.get(key);
2668
2319
 
2669
2320
  if (cached) {
2670
2321
  peg$currPos = cached.nextPos;
@@ -2689,30 +2340,7 @@ function peg$parse(input, options) {
2689
2340
  }
2690
2341
  peg$parse_();
2691
2342
  s5 = [];
2692
- s6 = peg$currPos;
2693
- if (input.charCodeAt(peg$currPos) === 44) {
2694
- s7 = peg$c34;
2695
- peg$currPos++;
2696
- } else {
2697
- s7 = peg$FAILED;
2698
- if (peg$silentFails === 0) { peg$fail(peg$e25); }
2699
- }
2700
- if (s7 !== peg$FAILED) {
2701
- peg$parse_();
2702
- s9 = peg$parseTernaryExpression();
2703
- if (s9 !== peg$FAILED) {
2704
- peg$parse_();
2705
- s6 = s9;
2706
- } else {
2707
- peg$currPos = s6;
2708
- s6 = peg$FAILED;
2709
- }
2710
- } else {
2711
- peg$currPos = s6;
2712
- s6 = peg$FAILED;
2713
- }
2714
- while (s6 !== peg$FAILED) {
2715
- s5.push(s6);
2343
+ for (;;) {
2716
2344
  s6 = peg$currPos;
2717
2345
  if (input.charCodeAt(peg$currPos) === 44) {
2718
2346
  s7 = peg$c34;
@@ -2735,6 +2363,8 @@ function peg$parse(input, options) {
2735
2363
  peg$currPos = s6;
2736
2364
  s6 = peg$FAILED;
2737
2365
  }
2366
+ if (s6 === peg$FAILED) { break; }
2367
+ s5.push(s6);
2738
2368
  }
2739
2369
  if (input.charCodeAt(peg$currPos) === 93) {
2740
2370
  s6 = peg$c31;
@@ -2759,7 +2389,7 @@ function peg$parse(input, options) {
2759
2389
  if (peg$silentFails === 0) { peg$fail(peg$e41); }
2760
2390
  }
2761
2391
 
2762
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2392
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
2763
2393
 
2764
2394
  return s0;
2765
2395
  }
@@ -2768,7 +2398,7 @@ function peg$parse(input, options) {
2768
2398
  let s0, s1, s3, s4, s5, s6, s7, s8, s9, s11, s13;
2769
2399
 
2770
2400
  const key = peg$currPos * 38 + 31;
2771
- const cached = peg$resultsCache[key];
2401
+ const cached = peg$resultsCache.get(key);
2772
2402
 
2773
2403
  if (cached) {
2774
2404
  peg$currPos = cached.nextPos;
@@ -2820,50 +2450,7 @@ function peg$parse(input, options) {
2820
2450
  }
2821
2451
  s4 = peg$parse_();
2822
2452
  s5 = [];
2823
- s6 = peg$currPos;
2824
- if (input.charCodeAt(peg$currPos) === 44) {
2825
- s7 = peg$c34;
2826
- peg$currPos++;
2827
- } else {
2828
- s7 = peg$FAILED;
2829
- if (peg$silentFails === 0) { peg$fail(peg$e25); }
2830
- }
2831
- if (s7 !== peg$FAILED) {
2832
- s8 = peg$parse_();
2833
- s9 = peg$parseStringLiteral();
2834
- if (s9 !== peg$FAILED) {
2835
- peg$parse_();
2836
- if (input.charCodeAt(peg$currPos) === 58) {
2837
- s11 = peg$c15;
2838
- peg$currPos++;
2839
- } else {
2840
- s11 = peg$FAILED;
2841
- if (peg$silentFails === 0) { peg$fail(peg$e11); }
2842
- }
2843
- if (s11 !== peg$FAILED) {
2844
- peg$parse_();
2845
- s13 = peg$parseTernaryExpression();
2846
- if (s13 !== peg$FAILED) {
2847
- peg$parse_();
2848
- s6 = [ s9, s13 ];
2849
- } else {
2850
- peg$currPos = s6;
2851
- s6 = peg$FAILED;
2852
- }
2853
- } else {
2854
- peg$currPos = s6;
2855
- s6 = peg$FAILED;
2856
- }
2857
- } else {
2858
- peg$currPos = s6;
2859
- s6 = peg$FAILED;
2860
- }
2861
- } else {
2862
- peg$currPos = s6;
2863
- s6 = peg$FAILED;
2864
- }
2865
- while (s6 !== peg$FAILED) {
2866
- s5.push(s6);
2453
+ for (;;) {
2867
2454
  s6 = peg$currPos;
2868
2455
  if (input.charCodeAt(peg$currPos) === 44) {
2869
2456
  s7 = peg$c34;
@@ -2906,6 +2493,8 @@ function peg$parse(input, options) {
2906
2493
  peg$currPos = s6;
2907
2494
  s6 = peg$FAILED;
2908
2495
  }
2496
+ if (s6 === peg$FAILED) { break; }
2497
+ s5.push(s6);
2909
2498
  }
2910
2499
  if (input.charCodeAt(peg$currPos) === 125) {
2911
2500
  s6 = peg$c43;
@@ -2930,7 +2519,7 @@ function peg$parse(input, options) {
2930
2519
  if (peg$silentFails === 0) { peg$fail(peg$e42); }
2931
2520
  }
2932
2521
 
2933
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2522
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
2934
2523
 
2935
2524
  return s0;
2936
2525
  }
@@ -2939,7 +2528,7 @@ function peg$parse(input, options) {
2939
2528
  let s0, s1, s2, s3, s4;
2940
2529
 
2941
2530
  const key = peg$currPos * 38 + 32;
2942
- const cached = peg$resultsCache[key];
2531
+ const cached = peg$resultsCache.get(key);
2943
2532
 
2944
2533
  if (cached) {
2945
2534
  peg$currPos = cached.nextPos;
@@ -2997,7 +2586,7 @@ function peg$parse(input, options) {
2997
2586
  if (peg$silentFails === 0) { peg$fail(peg$e43); }
2998
2587
  }
2999
2588
 
3000
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2589
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
3001
2590
 
3002
2591
  return s0;
3003
2592
  }
@@ -3006,7 +2595,7 @@ function peg$parse(input, options) {
3006
2595
  let s0, s1, s2, s3, s4;
3007
2596
 
3008
2597
  const key = peg$currPos * 38 + 33;
3009
- const cached = peg$resultsCache[key];
2598
+ const cached = peg$resultsCache.get(key);
3010
2599
 
3011
2600
  if (cached) {
3012
2601
  peg$currPos = cached.nextPos;
@@ -3017,7 +2606,7 @@ function peg$parse(input, options) {
3017
2606
  s0 = peg$currPos;
3018
2607
  s1 = peg$currPos;
3019
2608
  s2 = input.charAt(peg$currPos);
3020
- if (peg$r5.test(s2)) {
2609
+ if ((peg$charCode = s2.charCodeAt(0), (peg$charCode >= 97 && peg$charCode <= 122) || (peg$charCode >= 65 && peg$charCode <= 90) || peg$charCode === 36 || peg$charCode === 95)) {
3021
2610
  peg$currPos++;
3022
2611
  } else {
3023
2612
  s2 = peg$FAILED;
@@ -3025,22 +2614,16 @@ function peg$parse(input, options) {
3025
2614
  }
3026
2615
  if (s2 !== peg$FAILED) {
3027
2616
  s3 = [];
3028
- s4 = input.charAt(peg$currPos);
3029
- if (peg$r6.test(s4)) {
3030
- peg$currPos++;
3031
- } else {
3032
- s4 = peg$FAILED;
3033
- if (peg$silentFails === 0) { peg$fail(peg$e46); }
3034
- }
3035
- while (s4 !== peg$FAILED) {
3036
- s3.push(s4);
2617
+ for (;;) {
3037
2618
  s4 = input.charAt(peg$currPos);
3038
- if (peg$r6.test(s4)) {
2619
+ if ((peg$charCode = s4.charCodeAt(0), (peg$charCode >= 97 && peg$charCode <= 122) || (peg$charCode >= 65 && peg$charCode <= 90) || peg$charCode === 36 || peg$charCode === 95 || (peg$charCode >= 48 && peg$charCode <= 57))) {
3039
2620
  peg$currPos++;
3040
2621
  } else {
3041
2622
  s4 = peg$FAILED;
3042
2623
  if (peg$silentFails === 0) { peg$fail(peg$e46); }
3043
2624
  }
2625
+ if (s4 === peg$FAILED) { break; }
2626
+ s3.push(s4);
3044
2627
  }
3045
2628
  s2 = [s2, s3];
3046
2629
  s1 = s2;
@@ -3054,7 +2637,7 @@ function peg$parse(input, options) {
3054
2637
  s0 = s1;
3055
2638
  }
3056
2639
 
3057
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2640
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
3058
2641
 
3059
2642
  return s0;
3060
2643
  }
@@ -3063,7 +2646,7 @@ function peg$parse(input, options) {
3063
2646
  let s0, s1;
3064
2647
 
3065
2648
  const key = peg$currPos * 38 + 34;
3066
- const cached = peg$resultsCache[key];
2649
+ const cached = peg$resultsCache.get(key);
3067
2650
 
3068
2651
  if (cached) {
3069
2652
  peg$currPos = cached.nextPos;
@@ -3084,7 +2667,7 @@ function peg$parse(input, options) {
3084
2667
  if (peg$silentFails === 0) { peg$fail(peg$e47); }
3085
2668
  }
3086
2669
 
3087
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2670
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
3088
2671
 
3089
2672
  return s0;
3090
2673
  }
@@ -3093,7 +2676,7 @@ function peg$parse(input, options) {
3093
2676
  let s0;
3094
2677
 
3095
2678
  const key = peg$currPos * 38 + 35;
3096
- const cached = peg$resultsCache[key];
2679
+ const cached = peg$resultsCache.get(key);
3097
2680
 
3098
2681
  if (cached) {
3099
2682
  peg$currPos = cached.nextPos;
@@ -3118,7 +2701,7 @@ function peg$parse(input, options) {
3118
2701
  }
3119
2702
  }
3120
2703
 
3121
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2704
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
3122
2705
 
3123
2706
  return s0;
3124
2707
  }
@@ -3127,7 +2710,7 @@ function peg$parse(input, options) {
3127
2710
  let s0;
3128
2711
 
3129
2712
  const key = peg$currPos * 38 + 36;
3130
- const cached = peg$resultsCache[key];
2713
+ const cached = peg$resultsCache.get(key);
3131
2714
 
3132
2715
  if (cached) {
3133
2716
  peg$currPos = cached.nextPos;
@@ -3170,7 +2753,7 @@ function peg$parse(input, options) {
3170
2753
  }
3171
2754
  }
3172
2755
 
3173
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2756
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
3174
2757
 
3175
2758
  return s0;
3176
2759
  }
@@ -3179,7 +2762,7 @@ function peg$parse(input, options) {
3179
2762
  let s0, s1;
3180
2763
 
3181
2764
  const key = peg$currPos * 38 + 37;
3182
- const cached = peg$resultsCache[key];
2765
+ const cached = peg$resultsCache.get(key);
3183
2766
 
3184
2767
  if (cached) {
3185
2768
  peg$currPos = cached.nextPos;
@@ -3189,26 +2772,20 @@ function peg$parse(input, options) {
3189
2772
 
3190
2773
  peg$silentFails++;
3191
2774
  s0 = [];
3192
- s1 = input.charAt(peg$currPos);
3193
- if (peg$r7.test(s1)) {
3194
- peg$currPos++;
3195
- } else {
3196
- s1 = peg$FAILED;
3197
- if (peg$silentFails === 0) { peg$fail(peg$e54); }
3198
- }
3199
- while (s1 !== peg$FAILED) {
3200
- s0.push(s1);
2775
+ for (;;) {
3201
2776
  s1 = input.charAt(peg$currPos);
3202
- if (peg$r7.test(s1)) {
2777
+ if ((peg$charCode = s1.charCodeAt(0), peg$charCode === 32 || peg$charCode === 9 || peg$charCode === 10 || peg$charCode === 13)) {
3203
2778
  peg$currPos++;
3204
2779
  } else {
3205
2780
  s1 = peg$FAILED;
3206
2781
  if (peg$silentFails === 0) { peg$fail(peg$e54); }
3207
2782
  }
2783
+ if (s1 === peg$FAILED) { break; }
2784
+ s0.push(s1);
3208
2785
  }
3209
2786
  peg$silentFails--;
3210
2787
 
3211
- peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
2788
+ peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
3212
2789
 
3213
2790
  return s0;
3214
2791
  }
@@ -3250,10 +2827,10 @@ function peg$parse(input, options) {
3250
2827
  /**
3251
2828
  * The one lookup against a data stack: the innermost overlay carrying the name
3252
2829
  * wins, `self` is the innermost overlay itself, and a function overlay counts
3253
- * like an object one. Every lookup that resolves a name goes through this, so the
3254
- * imperative facades cannot drift from what a template sees.
2830
+ * like an object one.
3255
2831
  * @param {any[]} dataStack
3256
2832
  * @param {string|symbol} prop
2833
+ * @returns {any} undefined when no overlay carries the name
3257
2834
  */
3258
2835
  const resolveInStack = (dataStack, prop) => {
3259
2836
  if (prop === 'self') {
@@ -3317,8 +2894,7 @@ class EvaluatingVisitor {
3317
2894
  return this.visit(node.lhs) || this.visit(node.rhs);
3318
2895
  }
3319
2896
  [nodes.nullc](node) {
3320
- const lhs = this.visit(node.lhs);
3321
- return lhs !== null && lhs !== undefined ? lhs : this.visit(node.rhs);
2897
+ return this.visit(node.lhs) ?? this.visit(node.rhs);
3322
2898
  }
3323
2899
  [nodes.not](node) {
3324
2900
  return !this.visit(node.expr);
@@ -3385,8 +2961,7 @@ class EvaluatingVisitor {
3385
2961
  return this.visit(node.cond) ? this.visit(node.ifTrue) : this.visit(node.ifFalse);
3386
2962
  }
3387
2963
  [nodes.elv](node) {
3388
- const cond = this.visit(node.cond);
3389
- return cond ? cond : this.visit(node.ifFalse);
2964
+ return this.visit(node.cond) || this.visit(node.ifFalse);
3390
2965
  }
3391
2966
  [nodes.access](node) {
3392
2967
  let prev;
@@ -3452,16 +3027,23 @@ class EvaluatingVisitor {
3452
3027
  * spliced into the expression text.
3453
3028
  */
3454
3029
  class Expressions {
3030
+ /** Parses the whole text as one expression: `user.name`. */
3455
3031
  static MODE_EXPRESSION = Symbol('MODE_EXPRESSION');
3032
+ /**
3033
+ * Parses text with interpolations, `Hello {{ user.name }}`, evaluating to
3034
+ * its parts: `{ type, value }` for each literal run and each `{{ }}` (text),
3035
+ * `{{{ }}}` (html) or `{{{{ }}}}` (node) interpolation, in order.
3036
+ */
3456
3037
  static MODE_TEMPLATED = Symbol('MODE_TEMPLATED');
3457
3038
 
3458
3039
  static #astCache = new BoundedCache(1000);
3459
3040
 
3460
3041
  /**
3461
- * Parses an expression.
3042
+ * Parses an expression, caching the ast by mode and text.
3462
3043
  * @param {string} expression
3463
- * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
3464
- * @returns the ast
3044
+ * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] MODE_EXPRESSION when omitted
3045
+ * @returns {any} the ast
3046
+ * @throws a peggy syntax error when the text does not parse
3465
3047
  */
3466
3048
  static parse(expression, mode) {
3467
3049
  const key = mode?.toString() + expression;
@@ -3472,12 +3054,12 @@ class Expressions {
3472
3054
  );
3473
3055
  }
3474
3056
  /**
3475
- * Evaluates an expression.
3057
+ * Evaluates a parsed ast against the modules and the data stack.
3476
3058
  * @param {{[k: string]: any } | null | undefined } modules
3477
- * @param {any[]} dataStack
3478
- * @param {any} ast
3479
- * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
3480
- * @returns the result
3059
+ * @param {any[]} dataStack innermost overlay last
3060
+ * @param {any} ast what `parse` answered
3061
+ * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] the mode it was parsed in
3062
+ * @returns {any} the value, or the list of parts in MODE_TEMPLATED
3481
3063
  */
3482
3064
  static evaluate(modules, dataStack, ast, mode) {
3483
3065
  return new EvaluatingVisitor(modules, dataStack).visitRoot(ast, mode === Expressions.MODE_TEMPLATED);
@@ -3485,10 +3067,10 @@ class Expressions {
3485
3067
  /**
3486
3068
  * Parses and evaluates an expression.
3487
3069
  * @param {{ [x: string]: any; } | null | undefined} modules
3488
- * @param {any[]} dataStack
3070
+ * @param {any[]} dataStack innermost overlay last
3489
3071
  * @param {string} expression
3490
- * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
3491
- * @returns the result
3072
+ * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] MODE_EXPRESSION when omitted
3073
+ * @returns {any} the value, or the list of parts in MODE_TEMPLATED
3492
3074
  */
3493
3075
  static interpret(modules, dataStack, expression, mode) {
3494
3076
  return Expressions.evaluate(modules, dataStack, Expressions.parse(expression, mode), mode);
@@ -3504,14 +3086,31 @@ class Expressions {
3504
3086
  class ExpressionEvaluator {
3505
3087
  #modules;
3506
3088
  #dataStack;
3089
+ /**
3090
+ * @param {{ [k: string]: any } | null | undefined} modules
3091
+ * @param {any[]} dataStack innermost overlay last
3092
+ */
3507
3093
  constructor(modules, dataStack) {
3508
3094
  this.#modules = modules;
3509
3095
  this.#dataStack = dataStack;
3510
3096
  }
3097
+ /**
3098
+ * A new evaluator with one more module: `#name:fn()` resolves into it, and an
3099
+ * empty name merges a whole map whose functions resolve bare, as `#fn()`.
3100
+ * @param {string} name
3101
+ * @param {object} value
3102
+ * @returns {ExpressionEvaluator}
3103
+ */
3511
3104
  withModule(name, value) {
3512
3105
  const module = name ? { [name]: value } : value;
3513
3106
  return new ExpressionEvaluator({ ...this.#modules, ...module }, this.#dataStack);
3514
3107
  }
3108
+ /**
3109
+ * A new evaluator with the data pushed as the innermost overlays, the last
3110
+ * one winning a shared name.
3111
+ * @param {...any} data
3112
+ * @returns {ExpressionEvaluator}
3113
+ */
3515
3114
  withOverlay(...data) {
3516
3115
  return new ExpressionEvaluator(
3517
3116
  this.#modules,
@@ -3522,26 +3121,36 @@ class ExpressionEvaluator {
3522
3121
  * Resolves a name against the data stack, with no parse round trip: the
3523
3122
  * lookup a template's bare identifier makes, for an imperative caller.
3524
3123
  * @param {string} name
3124
+ * @returns {any} undefined when no overlay carries the name
3525
3125
  */
3526
3126
  resolve(name) {
3527
3127
  return resolveInStack(this.#dataStack, name);
3528
3128
  }
3529
- /** Evaluates an expression against this scope. */
3129
+ /**
3130
+ * Evaluates an expression against this scope.
3131
+ * @param {string} expression
3132
+ * @returns {any}
3133
+ */
3530
3134
  evaluateExpression(expression) {
3531
3135
  return Expressions.interpret(this.#modules, this.#dataStack, expression, Expressions.MODE_EXPRESSION);
3532
3136
  }
3533
- /** Evaluates a templated text, the `{{ }}` form, against this scope. */
3137
+ /**
3138
+ * Evaluates a templated text, the `{{ }}` form, against this scope.
3139
+ * @param {string} text
3140
+ * @returns {{ type: symbol, value: any }[]} its parts, as MODE_TEMPLATED describes
3141
+ */
3534
3142
  evaluateTemplated(text) {
3535
3143
  return Expressions.interpret(this.#modules, this.#dataStack, text, Expressions.MODE_TEMPLATED);
3536
3144
  }
3537
3145
  }
3538
3146
 
3539
- /** Creates and inspects the DocumentFragments a Template renders into. */
3147
+ /** Creates, serializes and inspects DocumentFragments. */
3540
3148
  class Fragments {
3541
3149
  /**
3542
- * Creates a DocumentFragment from an string.
3543
- * @param {...string} html
3544
- * @returns {DocumentFragment} the fragment
3150
+ * Parses html into a DocumentFragment adopted by the document, each piece
3151
+ * trimmed and the pieces joined.
3152
+ * @param {...string} html
3153
+ * @returns {DocumentFragment}
3545
3154
  */
3546
3155
  static fromHtml(...html) {
3547
3156
  const el = document.createElement('template');
@@ -3549,11 +3158,11 @@ class Fragments {
3549
3158
  return document.adoptNode(el.content);
3550
3159
  }
3551
3160
  /**
3552
- * Creates a string representation (HTML) of a DocumentFragment, consuming it:
3553
- * the nodes are moved out, not copied, and the fragment is left empty. Pass
3161
+ * Serializes a fragment, or a single node, to html, consuming it: the nodes
3162
+ * are moved out, not copied, and a fragment is left empty. Pass
3554
3163
  * `fragment.cloneNode(true)` to keep the original usable.
3555
- * @param {DocumentFragment} fragment
3556
- * @returns {string} the html
3164
+ * @param {DocumentFragment|Node} fragment
3165
+ * @returns {string}
3557
3166
  */
3558
3167
  static toHtml(fragment) {
3559
3168
  var el = document.createElement('template');
@@ -3561,17 +3170,17 @@ class Fragments {
3561
3170
  return el.innerHTML;
3562
3171
  }
3563
3172
  /**
3564
- * Checks if a fragment contains only blank text nodes
3173
+ * Whether a fragment holds no element and no text but whitespace.
3565
3174
  * @param {DocumentFragment} fragment
3566
- * @returns {boolean} true if the fragment is blank
3175
+ * @returns {boolean}
3567
3176
  */
3568
3177
  static isBlank(fragment) {
3569
3178
  return fragment.childElementCount === 0 && fragment.textContent.trim() === '';
3570
3179
  }
3571
3180
  /**
3572
- * Creates a DocumentFragment from nodes.
3573
- * @param {...Node} nodes
3574
- * @returns {DocumentFragment} the fragment
3181
+ * Moves the nodes, in order, into a new DocumentFragment.
3182
+ * @param {...Node} nodes
3183
+ * @returns {DocumentFragment}
3575
3184
  */
3576
3185
  static from(...nodes) {
3577
3186
  const fragment = new DocumentFragment();
@@ -3581,9 +3190,10 @@ class Fragments {
3581
3190
  return fragment;
3582
3191
  }
3583
3192
  /**
3584
- * Creates a DocumentFragment from childNodes of an element.
3193
+ * Moves the children of a node into a new DocumentFragment, leaving the
3194
+ * node empty.
3585
3195
  * @param {Node} el
3586
- * @returns {DocumentFragment} the fragment
3196
+ * @returns {DocumentFragment}
3587
3197
  */
3588
3198
  static fromChildNodes(el) {
3589
3199
  const fragment = new DocumentFragment();
@@ -3594,23 +3204,23 @@ class Fragments {
3594
3204
  }
3595
3205
  }
3596
3206
 
3597
- /** Attribute reads and writes where a nullish value removes the attribute instead of setting it to the string 'null'. */
3207
+ /** Attribute helpers: unique ids, defaults, prefixed forwarding, and writes where a nullish value removes the attribute. */
3598
3208
  class Attributes {
3599
3209
  static id = 0;
3600
3210
  /**
3601
- * Creates a unique id with the given prefix.
3211
+ * A page-unique id under the prefix, as `prefix-n`.
3602
3212
  * @param {string} prefix
3603
- * @returns
3213
+ * @returns {string}
3604
3214
  */
3605
3215
  static uid(prefix) {
3606
3216
  return `${prefix}-${++Attributes.id}`;
3607
3217
  }
3608
3218
  /**
3609
- * Sets an attribute if not present.
3219
+ * Sets an attribute when the element does not carry it.
3610
3220
  * @param {Element} el
3611
3221
  * @param {string} k
3612
3222
  * @param {string} v
3613
- * @returns
3223
+ * @returns {string|null} the attribute's value afterwards
3614
3224
  */
3615
3225
  static defaultValue(el, k, v) {
3616
3226
  if (!el.hasAttribute(k)) {
@@ -3619,7 +3229,9 @@ class Attributes {
3619
3229
  return el.getAttribute(k);
3620
3230
  }
3621
3231
  /**
3622
- * Forwards prefixed attributes from an element to another (removing the prefix).
3232
+ * Copies every attribute whose name starts with the prefix onto another
3233
+ * element, under the name without the prefix. `<prefix>class` adds its
3234
+ * classes to the target's instead of replacing them.
3623
3235
  * @param {string} prefix
3624
3236
  * @param {Element} from
3625
3237
  * @param {Element} to
@@ -3642,7 +3254,7 @@ class Attributes {
3642
3254
  });
3643
3255
  }
3644
3256
  /**
3645
- * Sets the value of an attribute. nullish values remove the attribute.
3257
+ * Sets an attribute, or removes it when the value is nullish.
3646
3258
  * @param {Element} el
3647
3259
  * @param {string} attr
3648
3260
  * @param {string | null | undefined} value
@@ -3664,13 +3276,14 @@ class Attributes {
3664
3276
  */
3665
3277
  class LightSlots {
3666
3278
  /**
3667
- * Extracts light slots from an element. For non default slots in a template tag, the content is extracted.
3279
+ * Takes an element's children apart into slots, leaving the element empty.
3280
+ * A named child is removed and contributes what `slotFromNode` answers;
3281
+ * children sharing a name are gathered in order. A child with an empty
3282
+ * `slot` belongs to the default slot, in document order among the rest.
3668
3283
  * @param {Element} el
3669
- * @returns the slots
3284
+ * @returns {Record<string, DocumentFragment>} the slots by name, `default` always present
3670
3285
  */
3671
3286
  static from(el) {
3672
- //the platform reads slot="" as the default slot: such children stay in
3673
- //the document order of the default content, their claim stripped like any named one
3674
3287
  for (const child of el.children) {
3675
3288
  if (child.matches('[slot=""]')) {
3676
3289
  child.removeAttribute('slot');
@@ -3685,7 +3298,7 @@ class LightSlots {
3685
3298
  el.removeAttribute('slot');
3686
3299
  return [slot, LightSlots.slotFromNode(el)];
3687
3300
  });
3688
- const slots = {};
3301
+ const slots = Object.create(null);
3689
3302
  slots.default = new DocumentFragment();
3690
3303
  slots.default.append(...el.childNodes);
3691
3304
  for (const [name, el] of namedSlots) {
@@ -3696,6 +3309,12 @@ class LightSlots {
3696
3309
  }
3697
3310
  return slots;
3698
3311
  }
3312
+ /**
3313
+ * What a slotted node contributes: a template's content, the parsed markup
3314
+ * of a `<script type="text/html">`, or the node itself.
3315
+ * @param {Element} el
3316
+ * @returns {Element|DocumentFragment}
3317
+ */
3699
3318
  static slotFromNode(el) {
3700
3319
  if (el instanceof HTMLTemplateElement) {
3701
3320
  return document.adoptNode(el.content);
@@ -3707,12 +3326,13 @@ class LightSlots {
3707
3326
  }
3708
3327
  }
3709
3328
 
3710
- /** Waits for the parser and for the document, for elements that upgrade before their own markup is complete. */
3329
+ /** Waits on the parser and the document, and queries an element's own children. */
3711
3330
  class Nodes {
3712
3331
  /**
3713
- * Checks if an element is already parsed.
3332
+ * Whether the parser has moved past the element: the element, or one of
3333
+ * its ancestors, has a next sibling.
3714
3334
  * @param {Element} el
3715
- * @returns
3335
+ * @returns {boolean}
3716
3336
  */
3717
3337
  static isParsed(el) {
3718
3338
  for (let c = /** @type {Node | null} */ (el); c; c = c.parentNode) {
@@ -3724,13 +3344,12 @@ class Nodes {
3724
3344
  }
3725
3345
 
3726
3346
  /**
3727
- * Waits for the document's DOMContentLoaded, resolving immediately when
3728
- * that moment already passed. 'interactive' alone cannot tell a document
3729
- * still waiting for deferred scripts (the event comes, however late) from
3730
- * a module imported past it (the event is gone): the two events order
3731
- * themselves, DOMContentLoaded always precedes load, so racing the two
3732
- * resolves with DCL whenever it is still coming and with load otherwise,
3733
- * never early.
3347
+ * Waits for the document's DOMContentLoaded. Resolves at once when that
3348
+ * moment already passed, and for a document with no view, which receives
3349
+ * no events. Never resolves early for a document still running its
3350
+ * deferred scripts.
3351
+ * @param {Document} doc
3352
+ * @returns {Promise<void>}
3734
3353
  */
3735
3354
  static waitDomContentLoaded(doc) {
3736
3355
  if (doc.readyState === 'loading') {
@@ -3743,7 +3362,13 @@ class Nodes {
3743
3362
  }
3744
3363
  const win = doc.defaultView;
3745
3364
  if (win === null) {
3746
- //a viewless document can receive no event at all
3365
+ return Promise.resolve();
3366
+ }
3367
+ // interactive spans both the deferred scripts and the time between DOMContentLoaded and load
3368
+ const [navigation] = /** @type {PerformanceNavigationTiming[]} */ (
3369
+ win.performance?.getEntriesByType('navigation') ?? []
3370
+ );
3371
+ if (navigation?.domContentLoadedEventStart > 0) {
3747
3372
  return Promise.resolve();
3748
3373
  }
3749
3374
  return new Promise((resolve) => {
@@ -3762,22 +3387,12 @@ class Nodes {
3762
3387
  * resolves once the element, or any of its ancestors, gains a next sibling,
3763
3388
  * which is the parser having moved past this subtree. The document's
3764
3389
  * DOMContentLoaded is the deadline. Resolves immediately for an element
3765
- * that is already parsed.
3766
- *
3767
- * Every ancestor is watched, not only the parent, because that is what the
3768
- * predicate reads: an element last among its siblings becomes parsed when
3769
- * an ancestor gains one, and a childList observer on the parent never sees
3770
- * that. Formatted markup usually hides the difference, the whitespace
3771
- * before a closing tag being a text node the parent does gain, so the gap
3772
- * shows on whitespace-free markup, where the wait fell back to the
3773
- * deadline. One observer takes many targets, so the cost is one observe()
3774
- * per level, and it is disconnected at the first checkpoint past the
3775
- * element either way.
3776
- * @param {any} el
3777
- * @returns {Promise<any>}
3390
+ * that is already parsed, or once the parser has finished the document.
3391
+ * @param {Element} el
3392
+ * @returns {Promise<Element>} the element
3778
3393
  */
3779
3394
  static waitParsed(el) {
3780
- if (Nodes.isParsed(el)) {
3395
+ if (Nodes.isParsed(el) || el.ownerDocument.readyState !== 'loading') {
3781
3396
  return Promise.resolve(el);
3782
3397
  }
3783
3398
  return new Promise((resolve) => {
@@ -3799,10 +3414,10 @@ class Nodes {
3799
3414
  }
3800
3415
 
3801
3416
  /**
3802
- * Returns the first child of the element element (if exists) matching the selector.
3803
- * @param {Element} el
3417
+ * The first child element matching the selector, descendants excluded.
3418
+ * @param {Element|DocumentFragment} el
3804
3419
  * @param {string} selector
3805
- * @returns
3420
+ * @returns {Element|null}
3806
3421
  */
3807
3422
  static queryChildren(el, selector) {
3808
3423
  for (const c of el.children) {
@@ -3813,10 +3428,10 @@ class Nodes {
3813
3428
  return null;
3814
3429
  }
3815
3430
  /**
3816
- * Returns all children of the element matching the selector.
3817
- * @param {Element} el
3431
+ * Every child element matching the selector, descendants excluded.
3432
+ * @param {Element|DocumentFragment} el
3818
3433
  * @param {string} selector
3819
- * @returns
3434
+ * @returns {Element[]}
3820
3435
  */
3821
3436
  static queryChildrenAll(el, selector) {
3822
3437
  const r = [];
@@ -3873,9 +3488,11 @@ class NodeOperations {
3873
3488
 
3874
3489
  /** The `data-tpl-*` commands, each taking the node it is written on and the scope it evaluates in. */
3875
3490
  class CommandsHandler {
3876
- //the order is the semantics: each command sees the node as the ones before
3877
- //it left it, so tplIf gates before tplWith/tplEach push their overlay and
3878
- //tplWhen gates after (inside the sub-render, where the overlay is in scope)
3491
+ /**
3492
+ * The order the commands run in on one element, each seeing the node as the
3493
+ * ones before left it: tplIf gates before tplWith and tplEach push their
3494
+ * overlay, tplWhen gates after, inside the scope that overlay opened.
3495
+ */
3879
3496
  static ORDERED_COMMANDS = [
3880
3497
  'tplIf',
3881
3498
  'tplWith',
@@ -3886,8 +3503,7 @@ class CommandsHandler {
3886
3503
  'tplRemove',
3887
3504
  'tplVerbatim',
3888
3505
  ];
3889
- //same body as tplWhen: the two differ only in ORDERED_COMMANDS position,
3890
- //if evaluates in the outer scope, before with/each
3506
+ /** Removes the node when the expression is falsy, evaluated before tplWith and tplEach open their overlay. */
3891
3507
  static tplIf(node, expression, ops, evaluator) {
3892
3508
  const accept = evaluator.evaluateExpression(expression);
3893
3509
  if (!accept) {
@@ -3902,49 +3518,37 @@ class CommandsHandler {
3902
3518
  .render();
3903
3519
  ops.replace(node, newNode);
3904
3520
  }
3521
+ /**
3522
+ * Renders the node once per entry, in place of the node. A Map iterates as
3523
+ * `{ key, value }` entries in its own order, a plain object as `{ key, value }`
3524
+ * entries in `Object.entries` order, and any other iterable as itself; a
3525
+ * value that is none of these throws. Each render sees its entry under
3526
+ * `data-tpl-var`, or as the whole overlay without one. `data-tpl-stat` names
3527
+ * an overlay beneath the entry carrying `index`, `count`, `size`, `first`,
3528
+ * `last`, `even` and `odd`, so an entry property of the same name wins; the
3529
+ * size, and so `last`, is null for a collection that knows neither its
3530
+ * `length` nor its `size`, which is never consumed to learn it.
3531
+ */
3905
3532
  static tplEach(node, expression, ops, evaluator) {
3906
3533
  const varName = ops.popData(node, 'tplVar');
3907
3534
  const statName = ops.popData(node, 'tplStat');
3908
3535
  const template = new Template(node, evaluator);
3909
3536
  const evaluated = evaluator.evaluateExpression(expression);
3910
- //keyed collections iterate as {key, value} entries: a Map in its own
3911
- //order, a non-iterable plain dict in Object.entries order. Plain alone:
3912
- //a class instance or a response wrapper standing where an array was
3913
- //expected still fails loudly below, and any other iterable (a Set, a
3914
- //generator, an entries() iterator) iterates as itself
3915
- const proto = evaluated === null || typeof evaluated !== 'object' ? undefined : Object.getPrototypeOf(evaluated);
3916
- const keyed =
3917
- evaluated instanceof Map
3918
- ? [...evaluated]
3919
- : !evaluated?.[Symbol.iterator] && (proto === Object.prototype || proto === null)
3920
- ? Object.entries(evaluated)
3921
- : null;
3537
+ const proto =
3538
+ evaluated === null || typeof evaluated !== 'object' ? undefined : Object.getPrototypeOf(evaluated);
3539
+ const plain = !evaluated?.[Symbol.iterator] && (proto === Object.prototype || proto === null);
3540
+ const dict = plain ? Object.entries(evaluated) : null;
3541
+ const keyed = evaluated instanceof Map ? [...evaluated] : dict;
3922
3542
  const entries = keyed === null ? evaluated : keyed.map(([key, value]) => ({ key, value }));
3923
3543
  if (!entries?.[Symbol.iterator]) {
3924
3544
  throw new Error(`Expected an iterable got '${evaluated}'`);
3925
3545
  }
3926
- if (!statName) {
3927
- for (const v of entries) {
3928
- ops.prepend(node, template.withOverlay(varName ? { [varName]: v } : v).render());
3929
- }
3930
- ops.remove(node);
3931
- return;
3932
- }
3933
- //the stat rides grouped under its declared name, overlaid below the
3934
- //item, so data can never collide with its fields and an item property
3935
- //sharing the stat's very name wins, as data always does. The size is
3936
- //read where the collection already knows it (arrays and keyed entries
3937
- //by length, sets and the like by size): an arbitrary iterator is never
3938
- //consumed to learn it, so its size and last read null, the unknown
3939
- const size = Array.isArray(entries)
3940
- ? entries.length
3941
- : typeof entries.length === 'number'
3942
- ? entries.length
3943
- : typeof entries.size === 'number'
3944
- ? entries.size
3945
- : null;
3546
+ const measured = statName ? entries : null;
3547
+ const counted = typeof measured?.length === 'number' ? measured.length : measured?.size;
3548
+ const size = typeof counted === 'number' ? counted : null;
3946
3549
  let index = 0;
3947
3550
  for (const v of entries) {
3551
+ const item = varName ? { [varName]: v } : v;
3948
3552
  const stat = {
3949
3553
  index,
3950
3554
  count: index + 1,
@@ -3954,16 +3558,13 @@ class CommandsHandler {
3954
3558
  even: index % 2 === 0,
3955
3559
  odd: index % 2 === 1,
3956
3560
  };
3957
- ops.prepend(
3958
- node,
3959
- template.withOverlay({ [statName]: stat }, varName ? { [varName]: v } : v).render(),
3960
- );
3561
+ const layers = statName ? [{ [statName]: stat }, item] : [item];
3562
+ ops.prepend(node, template.withOverlay(...layers).render());
3961
3563
  ++index;
3962
3564
  }
3963
3565
  ops.remove(node);
3964
3566
  }
3965
- //same body as tplIf: the two differ only in ORDERED_COMMANDS position,
3966
- //when evaluates after with/each, in the scope their overlay opened
3567
+ /** Removes the node when the expression is falsy, evaluated in the overlay tplWith or tplEach opened. */
3967
3568
  static tplWhen(node, expression, ops, evaluator) {
3968
3569
  const accept = evaluator.evaluateExpression(expression);
3969
3570
  if (!accept) {
@@ -3977,12 +3578,8 @@ class CommandsHandler {
3977
3578
  static tplRemove(node, value, ops, evaluator) {
3978
3579
  switch (value.toLowerCase()) {
3979
3580
  case 'tag': {
3980
- const fragment = new DocumentFragment();
3981
- while (node.firstChild) {
3982
- fragment.appendChild(node.firstChild);
3983
- }
3581
+ const fragment = Fragments.fromChildNodes(node);
3984
3582
  if ('tplVerbatim' in node.dataset) {
3985
- //we are removing the parent element so we have to handle the lower priority commands
3986
3583
  ops.popData(node, 'tplVerbatim');
3987
3584
  ops.replace(node, fragment);
3988
3585
  } else {
@@ -4018,6 +3615,9 @@ class CommandsHandler {
4018
3615
  }
4019
3616
  const tuples = Array.isArray(attributesAndValues[0]) ? attributesAndValues : [attributesAndValues];
4020
3617
  tuples.forEach(([k, v]) => {
3618
+ if (v === null || v === undefined) {
3619
+ return;
3620
+ }
4021
3621
  node.setAttribute(k, v);
4022
3622
  });
4023
3623
  }
@@ -4045,12 +3645,10 @@ class CommandsHandler {
4045
3645
  }
4046
3646
  }
4047
3647
 
4048
- // Module-isolated string cache for dataset-to-attribute conversions
4049
3648
  const attributeCache = new BoundedCache(1000);
4050
3649
 
4051
3650
  /**
4052
- * Converts a tpl camelCase dataset key into a kebab-case attribute name.
4053
- * Uses a cache lookup to avoid repetitive regex/string splitting.
3651
+ * The attribute a `tpl` dataset key targets: `tplAriaLabel` is `aria-label`.
4054
3652
  * @param {string} dataSetKey
4055
3653
  * @returns {string}
4056
3654
  */
@@ -4072,38 +3670,40 @@ function toAttr(dataSetKey) {
4072
3670
  */
4073
3671
  class Template {
4074
3672
  /**
4075
- * Creates a template from a string.
3673
+ * Creates a template from html.
4076
3674
  * @param {string} html
4077
3675
  * @param {{ [k: string] : any }?} [modules]
4078
- * @param {...*} data
4079
- * @returns the template
3676
+ * @param {...*} data the data stack, innermost last
3677
+ * @returns {Template}
4080
3678
  */
4081
3679
  static fromHtml(html, modules, ...data) {
4082
3680
  return new Template(Fragments.fromHtml(html), new ExpressionEvaluator(modules, data));
4083
3681
  }
4084
3682
 
4085
3683
  /**
4086
- * Creates a template from the content of the first template element matching the selector.
3684
+ * Creates a template from the content of the first element matching the
3685
+ * selector, which is adopted: the template element is left empty.
4087
3686
  * @param {string} selector for an HTMLTemplateElement
4088
3687
  * @param {{ [k: string] : any }?} [modules]
4089
- * @param {...*} data
4090
- * @returns the template
3688
+ * @param {...*} data the data stack, innermost last
3689
+ * @returns {Template}
3690
+ * @throws when the selector matches no template element
4091
3691
  */
4092
3692
  static fromSelector(selector, modules, ...data) {
4093
3693
  const templateEl = document.querySelector(selector);
4094
3694
  if (!(templateEl instanceof HTMLTemplateElement)) {
4095
3695
  throw new Error('template selector does not match any template tag');
4096
3696
  }
4097
- const fragment = document.adoptNode(templateEl.content);
4098
- return new Template(fragment, new ExpressionEvaluator(modules, data));
3697
+ return Template.fromTemplate(templateEl, modules, ...data);
4099
3698
  }
4100
3699
 
4101
3700
  /**
4102
- * Creates a template from the content of an HTMLTemplateElement.
3701
+ * Creates a template from the content of a template element, which is
3702
+ * adopted: the template element is left empty.
4103
3703
  * @param {HTMLTemplateElement} templateEl
4104
3704
  * @param {{ [k: string] : any }?} [modules]
4105
- * @param {...*} data
4106
- * @returns the template
3705
+ * @param {...*} data the data stack, innermost last
3706
+ * @returns {Template}
4107
3707
  */
4108
3708
  static fromTemplate(templateEl, modules, ...data) {
4109
3709
  const fragment = document.adoptNode(templateEl.content);
@@ -4111,11 +3711,12 @@ class Template {
4111
3711
  }
4112
3712
 
4113
3713
  /**
4114
- * Creates a template from a DocumentFragment.
3714
+ * Creates a template over a fragment, which is imported afresh on every
3715
+ * render and never modified.
4115
3716
  * @param {DocumentFragment} fragment
4116
3717
  * @param { { [k: string] : any }? } [modules]
4117
- * @param {...*} data
4118
- * @returns the template
3718
+ * @param {...*} data the data stack, innermost last
3719
+ * @returns {Template}
4119
3720
  */
4120
3721
  static fromFragment(fragment, modules, ...data) {
4121
3722
  return new Template(fragment, new ExpressionEvaluator(modules, data));
@@ -4135,28 +3736,32 @@ class Template {
4135
3736
  * Creates a new Template rendering in another scope: the one way to rebind
4136
3737
  * a compiled template to a registry's modules and data.
4137
3738
  * @param {ExpressionEvaluator} evaluator
3739
+ * @returns {Template}
4138
3740
  */
4139
3741
  withEvaluator(evaluator) {
4140
3742
  return new Template(this.#fragment, evaluator);
4141
3743
  }
4142
3744
  /**
4143
- * Creates a new Template replacing the fragment.
3745
+ * Creates a new Template over another fragment, in the same scope.
4144
3746
  * @param {DocumentFragment} fragment
3747
+ * @returns {Template}
4145
3748
  */
4146
3749
  withFragment(fragment) {
4147
3750
  return new Template(fragment, this.#evaluator);
4148
3751
  }
4149
3752
  /**
4150
- * Creates a new Template with a new module added.
3753
+ * Creates a new Template with a module added, as `ExpressionEvaluator.withModule` does.
4151
3754
  * @param {string?} name
4152
3755
  * @param {{[k: string]: any}} value
3756
+ * @returns {Template}
4153
3757
  */
4154
3758
  withModule(name, value) {
4155
3759
  return new Template(this.#fragment, this.#evaluator.withModule(name, value));
4156
3760
  }
4157
3761
  /**
4158
- * Creates a new Template with new a data overlay added to the stack.
3762
+ * Creates a new Template with the data pushed as the innermost overlays.
4159
3763
  * @param {...*} data
3764
+ * @returns {Template}
4160
3765
  */
4161
3766
  withOverlay(...data) {
4162
3767
  return new Template(this.#fragment, this.#evaluator.withOverlay(...data));
@@ -4165,7 +3770,7 @@ class Template {
4165
3770
  * Evaluates an expression in this template's scope, widened by the overlays.
4166
3771
  * @param {string} expression
4167
3772
  * @param {...*} data
4168
- * @returns the evaluated expression result
3773
+ * @returns {any}
4169
3774
  */
4170
3775
  evaluateExpression(expression, ...data) {
4171
3776
  return this.#evaluator.withOverlay(...data).evaluateExpression(expression);
@@ -4174,33 +3779,29 @@ class Template {
4174
3779
  * Evaluates a templated text, the `{{ }}` form, in this template's scope.
4175
3780
  * @param {string} text
4176
3781
  * @param {...*} data
4177
- * @returns the parts the text evaluates to
3782
+ * @returns {{ type: symbol, value: any }[]} the parts, as `Expressions.MODE_TEMPLATED` describes
4178
3783
  */
4179
3784
  evaluateTemplated(text, ...data) {
4180
3785
  return this.#evaluator.withOverlay(...data).evaluateTemplated(text);
4181
3786
  }
4182
3787
  /**
4183
- * Returns an expression evaluator with bound modules and dataStack.
3788
+ * The scope this template renders in.
3789
+ * @returns {ExpressionEvaluator}
4184
3790
  */
4185
3791
  evaluator() {
4186
3792
  return this.#evaluator;
4187
3793
  }
4188
3794
  /**
4189
- * Renders the template.
4190
- * @returns a DocumentFragment
3795
+ * Renders the template into a new fragment, applying the `data-tpl-*`
3796
+ * commands and bindings and the `{{ }}` interpolations.
3797
+ * @returns {DocumentFragment}
3798
+ * @throws {RenderError} naming the node and the directive that failed
4191
3799
  */
4192
3800
  render() {
4193
3801
  try {
4194
3802
  const ops = new NodeOperations();
4195
3803
  const imported = document.importNode(this.#fragment, true);
4196
- const fragment =
4197
- imported.nodeType === Node.DOCUMENT_FRAGMENT_NODE
4198
- ? imported
4199
- : (() => {
4200
- const d = new DocumentFragment();
4201
- d.appendChild(imported);
4202
- return d;
4203
- })();
3804
+ const fragment = imported.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? imported : Fragments.from(imported);
4204
3805
  const iterator = document.createNodeIterator(
4205
3806
  fragment,
4206
3807
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
@@ -4226,8 +3827,6 @@ class Template {
4226
3827
  try {
4227
3828
  CommandsHandler[command](el, value, ops, this.#evaluator);
4228
3829
  } catch (ex) {
4229
- //the directive is popped before it runs, so the open tag no
4230
- //longer carries it: the frame names it and its expression
4231
3830
  throw RenderError.wrap(`Error evaluating data-tpl-${toAttr(command)}="${value}"`, el, ex);
4232
3831
  }
4233
3832
  if (ops.removed(el)) {
@@ -4250,17 +3849,13 @@ class Template {
4250
3849
  el.setAttribute(attributeName, evaluated);
4251
3850
  }
4252
3851
  } catch (ex) {
4253
- throw RenderError.wrap(`Error evaluating data-tpl-${toAttr(dataSetKey)}="${expression}"`, el, ex);
3852
+ throw RenderError.wrap(`Error evaluating data-tpl-${attributeName}="${expression}"`, el, ex);
4254
3853
  }
4255
3854
  }
4256
3855
  }
4257
3856
  ops.cleanup();
4258
3857
  return fragment;
4259
3858
  } catch (ex) {
4260
- //a command, a text node or a nested render already named the node it
4261
- //failed on: wrapping again would add a frame for the fragment that
4262
- //contains it, one per level, serializing the whole template into the
4263
- //message that survives. Only a failure outside those is framed here
4264
3859
  if (ex instanceof RenderError) {
4265
3860
  throw ex;
4266
3861
  }
@@ -4268,22 +3863,26 @@ class Template {
4268
3863
  }
4269
3864
  }
4270
3865
  /**
4271
- * Renders this template on the Element (replacing children).
3866
+ * Renders this template into the element, replacing its children.
4272
3867
  * @param {Element} el
3868
+ * @throws {RenderError}
4273
3869
  */
4274
3870
  renderTo(el) {
4275
3871
  el.replaceChildren(this.render());
4276
3872
  }
4277
3873
  /**
4278
- * Renders this template appending the resulting fragment to the Element.
3874
+ * Renders this template and appends the result to the element.
4279
3875
  * @param {Element} el
3876
+ * @throws {RenderError}
4280
3877
  */
4281
3878
  appendTo(el) {
4282
3879
  el.appendChild(this.render());
4283
3880
  }
4284
3881
  /**
4285
- * Renders this template on the first Element matching the selector (replacing children), if exists.
3882
+ * Renders this template into the first element matching the selector,
3883
+ * replacing its children; does nothing when none matches.
4286
3884
  * @param {string} selector
3885
+ * @throws {RenderError}
4287
3886
  */
4288
3887
  renderToSelector(selector) {
4289
3888
  const el = document.querySelector(selector);
@@ -4292,8 +3891,10 @@ class Template {
4292
3891
  }
4293
3892
  }
4294
3893
  /**
4295
- * Renders this template appending the resulting fragment to the Element matching the selector, if exists.
3894
+ * Renders this template and appends the result to the first element
3895
+ * matching the selector; does nothing when none matches.
4296
3896
  * @param {string} selector
3897
+ * @throws {RenderError}
4297
3898
  */
4298
3899
  appendToSelector(selector) {
4299
3900
  const el = document.querySelector(selector);
@@ -4317,30 +3918,26 @@ class Template {
4317
3918
  }
4318
3919
 
4319
3920
  /**
4320
- * A render failure, one frame per nesting level. Each frame names the node it
4321
- * failed on and what was being evaluated there, so the chain reads as the path
4322
- * from the template's root down to the offending expression. The frame carries
4323
- * the node's identification only, an open tag rather than its whole subtree:
4324
- * the markup is serialized on demand through `html`, and the live node stays on
4325
- * `node`, so a failure costs no clone and a nested failure does not embed the
4326
- * page in its own message.
3921
+ * A render failure, one frame per nesting level, chained through `cause` from
3922
+ * the outermost frame down to the error that failed the expression. A frame's
3923
+ * message names what was being evaluated and the node's open tag, with the
3924
+ * attributes it still carries: the directive being evaluated is already off
3925
+ * the tag. The subtree is not in the message; `html` serializes it on demand.
4327
3926
  */
4328
3927
  class RenderError extends Error {
4329
- /**
4330
- * How many frames a chain keeps. The innermost are the specific ones, so a
4331
- * deeper nesting drops the outer context rather than the failure site: three
4332
- * frames name the offending node and the two levels that hold it, which is
4333
- * the path a reader follows without the page arriving with it.
4334
- */
3928
+ /** How many frames a chain keeps: a deeper nesting drops the outermost ones and marks the chain `truncated`. */
4335
3929
  static FRAMES = 3;
4336
3930
  #node;
4337
3931
  #depth;
4338
- /** true when the budget dropped the outer frames of this chain */
3932
+ /** Whether the chain dropped outer frames to stay within FRAMES. */
4339
3933
  truncated = false;
4340
3934
  /**
4341
- * Frames a failure, unless the chain already spent its budget: then the
4342
- * cause travels on, marked so a report can say the outer context was
4343
- * dropped.
3935
+ * Frames a failure, unless the chain already holds FRAMES frames: then the
3936
+ * cause is returned as it is, marked `truncated`.
3937
+ * @param {string} message
3938
+ * @param {Node} nodeOrFragment the node that failed
3939
+ * @param {any} cause
3940
+ * @returns {RenderError}
4344
3941
  */
4345
3942
  static wrap(message, nodeOrFragment, cause) {
4346
3943
  if (cause instanceof RenderError && cause.depth >= RenderError.FRAMES) {
@@ -4349,27 +3946,44 @@ class RenderError extends Error {
4349
3946
  }
4350
3947
  return new RenderError(message, nodeOrFragment, cause);
4351
3948
  }
3949
+ /**
3950
+ * @param {string} message
3951
+ * @param {Node} nodeOrFragment the node that failed
3952
+ * @param {any} cause
3953
+ */
4352
3954
  constructor(message, nodeOrFragment, cause) {
4353
3955
  super(`${message} in \`${RenderError.describe(nodeOrFragment)}\``, { cause });
4354
3956
  this.name = 'RenderError';
4355
3957
  this.#node = nodeOrFragment;
4356
3958
  this.#depth = (cause instanceof RenderError ? cause.depth : 0) + 1;
4357
3959
  }
4358
- /** How many frames this chain carries, this one included. */
3960
+ /**
3961
+ * How many frames this chain carries, this one included.
3962
+ * @returns {number}
3963
+ */
4359
3964
  get depth() {
4360
3965
  return this.#depth;
4361
3966
  }
4362
- /** The node the render failed on, live: it keeps its place in the fragment being built. */
3967
+ /**
3968
+ * The node the render failed on, live in the fragment being built.
3969
+ * @returns {Node}
3970
+ */
4363
3971
  get node() {
4364
3972
  return this.#node;
4365
3973
  }
4366
- /** The node's markup, serialized when asked for rather than on every failure. */
3974
+ /**
3975
+ * The failed node's markup, whitespace trimmed, serialized on each read.
3976
+ * @returns {string}
3977
+ */
4367
3978
  get html() {
4368
3979
  return RenderError.stringify(this.#node);
4369
3980
  }
4370
3981
  /**
4371
3982
  * What identifies a node in a frame: an element by its open tag, a text node
4372
- * by its source, a fragment by the open tags of the elements it holds.
3983
+ * by its source, a fragment by the open tags of the elements it holds, cut
3984
+ * at 120 characters.
3985
+ * @param {Node} nodeOrFragment
3986
+ * @returns {string}
4373
3987
  */
4374
3988
  static describe(nodeOrFragment) {
4375
3989
  if (nodeOrFragment.nodeType === Node.TEXT_NODE) {
@@ -4388,10 +4002,13 @@ class RenderError extends Error {
4388
4002
  static #ellipsize(text) {
4389
4003
  return text.length > 120 ? `${text.slice(0, 120)}…` : text;
4390
4004
  }
4005
+ /**
4006
+ * A node's markup with its whitespace-only text dropped and the rest trimmed.
4007
+ * @param {Node} nodeOrFragment left untouched: a clone is serialized
4008
+ * @returns {string}
4009
+ */
4391
4010
  static stringify(nodeOrFragment) {
4392
- const t = document.createElement('template');
4393
- t.content.appendChild(RenderError.#cleanup(nodeOrFragment.cloneNode(true)));
4394
- return t.innerHTML;
4011
+ return Fragments.toHtml(RenderError.#cleanup(nodeOrFragment.cloneNode(true)));
4395
4012
  }
4396
4013
  static #cleanup(node) {
4397
4014
  if (node.nodeType === Node.TEXT_NODE) {
@@ -4413,11 +4030,6 @@ class RenderError extends Error {
4413
4030
  }
4414
4031
  }
4415
4032
 
4416
- /**
4417
- * Tracks the elements waiting to render. `ready` resolves once the queue has
4418
- * drained and never rejects; the promise kept per element resolves when that
4419
- * element has rendered and rejects with whatever its upgrade threw.
4420
- */
4421
4033
  class UpgradeQueue {
4422
4034
  #q = new Map();
4423
4035
  #readyResolve;
@@ -4429,12 +4041,6 @@ class UpgradeQueue {
4429
4041
  this.#start();
4430
4042
  });
4431
4043
  }
4432
- /**
4433
- * Waits for the page's readiness: the promise resolves right after the
4434
- * ftl:ready event is dispatched, and immediately when that moment already
4435
- * passed.
4436
- * @returns {Promise<void>}
4437
- */
4438
4044
  ready() {
4439
4045
  return this.#ready;
4440
4046
  }
@@ -4450,13 +4056,8 @@ class UpgradeQueue {
4450
4056
  }
4451
4057
  enqueue(el) {
4452
4058
  if (this.#q.has(el)) {
4453
- //already upgrading, can happen when disconnecting an element
4454
- //while it's already queued for upgrade (e.g.: ful-form)
4455
4059
  return;
4456
4060
  }
4457
- //one entry, two signals: readiness waits on a signal that only ever
4458
- //resolves, so nothing here attaches a rejection handler to the upgrade
4459
- //itself and a component that fails is still reported the way it always was
4460
4061
  const { promise: finished, resolve: markFinished } = /** @type {PromiseWithResolvers<void>} */ (
4461
4062
  Promise.withResolvers()
4462
4063
  );
@@ -4468,11 +4069,6 @@ class UpgradeQueue {
4468
4069
  });
4469
4070
  this.#q.set(el, { upgrade, finished });
4470
4071
  }
4471
- /**
4472
- * The one fixed-point loop: drains the accepted entries, including the ones
4473
- * enqueued while waiting, since a component is only queued once its parent
4474
- * connects it and a single pass would miss everything nested.
4475
- */
4476
4072
  async #drain(accept, pick) {
4477
4073
  for (;;) {
4478
4074
  const pending = Array.from(this.#q)
@@ -4485,22 +4081,25 @@ class UpgradeQueue {
4485
4081
  }
4486
4082
  }
4487
4083
  /**
4488
- * Waits for the whole queue to drain. Never rejects: a component that fails
4489
- * does not hold the others back, and readiness means the queue drained rather
4490
- * than that everything worked.
4084
+ * Waits for the whole queue to drain, the upgrades enqueued meanwhile included.
4085
+ * Never rejects: a failed upgrade counts as drained.
4086
+ * @returns {Promise<void>}
4491
4087
  */
4492
4088
  settled() {
4493
4089
  return this.#drain(() => true, (entry) => entry.finished);
4494
4090
  }
4495
- /** Waits for the accepted upgrades, rejecting with the first that failed. */
4091
+ /**
4092
+ * Waits for the accepted upgrades, the ones enqueued meanwhile included,
4093
+ * rejecting with the first that failed.
4094
+ * @param {(el: Element) => boolean} accept
4095
+ * @returns {Promise<void>}
4096
+ */
4496
4097
  upgraded(accept) {
4497
4098
  return this.#drain(accept, (entry) => entry.upgrade);
4498
4099
  }
4499
- /** The pending upgrade of one element, undefined when it is not queued. */
4500
4100
  whenUpgraded(el) {
4501
4101
  return this.#q.get(el)?.upgrade;
4502
4102
  }
4503
- /** The elements whose upgrade is still pending, in queue order. */
4504
4103
  pending() {
4505
4104
  return Array.from(this.#q.keys());
4506
4105
  }
@@ -4511,8 +4110,8 @@ class UpgradeQueue {
4511
4110
  * and attribute mappers. Elements defined before configure() are deferred and
4512
4111
  * defined by it; from then on every defineElement takes effect immediately.
4513
4112
  * The exported `registry` singleton is the page's own; a `new Registry()` is a
4514
- * separate instance, and Rendering and the ftl:ready machinery wait on the
4515
- * singleton alone.
4113
+ * separate instance with its own upgrade queue, which dispatches its own
4114
+ * ftl:ready on the document, while Rendering waits on the singleton alone.
4516
4115
  */
4517
4116
  class Registry {
4518
4117
  #tagToClass = {};
@@ -4581,9 +4180,12 @@ class Registry {
4581
4180
  * Registers a custom element under its tag: the class is augmented with its
4582
4181
  * BITS (observed attributes, mappers, templates) and handed to the platform.
4583
4182
  * Before configure() the definition is deferred, so import order never
4584
- * matters.
4183
+ * matters. The element resolves its templates and components through this
4184
+ * registry, whichever one a module imports, and its `static config` is read
4185
+ * here, so a page replacing it does so before the definition.
4585
4186
  * @param {string} tag
4586
4187
  * @param {*} klass a ParsedElement subclass
4188
+ * @returns {Registry} this registry
4587
4189
  */
4588
4190
  defineElement(tag, klass) {
4589
4191
  if (!this.#configured) {
@@ -4601,13 +4203,8 @@ class Registry {
4601
4203
  * ancestors', so a base class declares what every subclass keeps observing
4602
4204
  * (a protocol attribute such as Field's disabled claim) and a leaf refines a
4603
4205
  * mapping, or moves a name's position, without repeating the whole list.
4604
- *
4605
- * The walk stops where the platform's own class hierarchy begins: no earlier
4606
- * stop can work, since a registered ancestor carries an own BITS of its own,
4607
- * and nothing above the elements declares anything.
4608
- *
4609
- * Everything deriving a component's attribute vocabulary reads it here, so
4610
- * the runtime and whatever documents it cannot walk the chain differently.
4206
+ * The observed attributes are applied in the composed order, a re-declared
4207
+ * name taking the position of its last declaration.
4611
4208
  * @param {*} klass a ParsedElement subclass
4612
4209
  * @returns {{ observed: string[], attributes: string[] }}
4613
4210
  */
@@ -4623,14 +4220,7 @@ class Registry {
4623
4220
  * The property an observed attribute drives, by the platform's own
4624
4221
  * dash-to-camel rule: the one `dataset` applies, so `clear-invalid-on-change`
4625
4222
  * would reach `clearInvalidOnChange` the way `data-clear-invalid-on-change`
4626
- * reaches `dataset.clearInvalidOnChange`. A single-word name is returned
4627
- * unchanged, which is what every observed attribute in the library is.
4628
- *
4629
- * It exists because the observed tier is the one that becomes properties:
4630
- * without it an attribute could only be observed if its name happened to be
4631
- * a usable identifier, which is why every multiword observed attribute here
4632
- * used to be squashed into one word while the configuration tier, which
4633
- * never becomes a property, spelled the same idea with a dash.
4223
+ * reaches `dataset.clearInvalidOnChange`.
4634
4224
  *
4635
4225
  * Only this direction is mapped. A property never derives its attribute: a
4636
4226
  * setter reflects through `reflectTo`, naming the attribute it writes.
@@ -4643,10 +4233,6 @@ class Registry {
4643
4233
  #augmentAndDefineElement(tag, klass) {
4644
4234
  const { observed, attributes } = Registry.declarationsOf(klass);
4645
4235
  const { template, templates, slots, mappers, config } = klass;
4646
- //a name a subclass re-declares takes the subclass's position, which is
4647
- //how a field whose value setter reads its own shape attributes declares
4648
- //that its value lands after them: the order the declarations compose in
4649
- //is the order the base applies them
4650
4236
  const declaredNames = observed.map((a) => a.split(':')[0]);
4651
4237
  const observedNames = [...new Set(declaredNames.reverse())].reverse();
4652
4238
  const attrToMapper = [...attributes, ...observed].reduce((acc, a) => {
@@ -4665,20 +4251,13 @@ class Registry {
4665
4251
  const nameToTemplate = Object.fromEntries(namesAndTemplates);
4666
4252
 
4667
4253
  klass.BITS = {
4668
- //the defining registry travels with the definition: an element resolves
4669
- //its templates and its components through the registry that defined it,
4670
- //not through whichever one a module happened to import
4671
4254
  registry: this,
4672
4255
  enqueue: (el) => this.#upgradeQueue.enqueue(el),
4673
4256
  SLOTS: slots,
4674
- //the class's own constants, overlaid on its templates as `config`:
4675
- //read here with the rest of the declaration, so a page replacing them
4676
- //does it before configure() like every other definition-time choice
4677
4257
  CONFIG: config,
4678
4258
  OBSERVED: observedNames,
4679
4259
  DECLARED: [...new Set([...observedNames, ...attributes.map((a) => a.split(':')[0])])],
4680
4260
  ATTR_TO_MAPPER: attrToMapper,
4681
- //resolved once here rather than at every attribute write
4682
4261
  ATTR_TO_PROPERTY: Object.fromEntries(observedNames.map((a) => [a, Registry.propertyOf(a)])),
4683
4262
  TEMPLATES: nameToTemplate,
4684
4263
  };
@@ -4690,6 +4269,7 @@ class Registry {
4690
4269
  * as `#fn`.
4691
4270
  * @param {string} name
4692
4271
  * @param {object} value
4272
+ * @returns {Registry} this registry
4693
4273
  */
4694
4274
  defineModule(name, value) {
4695
4275
  const module = name ? { [name]: value } : value;
@@ -4700,6 +4280,7 @@ class Registry {
4700
4280
  /**
4701
4281
  * Replaces the whole module map.
4702
4282
  * @param {object} ms
4283
+ * @returns {Registry} this registry
4703
4284
  */
4704
4285
  defineModules(ms) {
4705
4286
  this.#modules = ms;
@@ -4711,6 +4292,7 @@ class Registry {
4711
4292
  * through component().
4712
4293
  * @param {string} name
4713
4294
  * @param {*} value
4295
+ * @returns {Registry} this registry
4714
4296
  */
4715
4297
  defineComponent(name, value) {
4716
4298
  this.#components[name] = value;
@@ -4719,6 +4301,7 @@ class Registry {
4719
4301
  /**
4720
4302
  * Replaces the data stack the templates evaluate over.
4721
4303
  * @param {...any} data
4304
+ * @returns {Registry} this registry
4722
4305
  */
4723
4306
  defineData(...data) {
4724
4307
  this.#data = data;
@@ -4728,6 +4311,7 @@ class Registry {
4728
4311
  /**
4729
4312
  * Appends to the data stack, the later entry winning a shared name.
4730
4313
  * @param {...any} data
4314
+ * @returns {Registry} this registry
4731
4315
  */
4732
4316
  defineOverlay(...data) {
4733
4317
  this.#data = [...this.#data, ...data];
@@ -4739,6 +4323,7 @@ class Registry {
4739
4323
  * `name:type` declaration.
4740
4324
  * @param {string} k
4741
4325
  * @param {{ unmarshal(str: string|null, name: string, el: Element): any, marshal(value: any, name: string, el: Element): string|null }} v
4326
+ * @returns {Registry} this registry
4742
4327
  */
4743
4328
  defineMapper(k, v) {
4744
4329
  this.#mappers[k] = v;
@@ -4747,6 +4332,7 @@ class Registry {
4747
4332
  /**
4748
4333
  * Hands the registry over to the plugin's configure.
4749
4334
  * @param {{ configure(registry: Registry): void }} p
4335
+ * @returns {Registry} this registry
4750
4336
  */
4751
4337
  plugin(p) {
4752
4338
  p.configure(this);
@@ -4755,6 +4341,7 @@ class Registry {
4755
4341
  /**
4756
4342
  * Defines every element deferred so far; from then on, defineElement takes
4757
4343
  * effect immediately.
4344
+ * @returns {Registry} this registry
4758
4345
  */
4759
4346
  configure() {
4760
4347
  for (const [tag, klass] of Object.entries(this.#tagToClass)) {
@@ -4766,23 +4353,36 @@ class Registry {
4766
4353
  }
4767
4354
  /**
4768
4355
  * Waits for the queued upgrades the filter accepts, rejecting with the first
4769
- * that failed: the rejecting barrier Rendering is a facade over.
4356
+ * that failed, the upgrades enqueued meanwhile included: the rejecting
4357
+ * barrier Rendering is a facade over.
4770
4358
  * @param {(el: Element) => boolean} accept
4359
+ * @returns {Promise<void>}
4771
4360
  */
4772
4361
  settle(accept) {
4773
4362
  return this.#upgradeQueue.upgraded(accept);
4774
4363
  }
4775
- /** The pending upgrade of one element, undefined when it is not queued. */
4364
+ /**
4365
+ * The pending upgrade of one element: resolving when it has rendered,
4366
+ * rejecting with what its upgrade threw.
4367
+ * @param {Element} el
4368
+ * @returns {Promise<void>|undefined} undefined when the element is not queued
4369
+ */
4776
4370
  whenUpgraded(el) {
4777
4371
  return this.#upgradeQueue.whenUpgraded(el);
4778
4372
  }
4779
- /** The elements whose upgrade is still pending, in queue order. */
4373
+ /**
4374
+ * The elements whose upgrade is still pending, in queue order.
4375
+ * @returns {Element[]}
4376
+ */
4780
4377
  pending() {
4781
4378
  return this.#upgradeQueue.pending();
4782
4379
  }
4783
4380
  /**
4784
- * Waits for the page's readiness: the same moment the ftl:ready event is
4785
- * dispatched at, resolving immediately when that moment already passed.
4381
+ * Waits for this registry's readiness: resolves right after its ftl:ready
4382
+ * event is dispatched on the document, and immediately when that already
4383
+ * happened. Never rejects: a failed upgrade counts as settled, and its
4384
+ * rejection is left unhandled so it still reaches the console and the
4385
+ * page's error reporting.
4786
4386
  * @returns {Promise<void>}
4787
4387
  */
4788
4388
  ready() {
@@ -4795,11 +4395,16 @@ class Registry {
4795
4395
  * The scope every template on this registry renders in: the modules and the
4796
4396
  * data stack, as one value. Replaced whenever either is defined, so a holder
4797
4397
  * of an older one keeps rendering against what it was handed.
4398
+ * @returns {ExpressionEvaluator}
4798
4399
  */
4799
4400
  evaluator() {
4800
4401
  return this.#evaluator;
4801
4402
  }
4802
- /** The component registered under the name, undefined when none is. */
4403
+ /**
4404
+ * The component registered under the name.
4405
+ * @param {string} name
4406
+ * @returns {any} undefined when none is
4407
+ */
4803
4408
  component(name) {
4804
4409
  return this.#components[name];
4805
4410
  }
@@ -4807,33 +4412,59 @@ class Registry {
4807
4412
 
4808
4413
  const registry = new Registry();
4809
4414
 
4810
- /** The Template factories, each bound to the page registry's scope. */
4811
4415
  /** The Template factories bound to the page's own registry: the same four sources, with its modules and data already applied. */
4812
4416
  class Templates {
4417
+ /**
4418
+ * @param {string} html
4419
+ * @returns {Template}
4420
+ */
4813
4421
  static fromHtml(html) {
4814
4422
  return Template.fromHtml(html).withEvaluator(registry.evaluator());
4815
4423
  }
4424
+ /**
4425
+ * @param {string} selector for an HTMLTemplateElement
4426
+ * @returns {Template}
4427
+ */
4816
4428
  static fromSelector(selector) {
4817
4429
  return Template.fromSelector(selector).withEvaluator(registry.evaluator());
4818
4430
  }
4431
+ /**
4432
+ * @param {HTMLTemplateElement} templateEl consumed: its content is adopted
4433
+ * @returns {Template}
4434
+ */
4819
4435
  static fromTemplate(templateEl) {
4820
4436
  return Template.fromTemplate(templateEl).withEvaluator(registry.evaluator());
4821
4437
  }
4438
+ /**
4439
+ * @param {DocumentFragment} fragment
4440
+ * @returns {Template}
4441
+ */
4822
4442
  static fromFragment(fragment) {
4823
4443
  return Template.fromFragment(fragment).withEvaluator(registry.evaluator());
4824
4444
  }
4825
4445
  }
4826
4446
 
4827
4447
  /**
4828
- * Awaitable rendering barriers over the registry's upgrade queue. These are
4829
- * the rejecting waits: a failed upgrade among the awaited components rejects
4830
- * the wait, where registry.ready() only ever means the queue drained and
4831
- * leaves a failed component to its own unhandled-rejection report.
4448
+ * Waits for elements queued on the page's `registry` to render. Unlike
4449
+ * `registry.ready()`, these reject when an awaited upgrade fails. An element
4450
+ * defined by another Registry is awaited through that registry instead.
4832
4451
  */
4833
4452
  class Rendering {
4453
+ /**
4454
+ * Waits for the element and its descendants, the ones their own renders
4455
+ * enqueue included.
4456
+ * @param {Element} el
4457
+ * @returns {Promise<void>} rejecting with the first upgrade that failed
4458
+ */
4834
4459
  static waitFor(el) {
4835
4460
  return registry.settle((child) => el.contains(child));
4836
4461
  }
4462
+ /**
4463
+ * Waits for the element's descendants, the element itself excluded: what a
4464
+ * component awaits from inside its own render.
4465
+ * @param {Element} el
4466
+ * @returns {Promise<void>} rejecting with the first upgrade that failed
4467
+ */
4837
4468
  static waitForChildren(el) {
4838
4469
  return registry.settle((child) => el !== child && el.contains(child));
4839
4470
  }
@@ -4847,9 +4478,15 @@ class Rendering {
4847
4478
  * @property {(val: any, name: string, el: Element) => string|null} marshal
4848
4479
  */
4849
4480
 
4481
+ /**
4482
+ * The base of every fml element. A subclass declares its attributes, slots and
4483
+ * templates in statics and builds its dom in `render`; the base reads the light
4484
+ * slots, reads the declared attributes through their mappers, renders once, and
4485
+ * then forwards observed attribute writes to the matching properties. An element
4486
+ * resolves templates and components through the registry that defined it.
4487
+ */
4850
4488
  class ParsedElement extends HTMLElement {
4851
4489
  static BITS = {
4852
- //an element the registry never defined still answers the page's own
4853
4490
  registry,
4854
4491
  enqueue: (el) => {},
4855
4492
  SLOTS: false,
@@ -4873,14 +4510,12 @@ class ParsedElement extends HTMLElement {
4873
4510
  this.internals = this.attachInternals();
4874
4511
  }
4875
4512
  /**
4876
- * The platform's window into the element's own state. The base attaches it
4877
- * for every element, so a subclass never calls attachInternals itself: the
4878
- * platform allows it once, and a second call throws. Form association is a
4879
- * property of the definition rather than of who attached, so a subclass
4880
- * declaring `static formAssociated` still gets the form apis here.
4881
- *
4882
- * A subclass must not redeclare it: a class field with no initializer
4883
- * assigns undefined after super() returns, which would wipe it.
4513
+ * The element's ElementInternals, attached by the base for every element: a
4514
+ * subclass never calls attachInternals itself, since a second call throws.
4515
+ * A subclass declaring `static formAssociated` gets the form apis here.
4516
+ * A subclass must not redeclare the field: a class field without an
4517
+ * initializer assigns undefined after super() returns, wiping it.
4518
+ * @type {ElementInternals}
4884
4519
  */
4885
4520
  internals;
4886
4521
  #parsed = false;
@@ -4897,26 +4532,36 @@ class ParsedElement extends HTMLElement {
4897
4532
  #mapper(attr) {
4898
4533
  const mapper = this.#bits().ATTR_TO_MAPPER[attr];
4899
4534
  if (!mapper) {
4900
- //an attribute the class never declared has no type to read it as:
4901
- //say so rather than dying on the missing mapper. Content this
4902
- //element does not own, such as a custom loader's own configuration,
4903
- //is read with getAttribute, the platform's own answer
4904
4535
  throw new Error(
4905
4536
  `${this.constructor.name} declares no attribute '${attr}': declare it in static observed or static attributes, or read it with getAttribute`,
4906
4537
  );
4907
4538
  }
4908
4539
  return mapper;
4909
4540
  }
4541
+ /**
4542
+ * Reads an attribute value through the mapper it was declared with.
4543
+ * @param {string} attr
4544
+ * @param {string|null} str
4545
+ * @returns {any}
4546
+ * @throws when the class declares no such attribute; read undeclared ones with getAttribute
4547
+ */
4910
4548
  unmarshal(attr, str) {
4911
4549
  return this.#mapper(attr).unmarshal(str, attr, this);
4912
4550
  }
4551
+ /**
4552
+ * Writes a value as the attribute string its declared mapper produces.
4553
+ * @param {string} attr
4554
+ * @param {any} value
4555
+ * @returns {string|null} null for a value that removes the attribute
4556
+ * @throws when the class declares no such attribute
4557
+ */
4913
4558
  marshal(attr, value) {
4914
4559
  return this.#mapper(attr).marshal(value, attr, this);
4915
4560
  }
4916
4561
  /**
4917
- * @param {string} [name] - The name of the template target, defaults to 'default'
4562
+ * The registry that defined this element, the page's own for an undefined one.
4563
+ * @returns {typeof registry}
4918
4564
  */
4919
- /** The registry that defined this element, the page's own for an undefined one. */
4920
4565
  get _registry() {
4921
4566
  return this.#bits().registry;
4922
4567
  }
@@ -4924,10 +4569,18 @@ class ParsedElement extends HTMLElement {
4924
4569
  * The component registered under the name on this element's registry, the
4925
4570
  * one every ful loader and mapper resolves through.
4926
4571
  * @param {string} name
4572
+ * @returns {any} undefined when none is registered
4927
4573
  */
4928
4574
  component(name) {
4929
4575
  return this.#bits().registry.component(name);
4930
4576
  }
4577
+ /**
4578
+ * One of the class's templates, bound to its registry's scope with the
4579
+ * class's `static config` overlaid as `config`.
4580
+ * @param {string} [name] 'default', the `static template`, when omitted
4581
+ * @returns {Template}
4582
+ * @throws when the class declares no template of that name
4583
+ */
4931
4584
  template(name) {
4932
4585
  const target = this.#bits().TEMPLATES[name ?? 'default'];
4933
4586
  if (!target) {
@@ -4947,15 +4600,9 @@ class ParsedElement extends HTMLElement {
4947
4600
  if (oldValue === newValue) {
4948
4601
  return;
4949
4602
  }
4950
- //the mute is the attribute being reflected, not the element: a component
4951
- //that legitimately changes another observed attribute while one reflects
4952
- //is a change like any other, and used to be swallowed
4953
4603
  if (this.#reflecting.has(attr)) {
4954
4604
  return;
4955
4605
  }
4956
- //the properties go live only once the render is done: before that, an
4957
- //attribute write lands in the observed snapshot and the base applies it
4958
- //with the rest of the declared state
4959
4606
  if (!this.#parsed) {
4960
4607
  if (this.#pending !== null && attr in this.#pending) {
4961
4608
  this.#pending[attr] = this.unmarshal(attr, newValue);
@@ -4965,14 +4612,18 @@ class ParsedElement extends HTMLElement {
4965
4612
  this[this.#bits().ATTR_TO_PROPERTY[attr]] = this.unmarshal(attr, newValue);
4966
4613
  }
4967
4614
  /**
4968
- * Upgrades once: reads the declared attributes, keeps the observed half open
4969
- * to writes made while the render is pending, applies them to the properties
4970
- * when the render returns, and only then lets an attribute write forward.
4615
+ * Upgrades once, however often the element is connected: reads the declared
4616
+ * attributes, keeps the observed ones open to writes made while the render is
4617
+ * pending, renders, then applies the observed values to their properties in
4618
+ * the order the registry composed them, a base class's before a subclass's.
4619
+ * Only then do attribute writes forward to properties, and the element gains
4620
+ * the `rendered` custom state. A render that throws leaves the forwarding
4621
+ * shut and the state unset.
4971
4622
  *
4972
4623
  * An observed attribute drives the property the registry's `propertyOf`
4973
4624
  * names, so a hyphenated attribute is authored with its dashes and read as
4974
- * a camelCase property. A single-word attribute is its own property name,
4975
- * which is what every observed attribute in the library is.
4625
+ * a camelCase property.
4626
+ * @returns {Promise<void>} rejecting with what the render threw
4976
4627
  */
4977
4628
  async upgrade() {
4978
4629
  if (this.#started) {
@@ -4980,9 +4631,6 @@ class ParsedElement extends HTMLElement {
4980
4631
  }
4981
4632
  this.#started = true;
4982
4633
  const slots = this.#bits().SLOTS ? LightSlots.from(this) : undefined;
4983
- //both tiers are read here: the observed ones forward to a property once
4984
- //the render is done, the rest are the element's configuration, read the
4985
- //one time and never again
4986
4634
  const declared = Object.fromEntries(
4987
4635
  this.#bits().DECLARED.map((attribute) => [
4988
4636
  attribute,
@@ -4994,21 +4642,11 @@ class ParsedElement extends HTMLElement {
4994
4642
  this.#pending = declared;
4995
4643
  try {
4996
4644
  await this.render({ slots });
4997
- //the declared state reaches the properties once the dom the setters
4998
- //drive exists, in the order the registry composed the declarations:
4999
- //a base class's attributes before the subclass's own
5000
4645
  const properties = this.#bits().ATTR_TO_PROPERTY;
5001
4646
  for (const name of this.#bits().OBSERVED) {
5002
4647
  this[properties[name]] = declared[name];
5003
4648
  }
5004
- //the properties go live once the render is done: from here on, an
5005
- //attribute write forwards to the property. A render that threw
5006
- //leaves it shut, so a later attribute write cannot reach setters
5007
- //that assume pieces the failed render never adopted
5008
4649
  this.#parsed = true;
5009
- //the same moment, said to css. :defined is true from the constructor,
5010
- //which is before the dom exists, so a guard written against it reveals
5011
- //an element that has nothing in it yet
5012
4650
  this.internals.states.add('rendered');
5013
4651
  } finally {
5014
4652
  this.#pending = null;
@@ -5019,8 +4657,9 @@ class ParsedElement extends HTMLElement {
5019
4657
  * drive, and the base applies the declared state onto the properties as
5020
4658
  * soon as it returns. A render needing a declared value while it builds
5021
4659
  * reads it through `declared(name)`. The slots are undefined for an element
5022
- * declaring no slots.
5023
- * @param {{ slots: any }} c
4660
+ * declaring no slots. It may be async; the upgrade awaits it.
4661
+ * @param {{ slots: Record<string, DocumentFragment> | undefined }} c
4662
+ * @returns {void | Promise<void>}
5024
4663
  */
5025
4664
  render(c) {}
5026
4665
  /**
@@ -5032,12 +4671,9 @@ class ParsedElement extends HTMLElement {
5032
4671
  * observed name answers the snapshot while the render is pending, kept
5033
4672
  * open to attribute writes landing in that window, and the live attribute
5034
4673
  * afterwards, the property being live by then.
5035
- *
5036
- * The snapshot exists rather than a read of the dom because an element may
5037
- * write its own observed attributes while it renders, whether a reflection
5038
- * or a value the platform normalizes on the way in, and what the author
5039
- * declared is what the base applies, not what the render left behind.
5040
4674
  * @param {string} name
4675
+ * @returns {any}
4676
+ * @throws when the class declares no such attribute
5041
4677
  */
5042
4678
  declared(name) {
5043
4679
  if (name in this.#frozen) {
@@ -5048,15 +4684,18 @@ class ParsedElement extends HTMLElement {
5048
4684
  }
5049
4685
  return this.unmarshal(name, this.getAttribute(name));
5050
4686
  }
5051
- /** Whether the element's render completed: the moment its properties went live. */
4687
+ /**
4688
+ * Whether the element's render completed: the moment its properties went
4689
+ * live, which css reads as `:state(rendered)`. Unlike `:defined`, which holds
4690
+ * from the constructor, it is false until the dom exists.
4691
+ * @returns {boolean}
4692
+ */
5052
4693
  get rendered() {
5053
4694
  return this.#parsed;
5054
4695
  }
5055
4696
  /**
5056
4697
  * Projects a property back onto its observed attribute, marshalled through
5057
- * the mapper the attribute was declared with: the one place a property
5058
- * reaches its attribute, so a setter never has to know how its own type
5059
- * serializes.
4698
+ * the mapper the attribute was declared with.
5060
4699
  *
5061
4700
  * A value the attribute already carries is not written at all, so reflecting
5062
4701
  * what an attribute write just delivered ends there rather than looping, and
@@ -5096,9 +4735,13 @@ class ParsedElement extends HTMLElement {
5096
4735
  const PLACEHOLDER = /\{(\w+)\}/g;
5097
4736
  const POSITIONAL = /\{(\d+)\}/g;
5098
4737
  /** @param {any} v @returns {v is Record<string, string>} */
5099
- const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
4738
+ const isPlainObject = (v) =>
4739
+ typeof v === 'object' && v !== null && [Object.prototype, null].includes(Object.getPrototypeOf(v));
4740
+
4741
+ /** @param {any} problem @returns {string|undefined} the reason, unless it is missing or blank */
4742
+ const reasonOf = (problem) =>
4743
+ typeof problem?.reason === 'string' && problem.reason.trim() !== '' ? problem.reason : undefined;
5100
4744
 
5101
- /** every l10n problem is reported once per session: a missing key in a row template must not flood the console */
5102
4745
  const warned = new Set();
5103
4746
  const warnOnce = (problem) => {
5104
4747
  if (!warned.has(problem)) {
@@ -5109,11 +4752,7 @@ const warnOnce = (problem) => {
5109
4752
 
5110
4753
  const formatters = new BoundedCache(100);
5111
4754
  /**
5112
- * Intl construction is the expensive part of formatting (locale parsing, CLDR
5113
- * lookups), while formatting on a built instance is near free: a list rendering
5114
- * a size per row must not rebuild a NumberFormat per cell, so instances are
5115
- * memoized by constructor, locale and options.
5116
- *
4755
+ * An Intl instance, memoized by constructor, locale and options.
5117
4756
  * @param {{ new (locale: string | undefined, options: any): any }} ctor
5118
4757
  * @param {string | undefined} locale
5119
4758
  * @param {any} [options]
@@ -5132,26 +4771,29 @@ const formatter = (ctor, locale, options) => {
5132
4771
  class Localization {
5133
4772
  /**
5134
4773
  * Resolves a message from the translations and interpolates its arguments.
5135
- * A single plain object argument interpolates named placeholders
5136
- * ({name}); anything else interpolates positional ones ({0}).
4774
+ * A single plain object argument (an object literal, or one with a null
4775
+ * prototype) interpolates named placeholders ({name}); anything else interpolates positional ones ({0}).
5137
4776
  * A plural leaf selects its form through Intl.PluralRules over the
5138
4777
  * numeric {count} named argument.
5139
4778
  *
5140
4779
  * @param {string} key
5141
4780
  * @param {...any} args
4781
+ * Each problem (a missing key, placeholder or plural form) is warned once
4782
+ * per page.
4783
+ *
5142
4784
  * @this {Receiver}
5143
4785
  * @returns {string} the message, or the key itself when the translations do not carry it
5144
4786
  */
5145
4787
  static t(key, ...args) {
5146
4788
  const messages = this.l10n ?? {};
5147
- let message = messages[key];
4789
+ let message = Object.hasOwn(messages, key) ? messages[key] : undefined;
5148
4790
  if (message === undefined) {
5149
4791
  warnOnce(`missing message "${key}"`);
5150
4792
  return key;
5151
4793
  }
4794
+ const named = args.length === 1 && isPlainObject(args[0]) ? args[0] : null;
5152
4795
  if (isPlainObject(message)) {
5153
- const named = args.length === 1 && isPlainObject(args[0]) ? args[0] : {};
5154
- if (typeof named.count !== 'number') {
4796
+ if (typeof named?.count !== 'number') {
5155
4797
  warnOnce(`plural message "${key}" needs a numeric {count}`);
5156
4798
  message = message.other;
5157
4799
  } else {
@@ -5164,10 +4806,9 @@ class Localization {
5164
4806
  warnOnce(`plural message "${key}" has no "other" form`);
5165
4807
  return key;
5166
4808
  }
5167
- if (args.length === 1 && isPlainObject(args[0])) {
5168
- const named = args[0];
4809
+ if (named) {
5169
4810
  return message.replace(PLACEHOLDER, (literal, name) => {
5170
- if (!(name in named)) {
4811
+ if (!Object.hasOwn(named, name)) {
5171
4812
  warnOnce(`message "${key}" wants {${name}}`);
5172
4813
  return literal;
5173
4814
  }
@@ -5193,6 +4834,7 @@ class Localization {
5193
4834
  * @param {Date | number} value
5194
4835
  * @param {Intl.DateTimeFormatOptions} [options]
5195
4836
  * @this {Receiver}
4837
+ * @returns {string}
5196
4838
  */
5197
4839
  static date(value, options) {
5198
4840
  return formatter(Intl.DateTimeFormat, this.locale, options).format(value);
@@ -5204,6 +4846,7 @@ class Localization {
5204
4846
  * @param {number} value
5205
4847
  * @param {Intl.NumberFormatOptions} [options]
5206
4848
  * @this {Receiver}
4849
+ * @returns {string}
5207
4850
  */
5208
4851
  static number(value, options) {
5209
4852
  return formatter(Intl.NumberFormat, this.locale, options).format(value);
@@ -5216,6 +4859,7 @@ class Localization {
5216
4859
  *
5217
4860
  * @param {number} value
5218
4861
  * @this {Receiver}
4862
+ * @returns {string}
5219
4863
  */
5220
4864
  static bytes(value) {
5221
4865
  const format = formatter(Intl.NumberFormat, this.locale, { maximumFractionDigits: 2 }).format;
@@ -5231,16 +4875,37 @@ class Localization {
5231
4875
  return `${format(value)}B`;
5232
4876
  }
5233
4877
 
4878
+ /**
4879
+ * What went wrong, as text for the reader. A value carrying a non-empty
4880
+ * `problems` array reads as their reasons, one per line, a blank or missing
4881
+ * reason reading as the `failure.no-reason` message. Any other value with a
4882
+ * `message` reads as that message; anything else as its string form, and
4883
+ * null or undefined as the empty string.
4884
+ *
4885
+ * @param {any} value what was thrown or rejected with
4886
+ * @this {Receiver}
4887
+ * @returns {string}
4888
+ */
4889
+ static failure(value) {
4890
+ if (Array.isArray(value?.problems) && value.problems.length > 0) {
4891
+ return value.problems.map((p) => reasonOf(p) ?? Localization.t.call(this, 'failure.no-reason')).join('\n');
4892
+ }
4893
+ if (value?.message !== undefined) {
4894
+ return String(value.message);
4895
+ }
4896
+ return value == null ? '' : String(value);
4897
+ }
4898
+
5234
4899
  /**
5235
4900
  * An imperative facade over the module functions, resolving the translations
5236
- * and the locale from the registry overlays on every call.
4901
+ * and the locale from the registry overlays on every call, the same lookup a
4902
+ * template makes: a facade taken at module scope, before the plugin
4903
+ * configures, still sees the translations.
5237
4904
  *
5238
4905
  * @param {{ locale?: string }} [overrides] an explicit locale, winning over the registry one
4906
+ * @returns {{ t: (key: string, ...args: any[]) => string, date: (value: Date | number, options?: Intl.DateTimeFormatOptions) => string, number: (value: number, options?: Intl.NumberFormatOptions) => string, bytes: (value: number) => string, failure: (value: any) => string }}
5239
4907
  */
5240
4908
  static of(overrides = {}) {
5241
- //resolved per call, not per facade: a module-scope `Localization.of()` is
5242
- //bound before the plugin configures. The registry's own evaluator does the
5243
- //lookup, so this cannot drift from what a template sees
5244
4909
  const resolve = (prop) => registry.evaluator().resolve(prop);
5245
4910
  /** @param {any} fn */
5246
4911
  const bind =
@@ -5252,6 +4917,7 @@ class Localization {
5252
4917
  date: bind(Localization.date),
5253
4918
  number: bind(Localization.number),
5254
4919
  bytes: bind(Localization.bytes),
4920
+ failure: bind(Localization.failure),
5255
4921
  };
5256
4922
  }
5257
4923
  }