@optionfactory/fml 9.0.0-rc3 → 9.0.0-rc31

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 +491 -51
  7. package/dist/fml.css +25 -20
  8. package/dist/fml.css.map +1 -1
  9. package/dist/fml.iife.js +5866 -2563
  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 +425 -231
  14. package/dist/ftl.iife.js +774 -1044
  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 +774 -1044
  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 +3048 -556
  25. package/dist/ful.iife.js +4977 -1395
  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 +4975 -1396
  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 +239 -39
  43. package/dist/web-types.json +498 -51
  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,134 +2215,71 @@ 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;
2508
2222
 
2509
2223
  return cached.result;
2510
2224
  }
2511
-
2512
- peg$silentFails++;
2513
- s0 = peg$currPos;
2514
- 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) {
2544
- 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);
2555
- peg$currPos++;
2556
- } else {
2557
- 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;
2565
- }
2566
- } else {
2567
- peg$currPos = s2;
2568
- s2 = peg$FAILED;
2569
- }
2570
- }
2571
- if (s2 !== peg$FAILED) {
2572
- while (s2 !== peg$FAILED) {
2573
- s1.push(s2);
2574
- s2 = peg$currPos;
2575
- s3 = [];
2225
+
2226
+ peg$silentFails++;
2227
+ s0 = peg$currPos;
2228
+ s1 = [];
2229
+ for (;;) {
2230
+ s2 = peg$currPos;
2231
+ s3 = [];
2232
+ for (;;) {
2576
2233
  s4 = input.charAt(peg$currPos);
2577
- if (peg$r4.test(s4)) {
2234
+ if (s4 !== "" && !(peg$charCode = s4.charCodeAt(0), peg$charCode === 92 || peg$charCode === 96 || peg$charCode === 123)) {
2578
2235
  peg$currPos++;
2579
2236
  } else {
2580
2237
  s4 = peg$FAILED;
2581
2238
  if (peg$silentFails === 0) { peg$fail(peg$e40); }
2582
2239
  }
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
- }
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);
2248
+ } else {
2249
+ s2 = s3;
2250
+ }
2251
+ if (s2 === peg$FAILED) {
2252
+ s2 = peg$currPos;
2253
+ if (input.charCodeAt(peg$currPos) === 92) {
2254
+ s3 = peg$c7;
2255
+ peg$currPos++;
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') {
@@ -3270,6 +2847,8 @@ const resolveInStack = (dataStack, prop) => {
3270
2847
  return undefined;
3271
2848
  };
3272
2849
 
2850
+ const BOUND = Symbol('BOUND');
2851
+
3273
2852
  /**
3274
2853
  * Evaluates a parsed expression against a scope. One instance per evaluation:
3275
2854
  * it holds the modules and the data stack that names resolve against, and keeps
@@ -3278,9 +2857,11 @@ const resolveInStack = (dataStack, prop) => {
3278
2857
  class EvaluatingVisitor {
3279
2858
  #modules;
3280
2859
  #dataStack;
3281
- constructor(modules, dataStack) {
2860
+ #bound;
2861
+ constructor(modules, dataStack, bound) {
3282
2862
  this.#modules = modules;
3283
2863
  this.#dataStack = dataStack;
2864
+ this.#bound = bound ?? dataStack.length;
3284
2865
  }
3285
2866
  #resolve(prop) {
3286
2867
  return resolveInStack(this.#dataStack, prop);
@@ -3304,8 +2885,9 @@ class EvaluatingVisitor {
3304
2885
  #cached_resolve_proxy;
3305
2886
  #resolve_proxy() {
3306
2887
  if (!this.#cached_resolve_proxy) {
2888
+ const bound = (name) => resolveInStack(this.#dataStack.slice(0, this.#bound), name);
3307
2889
  this.#cached_resolve_proxy = new Proxy(this.#dataStack, {
3308
- get: (target, prop) => this.#resolve(prop),
2890
+ get: (target, prop) => (prop === BOUND ? bound : this.#resolve(prop)),
3309
2891
  });
3310
2892
  }
3311
2893
  return this.#cached_resolve_proxy;
@@ -3317,8 +2899,7 @@ class EvaluatingVisitor {
3317
2899
  return this.visit(node.lhs) || this.visit(node.rhs);
3318
2900
  }
3319
2901
  [nodes.nullc](node) {
3320
- const lhs = this.visit(node.lhs);
3321
- return lhs !== null && lhs !== undefined ? lhs : this.visit(node.rhs);
2902
+ return this.visit(node.lhs) ?? this.visit(node.rhs);
3322
2903
  }
3323
2904
  [nodes.not](node) {
3324
2905
  return !this.visit(node.expr);
@@ -3385,8 +2966,7 @@ class EvaluatingVisitor {
3385
2966
  return this.visit(node.cond) ? this.visit(node.ifTrue) : this.visit(node.ifFalse);
3386
2967
  }
3387
2968
  [nodes.elv](node) {
3388
- const cond = this.visit(node.cond);
3389
- return cond ? cond : this.visit(node.ifFalse);
2969
+ return this.visit(node.cond) || this.visit(node.ifFalse);
3390
2970
  }
3391
2971
  [nodes.access](node) {
3392
2972
  let prev;
@@ -3452,16 +3032,33 @@ class EvaluatingVisitor {
3452
3032
  * spliced into the expression text.
3453
3033
  */
3454
3034
  class Expressions {
3035
+ /** Parses the whole text as one expression: `user.name`. */
3455
3036
  static MODE_EXPRESSION = Symbol('MODE_EXPRESSION');
3037
+ /**
3038
+ * Parses text with interpolations, `Hello {{ user.name }}`, evaluating to
3039
+ * its parts: `{ type, value }` for each literal run and each `{{ }}` (text),
3040
+ * `{{{ }}}` (html) or `{{{{ }}}}` (node) interpolation, in order.
3041
+ */
3456
3042
  static MODE_TEMPLATED = Symbol('MODE_TEMPLATED');
3043
+ /**
3044
+ * The key under which the receiver of a module function answers a lookup
3045
+ * against the bound data alone: `this[Expressions.BOUND](name)` resolves
3046
+ * the name in the layers the scope was created with (a registry's data,
3047
+ * the data handed to a Template factory) and never in an overlay pushed
3048
+ * afterwards, by `data-tpl-each`, `data-tpl-with` or `withOverlay`. It is
3049
+ * how a module reads what the page configured without the rendered data
3050
+ * being able to supply or replace it. No name on the data stack reaches it.
3051
+ */
3052
+ static BOUND = BOUND;
3457
3053
 
3458
3054
  static #astCache = new BoundedCache(1000);
3459
3055
 
3460
3056
  /**
3461
- * Parses an expression.
3057
+ * Parses an expression, caching the ast by mode and text.
3462
3058
  * @param {string} expression
3463
- * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
3464
- * @returns the ast
3059
+ * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] MODE_EXPRESSION when omitted
3060
+ * @returns {any} the ast
3061
+ * @throws a peggy syntax error when the text does not parse
3465
3062
  */
3466
3063
  static parse(expression, mode) {
3467
3064
  const key = mode?.toString() + expression;
@@ -3472,26 +3069,28 @@ class Expressions {
3472
3069
  );
3473
3070
  }
3474
3071
  /**
3475
- * Evaluates an expression.
3072
+ * Evaluates a parsed ast against the modules and the data stack.
3476
3073
  * @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
3074
+ * @param {any[]} dataStack innermost overlay last
3075
+ * @param {any} ast what `parse` answered
3076
+ * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] the mode it was parsed in
3077
+ * @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
3078
+ * @returns {any} the value, or the list of parts in MODE_TEMPLATED
3481
3079
  */
3482
- static evaluate(modules, dataStack, ast, mode) {
3483
- return new EvaluatingVisitor(modules, dataStack).visitRoot(ast, mode === Expressions.MODE_TEMPLATED);
3080
+ static evaluate(modules, dataStack, ast, mode, bound) {
3081
+ return new EvaluatingVisitor(modules, dataStack, bound).visitRoot(ast, mode === Expressions.MODE_TEMPLATED);
3484
3082
  }
3485
3083
  /**
3486
3084
  * Parses and evaluates an expression.
3487
3085
  * @param {{ [x: string]: any; } | null | undefined} modules
3488
- * @param {any[]} dataStack
3086
+ * @param {any[]} dataStack innermost overlay last
3489
3087
  * @param {string} expression
3490
- * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
3491
- * @returns the result
3088
+ * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] MODE_EXPRESSION when omitted
3089
+ * @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
3090
+ * @returns {any} the value, or the list of parts in MODE_TEMPLATED
3492
3091
  */
3493
- static interpret(modules, dataStack, expression, mode) {
3494
- return Expressions.evaluate(modules, dataStack, Expressions.parse(expression, mode), mode);
3092
+ static interpret(modules, dataStack, expression, mode, bound) {
3093
+ return Expressions.evaluate(modules, dataStack, Expressions.parse(expression, mode), mode, bound);
3495
3094
  }
3496
3095
  }
3497
3096
 
@@ -3500,48 +3099,89 @@ class Expressions {
3500
3099
  * that a bare identifier resolves against, as a single value. Adding an overlay
3501
3100
  * returns a new evaluator instead of modifying this one, so a template can add
3502
3101
  * data for one subtree without affecting the rest of the render.
3102
+ *
3103
+ * The layers an evaluator is created with are its bound data; the overlays
3104
+ * pushed afterwards are not. A bare identifier resolves against both, while a
3105
+ * module function can ask for the bound data alone through `Expressions.BOUND`.
3503
3106
  */
3504
3107
  class ExpressionEvaluator {
3505
3108
  #modules;
3506
3109
  #dataStack;
3507
- constructor(modules, dataStack) {
3110
+ #bound;
3111
+ /**
3112
+ * @param {{ [k: string]: any } | null | undefined} modules
3113
+ * @param {any[]} dataStack innermost overlay last
3114
+ * @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
3115
+ */
3116
+ constructor(modules, dataStack, bound) {
3508
3117
  this.#modules = modules;
3509
3118
  this.#dataStack = dataStack;
3119
+ this.#bound = bound ?? dataStack.length;
3510
3120
  }
3121
+ /**
3122
+ * A new evaluator with one more module: `#name:fn()` resolves into it, and an
3123
+ * empty name merges a whole map whose functions resolve bare, as `#fn()`.
3124
+ * @param {string} name
3125
+ * @param {object} value
3126
+ * @returns {ExpressionEvaluator}
3127
+ */
3511
3128
  withModule(name, value) {
3512
3129
  const module = name ? { [name]: value } : value;
3513
- return new ExpressionEvaluator({ ...this.#modules, ...module }, this.#dataStack);
3130
+ return new ExpressionEvaluator({ ...this.#modules, ...module }, this.#dataStack, this.#bound);
3514
3131
  }
3132
+ /**
3133
+ * A new evaluator with the data pushed as the innermost overlays, the last
3134
+ * one winning a shared name. The bound data stays what it was.
3135
+ * @param {...any} data
3136
+ * @returns {ExpressionEvaluator}
3137
+ */
3515
3138
  withOverlay(...data) {
3516
3139
  return new ExpressionEvaluator(
3517
3140
  this.#modules,
3518
3141
  data.length === 0 ? this.#dataStack : [...this.#dataStack, ...data],
3142
+ this.#bound,
3519
3143
  );
3520
3144
  }
3521
3145
  /**
3522
3146
  * Resolves a name against the data stack, with no parse round trip: the
3523
3147
  * lookup a template's bare identifier makes, for an imperative caller.
3524
3148
  * @param {string} name
3149
+ * @returns {any} undefined when no overlay carries the name
3525
3150
  */
3526
3151
  resolve(name) {
3527
3152
  return resolveInStack(this.#dataStack, name);
3528
3153
  }
3529
- /** Evaluates an expression against this scope. */
3154
+ /**
3155
+ * Evaluates an expression against this scope.
3156
+ * @param {string} expression
3157
+ * @returns {any}
3158
+ */
3530
3159
  evaluateExpression(expression) {
3531
- return Expressions.interpret(this.#modules, this.#dataStack, expression, Expressions.MODE_EXPRESSION);
3160
+ return Expressions.interpret(
3161
+ this.#modules,
3162
+ this.#dataStack,
3163
+ expression,
3164
+ Expressions.MODE_EXPRESSION,
3165
+ this.#bound,
3166
+ );
3532
3167
  }
3533
- /** Evaluates a templated text, the `{{ }}` form, against this scope. */
3168
+ /**
3169
+ * Evaluates a templated text, the `{{ }}` form, against this scope.
3170
+ * @param {string} text
3171
+ * @returns {{ type: symbol, value: any }[]} its parts, as MODE_TEMPLATED describes
3172
+ */
3534
3173
  evaluateTemplated(text) {
3535
- return Expressions.interpret(this.#modules, this.#dataStack, text, Expressions.MODE_TEMPLATED);
3174
+ return Expressions.interpret(this.#modules, this.#dataStack, text, Expressions.MODE_TEMPLATED, this.#bound);
3536
3175
  }
3537
3176
  }
3538
3177
 
3539
- /** Creates and inspects the DocumentFragments a Template renders into. */
3178
+ /** Creates, serializes and inspects DocumentFragments. */
3540
3179
  class Fragments {
3541
3180
  /**
3542
- * Creates a DocumentFragment from an string.
3543
- * @param {...string} html
3544
- * @returns {DocumentFragment} the fragment
3181
+ * Parses html into a DocumentFragment adopted by the document, each piece
3182
+ * trimmed and the pieces joined.
3183
+ * @param {...string} html
3184
+ * @returns {DocumentFragment}
3545
3185
  */
3546
3186
  static fromHtml(...html) {
3547
3187
  const el = document.createElement('template');
@@ -3549,11 +3189,11 @@ class Fragments {
3549
3189
  return document.adoptNode(el.content);
3550
3190
  }
3551
3191
  /**
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
3192
+ * Serializes a fragment, or a single node, to html, consuming it: the nodes
3193
+ * are moved out, not copied, and a fragment is left empty. Pass
3554
3194
  * `fragment.cloneNode(true)` to keep the original usable.
3555
- * @param {DocumentFragment} fragment
3556
- * @returns {string} the html
3195
+ * @param {DocumentFragment|Node} fragment
3196
+ * @returns {string}
3557
3197
  */
3558
3198
  static toHtml(fragment) {
3559
3199
  var el = document.createElement('template');
@@ -3561,17 +3201,17 @@ class Fragments {
3561
3201
  return el.innerHTML;
3562
3202
  }
3563
3203
  /**
3564
- * Checks if a fragment contains only blank text nodes
3204
+ * Whether a fragment holds no element and no text but whitespace.
3565
3205
  * @param {DocumentFragment} fragment
3566
- * @returns {boolean} true if the fragment is blank
3206
+ * @returns {boolean}
3567
3207
  */
3568
3208
  static isBlank(fragment) {
3569
3209
  return fragment.childElementCount === 0 && fragment.textContent.trim() === '';
3570
3210
  }
3571
3211
  /**
3572
- * Creates a DocumentFragment from nodes.
3573
- * @param {...Node} nodes
3574
- * @returns {DocumentFragment} the fragment
3212
+ * Moves the nodes, in order, into a new DocumentFragment.
3213
+ * @param {...Node} nodes
3214
+ * @returns {DocumentFragment}
3575
3215
  */
3576
3216
  static from(...nodes) {
3577
3217
  const fragment = new DocumentFragment();
@@ -3581,9 +3221,10 @@ class Fragments {
3581
3221
  return fragment;
3582
3222
  }
3583
3223
  /**
3584
- * Creates a DocumentFragment from childNodes of an element.
3224
+ * Moves the children of a node into a new DocumentFragment, leaving the
3225
+ * node empty.
3585
3226
  * @param {Node} el
3586
- * @returns {DocumentFragment} the fragment
3227
+ * @returns {DocumentFragment}
3587
3228
  */
3588
3229
  static fromChildNodes(el) {
3589
3230
  const fragment = new DocumentFragment();
@@ -3594,23 +3235,23 @@ class Fragments {
3594
3235
  }
3595
3236
  }
3596
3237
 
3597
- /** Attribute reads and writes where a nullish value removes the attribute instead of setting it to the string 'null'. */
3238
+ /** Attribute helpers: unique ids, defaults, prefixed forwarding, and writes where a nullish value removes the attribute. */
3598
3239
  class Attributes {
3599
3240
  static id = 0;
3600
3241
  /**
3601
- * Creates a unique id with the given prefix.
3242
+ * A page-unique id under the prefix, as `prefix-n`.
3602
3243
  * @param {string} prefix
3603
- * @returns
3244
+ * @returns {string}
3604
3245
  */
3605
3246
  static uid(prefix) {
3606
3247
  return `${prefix}-${++Attributes.id}`;
3607
3248
  }
3608
3249
  /**
3609
- * Sets an attribute if not present.
3250
+ * Sets an attribute when the element does not carry it.
3610
3251
  * @param {Element} el
3611
3252
  * @param {string} k
3612
3253
  * @param {string} v
3613
- * @returns
3254
+ * @returns {string|null} the attribute's value afterwards
3614
3255
  */
3615
3256
  static defaultValue(el, k, v) {
3616
3257
  if (!el.hasAttribute(k)) {
@@ -3619,7 +3260,9 @@ class Attributes {
3619
3260
  return el.getAttribute(k);
3620
3261
  }
3621
3262
  /**
3622
- * Forwards prefixed attributes from an element to another (removing the prefix).
3263
+ * Copies every attribute whose name starts with the prefix onto another
3264
+ * element, under the name without the prefix. `<prefix>class` adds its
3265
+ * classes to the target's instead of replacing them.
3623
3266
  * @param {string} prefix
3624
3267
  * @param {Element} from
3625
3268
  * @param {Element} to
@@ -3642,7 +3285,7 @@ class Attributes {
3642
3285
  });
3643
3286
  }
3644
3287
  /**
3645
- * Sets the value of an attribute. nullish values remove the attribute.
3288
+ * Sets an attribute, or removes it when the value is nullish.
3646
3289
  * @param {Element} el
3647
3290
  * @param {string} attr
3648
3291
  * @param {string | null | undefined} value
@@ -3664,13 +3307,14 @@ class Attributes {
3664
3307
  */
3665
3308
  class LightSlots {
3666
3309
  /**
3667
- * Extracts light slots from an element. For non default slots in a template tag, the content is extracted.
3310
+ * Takes an element's children apart into slots, leaving the element empty.
3311
+ * A named child is removed and contributes what `slotFromNode` answers;
3312
+ * children sharing a name are gathered in order. A child with an empty
3313
+ * `slot` belongs to the default slot, in document order among the rest.
3668
3314
  * @param {Element} el
3669
- * @returns the slots
3315
+ * @returns {Record<string, DocumentFragment>} the slots by name, `default` always present
3670
3316
  */
3671
3317
  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
3318
  for (const child of el.children) {
3675
3319
  if (child.matches('[slot=""]')) {
3676
3320
  child.removeAttribute('slot');
@@ -3685,7 +3329,7 @@ class LightSlots {
3685
3329
  el.removeAttribute('slot');
3686
3330
  return [slot, LightSlots.slotFromNode(el)];
3687
3331
  });
3688
- const slots = {};
3332
+ const slots = Object.create(null);
3689
3333
  slots.default = new DocumentFragment();
3690
3334
  slots.default.append(...el.childNodes);
3691
3335
  for (const [name, el] of namedSlots) {
@@ -3696,6 +3340,12 @@ class LightSlots {
3696
3340
  }
3697
3341
  return slots;
3698
3342
  }
3343
+ /**
3344
+ * What a slotted node contributes: a template's content, the parsed markup
3345
+ * of a `<script type="text/html">`, or the node itself.
3346
+ * @param {Element} el
3347
+ * @returns {Element|DocumentFragment}
3348
+ */
3699
3349
  static slotFromNode(el) {
3700
3350
  if (el instanceof HTMLTemplateElement) {
3701
3351
  return document.adoptNode(el.content);
@@ -3707,12 +3357,13 @@ class LightSlots {
3707
3357
  }
3708
3358
  }
3709
3359
 
3710
- /** Waits for the parser and for the document, for elements that upgrade before their own markup is complete. */
3360
+ /** Waits on the parser and the document, and queries an element's own children. */
3711
3361
  class Nodes {
3712
3362
  /**
3713
- * Checks if an element is already parsed.
3363
+ * Whether the parser has moved past the element: the element, or one of
3364
+ * its ancestors, has a next sibling.
3714
3365
  * @param {Element} el
3715
- * @returns
3366
+ * @returns {boolean}
3716
3367
  */
3717
3368
  static isParsed(el) {
3718
3369
  for (let c = /** @type {Node | null} */ (el); c; c = c.parentNode) {
@@ -3724,13 +3375,12 @@ class Nodes {
3724
3375
  }
3725
3376
 
3726
3377
  /**
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.
3378
+ * Waits for the document's DOMContentLoaded. Resolves at once when that
3379
+ * moment already passed, and for a document with no view, which receives
3380
+ * no events. Never resolves early for a document still running its
3381
+ * deferred scripts.
3382
+ * @param {Document} doc
3383
+ * @returns {Promise<void>}
3734
3384
  */
3735
3385
  static waitDomContentLoaded(doc) {
3736
3386
  if (doc.readyState === 'loading') {
@@ -3743,7 +3393,13 @@ class Nodes {
3743
3393
  }
3744
3394
  const win = doc.defaultView;
3745
3395
  if (win === null) {
3746
- //a viewless document can receive no event at all
3396
+ return Promise.resolve();
3397
+ }
3398
+ // interactive spans both the deferred scripts and the time between DOMContentLoaded and load
3399
+ const [navigation] = /** @type {PerformanceNavigationTiming[]} */ (
3400
+ win.performance?.getEntriesByType('navigation') ?? []
3401
+ );
3402
+ if (navigation?.domContentLoadedEventStart > 0) {
3747
3403
  return Promise.resolve();
3748
3404
  }
3749
3405
  return new Promise((resolve) => {
@@ -3762,22 +3418,12 @@ class Nodes {
3762
3418
  * resolves once the element, or any of its ancestors, gains a next sibling,
3763
3419
  * which is the parser having moved past this subtree. The document's
3764
3420
  * 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>}
3421
+ * that is already parsed, or once the parser has finished the document.
3422
+ * @param {Element} el
3423
+ * @returns {Promise<Element>} the element
3778
3424
  */
3779
3425
  static waitParsed(el) {
3780
- if (Nodes.isParsed(el)) {
3426
+ if (Nodes.isParsed(el) || el.ownerDocument.readyState !== 'loading') {
3781
3427
  return Promise.resolve(el);
3782
3428
  }
3783
3429
  return new Promise((resolve) => {
@@ -3799,10 +3445,10 @@ class Nodes {
3799
3445
  }
3800
3446
 
3801
3447
  /**
3802
- * Returns the first child of the element element (if exists) matching the selector.
3803
- * @param {Element} el
3448
+ * The first child element matching the selector, descendants excluded.
3449
+ * @param {Element|DocumentFragment} el
3804
3450
  * @param {string} selector
3805
- * @returns
3451
+ * @returns {Element|null}
3806
3452
  */
3807
3453
  static queryChildren(el, selector) {
3808
3454
  for (const c of el.children) {
@@ -3813,10 +3459,10 @@ class Nodes {
3813
3459
  return null;
3814
3460
  }
3815
3461
  /**
3816
- * Returns all children of the element matching the selector.
3817
- * @param {Element} el
3462
+ * Every child element matching the selector, descendants excluded.
3463
+ * @param {Element|DocumentFragment} el
3818
3464
  * @param {string} selector
3819
- * @returns
3465
+ * @returns {Element[]}
3820
3466
  */
3821
3467
  static queryChildrenAll(el, selector) {
3822
3468
  const r = [];
@@ -3873,9 +3519,11 @@ class NodeOperations {
3873
3519
 
3874
3520
  /** The `data-tpl-*` commands, each taking the node it is written on and the scope it evaluates in. */
3875
3521
  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)
3522
+ /**
3523
+ * The order the commands run in on one element, each seeing the node as the
3524
+ * ones before left it: tplIf gates before tplWith and tplEach push their
3525
+ * overlay, tplWhen gates after, inside the scope that overlay opened.
3526
+ */
3879
3527
  static ORDERED_COMMANDS = [
3880
3528
  'tplIf',
3881
3529
  'tplWith',
@@ -3886,8 +3534,7 @@ class CommandsHandler {
3886
3534
  'tplRemove',
3887
3535
  'tplVerbatim',
3888
3536
  ];
3889
- //same body as tplWhen: the two differ only in ORDERED_COMMANDS position,
3890
- //if evaluates in the outer scope, before with/each
3537
+ /** Removes the node when the expression is falsy, evaluated before tplWith and tplEach open their overlay. */
3891
3538
  static tplIf(node, expression, ops, evaluator) {
3892
3539
  const accept = evaluator.evaluateExpression(expression);
3893
3540
  if (!accept) {
@@ -3902,49 +3549,37 @@ class CommandsHandler {
3902
3549
  .render();
3903
3550
  ops.replace(node, newNode);
3904
3551
  }
3552
+ /**
3553
+ * Renders the node once per entry, in place of the node. A Map iterates as
3554
+ * `{ key, value }` entries in its own order, a plain object as `{ key, value }`
3555
+ * entries in `Object.entries` order, and any other iterable as itself; a
3556
+ * value that is none of these throws. Each render sees its entry under
3557
+ * `data-tpl-var`, or as the whole overlay without one. `data-tpl-stat` names
3558
+ * an overlay beneath the entry carrying `index`, `count`, `size`, `first`,
3559
+ * `last`, `even` and `odd`, so an entry property of the same name wins; the
3560
+ * size, and so `last`, is null for a collection that knows neither its
3561
+ * `length` nor its `size`, which is never consumed to learn it.
3562
+ */
3905
3563
  static tplEach(node, expression, ops, evaluator) {
3906
3564
  const varName = ops.popData(node, 'tplVar');
3907
3565
  const statName = ops.popData(node, 'tplStat');
3908
3566
  const template = new Template(node, evaluator);
3909
3567
  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;
3568
+ const proto =
3569
+ evaluated === null || typeof evaluated !== 'object' ? undefined : Object.getPrototypeOf(evaluated);
3570
+ const plain = !evaluated?.[Symbol.iterator] && (proto === Object.prototype || proto === null);
3571
+ const dict = plain ? Object.entries(evaluated) : null;
3572
+ const keyed = evaluated instanceof Map ? [...evaluated] : dict;
3922
3573
  const entries = keyed === null ? evaluated : keyed.map(([key, value]) => ({ key, value }));
3923
3574
  if (!entries?.[Symbol.iterator]) {
3924
3575
  throw new Error(`Expected an iterable got '${evaluated}'`);
3925
3576
  }
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;
3577
+ const measured = statName ? entries : null;
3578
+ const counted = typeof measured?.length === 'number' ? measured.length : measured?.size;
3579
+ const size = typeof counted === 'number' ? counted : null;
3946
3580
  let index = 0;
3947
3581
  for (const v of entries) {
3582
+ const item = varName ? { [varName]: v } : v;
3948
3583
  const stat = {
3949
3584
  index,
3950
3585
  count: index + 1,
@@ -3954,16 +3589,13 @@ class CommandsHandler {
3954
3589
  even: index % 2 === 0,
3955
3590
  odd: index % 2 === 1,
3956
3591
  };
3957
- ops.prepend(
3958
- node,
3959
- template.withOverlay({ [statName]: stat }, varName ? { [varName]: v } : v).render(),
3960
- );
3592
+ const layers = statName ? [{ [statName]: stat }, item] : [item];
3593
+ ops.prepend(node, template.withOverlay(...layers).render());
3961
3594
  ++index;
3962
3595
  }
3963
3596
  ops.remove(node);
3964
3597
  }
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
3598
+ /** Removes the node when the expression is falsy, evaluated in the overlay tplWith or tplEach opened. */
3967
3599
  static tplWhen(node, expression, ops, evaluator) {
3968
3600
  const accept = evaluator.evaluateExpression(expression);
3969
3601
  if (!accept) {
@@ -3977,12 +3609,8 @@ class CommandsHandler {
3977
3609
  static tplRemove(node, value, ops, evaluator) {
3978
3610
  switch (value.toLowerCase()) {
3979
3611
  case 'tag': {
3980
- const fragment = new DocumentFragment();
3981
- while (node.firstChild) {
3982
- fragment.appendChild(node.firstChild);
3983
- }
3612
+ const fragment = Fragments.fromChildNodes(node);
3984
3613
  if ('tplVerbatim' in node.dataset) {
3985
- //we are removing the parent element so we have to handle the lower priority commands
3986
3614
  ops.popData(node, 'tplVerbatim');
3987
3615
  ops.replace(node, fragment);
3988
3616
  } else {
@@ -4018,6 +3646,9 @@ class CommandsHandler {
4018
3646
  }
4019
3647
  const tuples = Array.isArray(attributesAndValues[0]) ? attributesAndValues : [attributesAndValues];
4020
3648
  tuples.forEach(([k, v]) => {
3649
+ if (v === null || v === undefined) {
3650
+ return;
3651
+ }
4021
3652
  node.setAttribute(k, v);
4022
3653
  });
4023
3654
  }
@@ -4045,12 +3676,10 @@ class CommandsHandler {
4045
3676
  }
4046
3677
  }
4047
3678
 
4048
- // Module-isolated string cache for dataset-to-attribute conversions
4049
3679
  const attributeCache = new BoundedCache(1000);
4050
3680
 
4051
3681
  /**
4052
- * Converts a tpl camelCase dataset key into a kebab-case attribute name.
4053
- * Uses a cache lookup to avoid repetitive regex/string splitting.
3682
+ * The attribute a `tpl` dataset key targets: `tplAriaLabel` is `aria-label`.
4054
3683
  * @param {string} dataSetKey
4055
3684
  * @returns {string}
4056
3685
  */
@@ -4072,38 +3701,40 @@ function toAttr(dataSetKey) {
4072
3701
  */
4073
3702
  class Template {
4074
3703
  /**
4075
- * Creates a template from a string.
3704
+ * Creates a template from html.
4076
3705
  * @param {string} html
4077
3706
  * @param {{ [k: string] : any }?} [modules]
4078
- * @param {...*} data
4079
- * @returns the template
3707
+ * @param {...*} data the data stack, innermost last
3708
+ * @returns {Template}
4080
3709
  */
4081
3710
  static fromHtml(html, modules, ...data) {
4082
3711
  return new Template(Fragments.fromHtml(html), new ExpressionEvaluator(modules, data));
4083
3712
  }
4084
3713
 
4085
3714
  /**
4086
- * Creates a template from the content of the first template element matching the selector.
3715
+ * Creates a template from the content of the first element matching the
3716
+ * selector, which is adopted: the template element is left empty.
4087
3717
  * @param {string} selector for an HTMLTemplateElement
4088
3718
  * @param {{ [k: string] : any }?} [modules]
4089
- * @param {...*} data
4090
- * @returns the template
3719
+ * @param {...*} data the data stack, innermost last
3720
+ * @returns {Template}
3721
+ * @throws when the selector matches no template element
4091
3722
  */
4092
3723
  static fromSelector(selector, modules, ...data) {
4093
3724
  const templateEl = document.querySelector(selector);
4094
3725
  if (!(templateEl instanceof HTMLTemplateElement)) {
4095
3726
  throw new Error('template selector does not match any template tag');
4096
3727
  }
4097
- const fragment = document.adoptNode(templateEl.content);
4098
- return new Template(fragment, new ExpressionEvaluator(modules, data));
3728
+ return Template.fromTemplate(templateEl, modules, ...data);
4099
3729
  }
4100
3730
 
4101
3731
  /**
4102
- * Creates a template from the content of an HTMLTemplateElement.
3732
+ * Creates a template from the content of a template element, which is
3733
+ * adopted: the template element is left empty.
4103
3734
  * @param {HTMLTemplateElement} templateEl
4104
3735
  * @param {{ [k: string] : any }?} [modules]
4105
- * @param {...*} data
4106
- * @returns the template
3736
+ * @param {...*} data the data stack, innermost last
3737
+ * @returns {Template}
4107
3738
  */
4108
3739
  static fromTemplate(templateEl, modules, ...data) {
4109
3740
  const fragment = document.adoptNode(templateEl.content);
@@ -4111,11 +3742,12 @@ class Template {
4111
3742
  }
4112
3743
 
4113
3744
  /**
4114
- * Creates a template from a DocumentFragment.
3745
+ * Creates a template over a fragment, which is imported afresh on every
3746
+ * render and never modified.
4115
3747
  * @param {DocumentFragment} fragment
4116
3748
  * @param { { [k: string] : any }? } [modules]
4117
- * @param {...*} data
4118
- * @returns the template
3749
+ * @param {...*} data the data stack, innermost last
3750
+ * @returns {Template}
4119
3751
  */
4120
3752
  static fromFragment(fragment, modules, ...data) {
4121
3753
  return new Template(fragment, new ExpressionEvaluator(modules, data));
@@ -4135,28 +3767,32 @@ class Template {
4135
3767
  * Creates a new Template rendering in another scope: the one way to rebind
4136
3768
  * a compiled template to a registry's modules and data.
4137
3769
  * @param {ExpressionEvaluator} evaluator
3770
+ * @returns {Template}
4138
3771
  */
4139
3772
  withEvaluator(evaluator) {
4140
3773
  return new Template(this.#fragment, evaluator);
4141
3774
  }
4142
3775
  /**
4143
- * Creates a new Template replacing the fragment.
3776
+ * Creates a new Template over another fragment, in the same scope.
4144
3777
  * @param {DocumentFragment} fragment
3778
+ * @returns {Template}
4145
3779
  */
4146
3780
  withFragment(fragment) {
4147
3781
  return new Template(fragment, this.#evaluator);
4148
3782
  }
4149
3783
  /**
4150
- * Creates a new Template with a new module added.
3784
+ * Creates a new Template with a module added, as `ExpressionEvaluator.withModule` does.
4151
3785
  * @param {string?} name
4152
3786
  * @param {{[k: string]: any}} value
3787
+ * @returns {Template}
4153
3788
  */
4154
3789
  withModule(name, value) {
4155
3790
  return new Template(this.#fragment, this.#evaluator.withModule(name, value));
4156
3791
  }
4157
3792
  /**
4158
- * Creates a new Template with new a data overlay added to the stack.
3793
+ * Creates a new Template with the data pushed as the innermost overlays.
4159
3794
  * @param {...*} data
3795
+ * @returns {Template}
4160
3796
  */
4161
3797
  withOverlay(...data) {
4162
3798
  return new Template(this.#fragment, this.#evaluator.withOverlay(...data));
@@ -4165,7 +3801,7 @@ class Template {
4165
3801
  * Evaluates an expression in this template's scope, widened by the overlays.
4166
3802
  * @param {string} expression
4167
3803
  * @param {...*} data
4168
- * @returns the evaluated expression result
3804
+ * @returns {any}
4169
3805
  */
4170
3806
  evaluateExpression(expression, ...data) {
4171
3807
  return this.#evaluator.withOverlay(...data).evaluateExpression(expression);
@@ -4174,33 +3810,29 @@ class Template {
4174
3810
  * Evaluates a templated text, the `{{ }}` form, in this template's scope.
4175
3811
  * @param {string} text
4176
3812
  * @param {...*} data
4177
- * @returns the parts the text evaluates to
3813
+ * @returns {{ type: symbol, value: any }[]} the parts, as `Expressions.MODE_TEMPLATED` describes
4178
3814
  */
4179
3815
  evaluateTemplated(text, ...data) {
4180
3816
  return this.#evaluator.withOverlay(...data).evaluateTemplated(text);
4181
3817
  }
4182
3818
  /**
4183
- * Returns an expression evaluator with bound modules and dataStack.
3819
+ * The scope this template renders in.
3820
+ * @returns {ExpressionEvaluator}
4184
3821
  */
4185
3822
  evaluator() {
4186
3823
  return this.#evaluator;
4187
3824
  }
4188
3825
  /**
4189
- * Renders the template.
4190
- * @returns a DocumentFragment
3826
+ * Renders the template into a new fragment, applying the `data-tpl-*`
3827
+ * commands and bindings and the `{{ }}` interpolations.
3828
+ * @returns {DocumentFragment}
3829
+ * @throws {RenderError} naming the node and the directive that failed
4191
3830
  */
4192
3831
  render() {
4193
3832
  try {
4194
3833
  const ops = new NodeOperations();
4195
3834
  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
- })();
3835
+ const fragment = imported.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? imported : Fragments.from(imported);
4204
3836
  const iterator = document.createNodeIterator(
4205
3837
  fragment,
4206
3838
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
@@ -4226,8 +3858,6 @@ class Template {
4226
3858
  try {
4227
3859
  CommandsHandler[command](el, value, ops, this.#evaluator);
4228
3860
  } 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
3861
  throw RenderError.wrap(`Error evaluating data-tpl-${toAttr(command)}="${value}"`, el, ex);
4232
3862
  }
4233
3863
  if (ops.removed(el)) {
@@ -4250,17 +3880,13 @@ class Template {
4250
3880
  el.setAttribute(attributeName, evaluated);
4251
3881
  }
4252
3882
  } catch (ex) {
4253
- throw RenderError.wrap(`Error evaluating data-tpl-${toAttr(dataSetKey)}="${expression}"`, el, ex);
3883
+ throw RenderError.wrap(`Error evaluating data-tpl-${attributeName}="${expression}"`, el, ex);
4254
3884
  }
4255
3885
  }
4256
3886
  }
4257
3887
  ops.cleanup();
4258
3888
  return fragment;
4259
3889
  } 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
3890
  if (ex instanceof RenderError) {
4265
3891
  throw ex;
4266
3892
  }
@@ -4268,22 +3894,26 @@ class Template {
4268
3894
  }
4269
3895
  }
4270
3896
  /**
4271
- * Renders this template on the Element (replacing children).
3897
+ * Renders this template into the element, replacing its children.
4272
3898
  * @param {Element} el
3899
+ * @throws {RenderError}
4273
3900
  */
4274
3901
  renderTo(el) {
4275
3902
  el.replaceChildren(this.render());
4276
3903
  }
4277
3904
  /**
4278
- * Renders this template appending the resulting fragment to the Element.
3905
+ * Renders this template and appends the result to the element.
4279
3906
  * @param {Element} el
3907
+ * @throws {RenderError}
4280
3908
  */
4281
3909
  appendTo(el) {
4282
3910
  el.appendChild(this.render());
4283
3911
  }
4284
3912
  /**
4285
- * Renders this template on the first Element matching the selector (replacing children), if exists.
3913
+ * Renders this template into the first element matching the selector,
3914
+ * replacing its children; does nothing when none matches.
4286
3915
  * @param {string} selector
3916
+ * @throws {RenderError}
4287
3917
  */
4288
3918
  renderToSelector(selector) {
4289
3919
  const el = document.querySelector(selector);
@@ -4292,8 +3922,10 @@ class Template {
4292
3922
  }
4293
3923
  }
4294
3924
  /**
4295
- * Renders this template appending the resulting fragment to the Element matching the selector, if exists.
3925
+ * Renders this template and appends the result to the first element
3926
+ * matching the selector; does nothing when none matches.
4296
3927
  * @param {string} selector
3928
+ * @throws {RenderError}
4297
3929
  */
4298
3930
  appendToSelector(selector) {
4299
3931
  const el = document.querySelector(selector);
@@ -4317,30 +3949,26 @@ class Template {
4317
3949
  }
4318
3950
 
4319
3951
  /**
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.
3952
+ * A render failure, one frame per nesting level, chained through `cause` from
3953
+ * the outermost frame down to the error that failed the expression. A frame's
3954
+ * message names what was being evaluated and the node's open tag, with the
3955
+ * attributes it still carries: the directive being evaluated is already off
3956
+ * the tag. The subtree is not in the message; `html` serializes it on demand.
4327
3957
  */
4328
3958
  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
- */
3959
+ /** How many frames a chain keeps: a deeper nesting drops the outermost ones and marks the chain `truncated`. */
4335
3960
  static FRAMES = 3;
4336
3961
  #node;
4337
3962
  #depth;
4338
- /** true when the budget dropped the outer frames of this chain */
3963
+ /** Whether the chain dropped outer frames to stay within FRAMES. */
4339
3964
  truncated = false;
4340
3965
  /**
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.
3966
+ * Frames a failure, unless the chain already holds FRAMES frames: then the
3967
+ * cause is returned as it is, marked `truncated`.
3968
+ * @param {string} message
3969
+ * @param {Node} nodeOrFragment the node that failed
3970
+ * @param {any} cause
3971
+ * @returns {RenderError}
4344
3972
  */
4345
3973
  static wrap(message, nodeOrFragment, cause) {
4346
3974
  if (cause instanceof RenderError && cause.depth >= RenderError.FRAMES) {
@@ -4349,27 +3977,44 @@ class RenderError extends Error {
4349
3977
  }
4350
3978
  return new RenderError(message, nodeOrFragment, cause);
4351
3979
  }
3980
+ /**
3981
+ * @param {string} message
3982
+ * @param {Node} nodeOrFragment the node that failed
3983
+ * @param {any} cause
3984
+ */
4352
3985
  constructor(message, nodeOrFragment, cause) {
4353
3986
  super(`${message} in \`${RenderError.describe(nodeOrFragment)}\``, { cause });
4354
3987
  this.name = 'RenderError';
4355
3988
  this.#node = nodeOrFragment;
4356
3989
  this.#depth = (cause instanceof RenderError ? cause.depth : 0) + 1;
4357
3990
  }
4358
- /** How many frames this chain carries, this one included. */
3991
+ /**
3992
+ * How many frames this chain carries, this one included.
3993
+ * @returns {number}
3994
+ */
4359
3995
  get depth() {
4360
3996
  return this.#depth;
4361
3997
  }
4362
- /** The node the render failed on, live: it keeps its place in the fragment being built. */
3998
+ /**
3999
+ * The node the render failed on, live in the fragment being built.
4000
+ * @returns {Node}
4001
+ */
4363
4002
  get node() {
4364
4003
  return this.#node;
4365
4004
  }
4366
- /** The node's markup, serialized when asked for rather than on every failure. */
4005
+ /**
4006
+ * The failed node's markup, whitespace trimmed, serialized on each read.
4007
+ * @returns {string}
4008
+ */
4367
4009
  get html() {
4368
4010
  return RenderError.stringify(this.#node);
4369
4011
  }
4370
4012
  /**
4371
4013
  * 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.
4014
+ * by its source, a fragment by the open tags of the elements it holds, cut
4015
+ * at 120 characters.
4016
+ * @param {Node} nodeOrFragment
4017
+ * @returns {string}
4373
4018
  */
4374
4019
  static describe(nodeOrFragment) {
4375
4020
  if (nodeOrFragment.nodeType === Node.TEXT_NODE) {
@@ -4388,10 +4033,13 @@ class RenderError extends Error {
4388
4033
  static #ellipsize(text) {
4389
4034
  return text.length > 120 ? `${text.slice(0, 120)}…` : text;
4390
4035
  }
4036
+ /**
4037
+ * A node's markup with its whitespace-only text dropped and the rest trimmed.
4038
+ * @param {Node} nodeOrFragment left untouched: a clone is serialized
4039
+ * @returns {string}
4040
+ */
4391
4041
  static stringify(nodeOrFragment) {
4392
- const t = document.createElement('template');
4393
- t.content.appendChild(RenderError.#cleanup(nodeOrFragment.cloneNode(true)));
4394
- return t.innerHTML;
4042
+ return Fragments.toHtml(RenderError.#cleanup(nodeOrFragment.cloneNode(true)));
4395
4043
  }
4396
4044
  static #cleanup(node) {
4397
4045
  if (node.nodeType === Node.TEXT_NODE) {
@@ -4413,31 +4061,28 @@ class RenderError extends Error {
4413
4061
  }
4414
4062
  }
4415
4063
 
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
4064
  class UpgradeQueue {
4422
4065
  #q = new Map();
4423
4066
  #readyResolve;
4424
4067
  #ready = new Promise((resolve) => {
4425
4068
  this.#readyResolve = resolve;
4426
4069
  });
4070
+ #configured = /** @type {PromiseWithResolvers<void>} */ (Promise.withResolvers());
4427
4071
  constructor() {
4428
- Nodes.waitDomContentLoaded(document).then(() => {
4072
+ Promise.all([Nodes.waitDomContentLoaded(document), this.#configured.promise]).then(() => {
4429
4073
  this.#start();
4430
4074
  });
4431
4075
  }
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
4076
  ready() {
4439
4077
  return this.#ready;
4440
4078
  }
4079
+ /**
4080
+ * Marks the registry as configured: until then nothing is defined, so an
4081
+ * empty queue says nothing about the page and readiness is held back.
4082
+ */
4083
+ configured() {
4084
+ this.#configured.resolve();
4085
+ }
4441
4086
  async #start() {
4442
4087
  await this.settled();
4443
4088
  document.dispatchEvent(
@@ -4450,13 +4095,8 @@ class UpgradeQueue {
4450
4095
  }
4451
4096
  enqueue(el) {
4452
4097
  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
4098
  return;
4456
4099
  }
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
4100
  const { promise: finished, resolve: markFinished } = /** @type {PromiseWithResolvers<void>} */ (
4461
4101
  Promise.withResolvers()
4462
4102
  );
@@ -4468,11 +4108,6 @@ class UpgradeQueue {
4468
4108
  });
4469
4109
  this.#q.set(el, { upgrade, finished });
4470
4110
  }
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
4111
  async #drain(accept, pick) {
4477
4112
  for (;;) {
4478
4113
  const pending = Array.from(this.#q)
@@ -4485,22 +4120,28 @@ class UpgradeQueue {
4485
4120
  }
4486
4121
  }
4487
4122
  /**
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.
4123
+ * Waits for the whole queue to drain, the upgrades enqueued meanwhile included.
4124
+ * Never rejects: a failed upgrade counts as drained.
4125
+ * @returns {Promise<void>}
4491
4126
  */
4492
4127
  settled() {
4493
- return this.#drain(() => true, (entry) => entry.finished);
4128
+ return this.#drain(
4129
+ () => true,
4130
+ (entry) => entry.finished,
4131
+ );
4494
4132
  }
4495
- /** Waits for the accepted upgrades, rejecting with the first that failed. */
4133
+ /**
4134
+ * Waits for the accepted upgrades, the ones enqueued meanwhile included,
4135
+ * rejecting with the first that failed.
4136
+ * @param {(el: Element) => boolean} accept
4137
+ * @returns {Promise<void>}
4138
+ */
4496
4139
  upgraded(accept) {
4497
4140
  return this.#drain(accept, (entry) => entry.upgrade);
4498
4141
  }
4499
- /** The pending upgrade of one element, undefined when it is not queued. */
4500
4142
  whenUpgraded(el) {
4501
4143
  return this.#q.get(el)?.upgrade;
4502
4144
  }
4503
- /** The elements whose upgrade is still pending, in queue order. */
4504
4145
  pending() {
4505
4146
  return Array.from(this.#q.keys());
4506
4147
  }
@@ -4511,8 +4152,8 @@ class UpgradeQueue {
4511
4152
  * and attribute mappers. Elements defined before configure() are deferred and
4512
4153
  * defined by it; from then on every defineElement takes effect immediately.
4513
4154
  * 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.
4155
+ * separate instance with its own upgrade queue, which dispatches its own
4156
+ * ftl:ready on the document, while Rendering waits on the singleton alone.
4516
4157
  */
4517
4158
  class Registry {
4518
4159
  #tagToClass = {};
@@ -4581,9 +4222,12 @@ class Registry {
4581
4222
  * Registers a custom element under its tag: the class is augmented with its
4582
4223
  * BITS (observed attributes, mappers, templates) and handed to the platform.
4583
4224
  * Before configure() the definition is deferred, so import order never
4584
- * matters.
4225
+ * matters. The element resolves its templates and components through this
4226
+ * registry, whichever one a module imports, and its `static config` is read
4227
+ * here, so a page replacing it does so before the definition.
4585
4228
  * @param {string} tag
4586
4229
  * @param {*} klass a ParsedElement subclass
4230
+ * @returns {Registry} this registry
4587
4231
  */
4588
4232
  defineElement(tag, klass) {
4589
4233
  if (!this.#configured) {
@@ -4601,13 +4245,8 @@ class Registry {
4601
4245
  * ancestors', so a base class declares what every subclass keeps observing
4602
4246
  * (a protocol attribute such as Field's disabled claim) and a leaf refines a
4603
4247
  * 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.
4248
+ * The observed attributes are applied in the composed order, a re-declared
4249
+ * name taking the position of its last declaration.
4611
4250
  * @param {*} klass a ParsedElement subclass
4612
4251
  * @returns {{ observed: string[], attributes: string[] }}
4613
4252
  */
@@ -4623,14 +4262,7 @@ class Registry {
4623
4262
  * The property an observed attribute drives, by the platform's own
4624
4263
  * dash-to-camel rule: the one `dataset` applies, so `clear-invalid-on-change`
4625
4264
  * 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.
4265
+ * reaches `dataset.clearInvalidOnChange`.
4634
4266
  *
4635
4267
  * Only this direction is mapped. A property never derives its attribute: a
4636
4268
  * setter reflects through `reflectTo`, naming the attribute it writes.
@@ -4643,10 +4275,6 @@ class Registry {
4643
4275
  #augmentAndDefineElement(tag, klass) {
4644
4276
  const { observed, attributes } = Registry.declarationsOf(klass);
4645
4277
  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
4278
  const declaredNames = observed.map((a) => a.split(':')[0]);
4651
4279
  const observedNames = [...new Set(declaredNames.reverse())].reverse();
4652
4280
  const attrToMapper = [...attributes, ...observed].reduce((acc, a) => {
@@ -4665,20 +4293,13 @@ class Registry {
4665
4293
  const nameToTemplate = Object.fromEntries(namesAndTemplates);
4666
4294
 
4667
4295
  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
4296
  registry: this,
4672
4297
  enqueue: (el) => this.#upgradeQueue.enqueue(el),
4673
4298
  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
4299
  CONFIG: config,
4678
4300
  OBSERVED: observedNames,
4679
4301
  DECLARED: [...new Set([...observedNames, ...attributes.map((a) => a.split(':')[0])])],
4680
4302
  ATTR_TO_MAPPER: attrToMapper,
4681
- //resolved once here rather than at every attribute write
4682
4303
  ATTR_TO_PROPERTY: Object.fromEntries(observedNames.map((a) => [a, Registry.propertyOf(a)])),
4683
4304
  TEMPLATES: nameToTemplate,
4684
4305
  };
@@ -4690,6 +4311,7 @@ class Registry {
4690
4311
  * as `#fn`.
4691
4312
  * @param {string} name
4692
4313
  * @param {object} value
4314
+ * @returns {Registry} this registry
4693
4315
  */
4694
4316
  defineModule(name, value) {
4695
4317
  const module = name ? { [name]: value } : value;
@@ -4700,6 +4322,7 @@ class Registry {
4700
4322
  /**
4701
4323
  * Replaces the whole module map.
4702
4324
  * @param {object} ms
4325
+ * @returns {Registry} this registry
4703
4326
  */
4704
4327
  defineModules(ms) {
4705
4328
  this.#modules = ms;
@@ -4711,6 +4334,7 @@ class Registry {
4711
4334
  * through component().
4712
4335
  * @param {string} name
4713
4336
  * @param {*} value
4337
+ * @returns {Registry} this registry
4714
4338
  */
4715
4339
  defineComponent(name, value) {
4716
4340
  this.#components[name] = value;
@@ -4719,6 +4343,7 @@ class Registry {
4719
4343
  /**
4720
4344
  * Replaces the data stack the templates evaluate over.
4721
4345
  * @param {...any} data
4346
+ * @returns {Registry} this registry
4722
4347
  */
4723
4348
  defineData(...data) {
4724
4349
  this.#data = data;
@@ -4728,6 +4353,7 @@ class Registry {
4728
4353
  /**
4729
4354
  * Appends to the data stack, the later entry winning a shared name.
4730
4355
  * @param {...any} data
4356
+ * @returns {Registry} this registry
4731
4357
  */
4732
4358
  defineOverlay(...data) {
4733
4359
  this.#data = [...this.#data, ...data];
@@ -4739,6 +4365,7 @@ class Registry {
4739
4365
  * `name:type` declaration.
4740
4366
  * @param {string} k
4741
4367
  * @param {{ unmarshal(str: string|null, name: string, el: Element): any, marshal(value: any, name: string, el: Element): string|null }} v
4368
+ * @returns {Registry} this registry
4742
4369
  */
4743
4370
  defineMapper(k, v) {
4744
4371
  this.#mappers[k] = v;
@@ -4747,6 +4374,7 @@ class Registry {
4747
4374
  /**
4748
4375
  * Hands the registry over to the plugin's configure.
4749
4376
  * @param {{ configure(registry: Registry): void }} p
4377
+ * @returns {Registry} this registry
4750
4378
  */
4751
4379
  plugin(p) {
4752
4380
  p.configure(this);
@@ -4754,7 +4382,10 @@ class Registry {
4754
4382
  }
4755
4383
  /**
4756
4384
  * Defines every element deferred so far; from then on, defineElement takes
4757
- * effect immediately.
4385
+ * effect immediately. Readiness waits for the first call: a page that
4386
+ * configures late (translations fetched first) gets its ftl:ready once the
4387
+ * elements this defines have upgraded, not at DOMContentLoaded.
4388
+ * @returns {Registry} this registry
4758
4389
  */
4759
4390
  configure() {
4760
4391
  for (const [tag, klass] of Object.entries(this.#tagToClass)) {
@@ -4762,27 +4393,43 @@ class Registry {
4762
4393
  delete this.#tagToClass[tag];
4763
4394
  }
4764
4395
  this.#configured = true;
4396
+ this.#upgradeQueue.configured();
4765
4397
  return this;
4766
4398
  }
4767
4399
  /**
4768
4400
  * Waits for the queued upgrades the filter accepts, rejecting with the first
4769
- * that failed: the rejecting barrier Rendering is a facade over.
4401
+ * that failed, the upgrades enqueued meanwhile included: the rejecting
4402
+ * barrier Rendering is a facade over.
4770
4403
  * @param {(el: Element) => boolean} accept
4404
+ * @returns {Promise<void>}
4771
4405
  */
4772
4406
  settle(accept) {
4773
4407
  return this.#upgradeQueue.upgraded(accept);
4774
4408
  }
4775
- /** The pending upgrade of one element, undefined when it is not queued. */
4409
+ /**
4410
+ * The pending upgrade of one element: resolving when it has rendered,
4411
+ * rejecting with what its upgrade threw.
4412
+ * @param {Element} el
4413
+ * @returns {Promise<void>|undefined} undefined when the element is not queued
4414
+ */
4776
4415
  whenUpgraded(el) {
4777
4416
  return this.#upgradeQueue.whenUpgraded(el);
4778
4417
  }
4779
- /** The elements whose upgrade is still pending, in queue order. */
4418
+ /**
4419
+ * The elements whose upgrade is still pending, in queue order.
4420
+ * @returns {Element[]}
4421
+ */
4780
4422
  pending() {
4781
4423
  return this.#upgradeQueue.pending();
4782
4424
  }
4783
4425
  /**
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.
4426
+ * Waits for this registry's readiness: resolves right after its ftl:ready
4427
+ * event is dispatched on the document, and immediately when that already
4428
+ * happened. The event is dispatched once DOMContentLoaded has passed,
4429
+ * configure() has been called and the queue has drained, so a registry
4430
+ * that is never configured is never ready. Never rejects: a failed
4431
+ * upgrade counts as settled, and its rejection is left unhandled so it
4432
+ * still reaches the console and the page's error reporting.
4786
4433
  * @returns {Promise<void>}
4787
4434
  */
4788
4435
  ready() {
@@ -4795,11 +4442,16 @@ class Registry {
4795
4442
  * The scope every template on this registry renders in: the modules and the
4796
4443
  * data stack, as one value. Replaced whenever either is defined, so a holder
4797
4444
  * of an older one keeps rendering against what it was handed.
4445
+ * @returns {ExpressionEvaluator}
4798
4446
  */
4799
4447
  evaluator() {
4800
4448
  return this.#evaluator;
4801
4449
  }
4802
- /** The component registered under the name, undefined when none is. */
4450
+ /**
4451
+ * The component registered under the name.
4452
+ * @param {string} name
4453
+ * @returns {any} undefined when none is
4454
+ */
4803
4455
  component(name) {
4804
4456
  return this.#components[name];
4805
4457
  }
@@ -4807,33 +4459,59 @@ class Registry {
4807
4459
 
4808
4460
  const registry = new Registry();
4809
4461
 
4810
- /** The Template factories, each bound to the page registry's scope. */
4811
4462
  /** The Template factories bound to the page's own registry: the same four sources, with its modules and data already applied. */
4812
4463
  class Templates {
4464
+ /**
4465
+ * @param {string} html
4466
+ * @returns {Template}
4467
+ */
4813
4468
  static fromHtml(html) {
4814
4469
  return Template.fromHtml(html).withEvaluator(registry.evaluator());
4815
4470
  }
4471
+ /**
4472
+ * @param {string} selector for an HTMLTemplateElement
4473
+ * @returns {Template}
4474
+ */
4816
4475
  static fromSelector(selector) {
4817
4476
  return Template.fromSelector(selector).withEvaluator(registry.evaluator());
4818
4477
  }
4478
+ /**
4479
+ * @param {HTMLTemplateElement} templateEl consumed: its content is adopted
4480
+ * @returns {Template}
4481
+ */
4819
4482
  static fromTemplate(templateEl) {
4820
4483
  return Template.fromTemplate(templateEl).withEvaluator(registry.evaluator());
4821
4484
  }
4485
+ /**
4486
+ * @param {DocumentFragment} fragment
4487
+ * @returns {Template}
4488
+ */
4822
4489
  static fromFragment(fragment) {
4823
4490
  return Template.fromFragment(fragment).withEvaluator(registry.evaluator());
4824
4491
  }
4825
4492
  }
4826
4493
 
4827
4494
  /**
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.
4495
+ * Waits for elements queued on the page's `registry` to render. Unlike
4496
+ * `registry.ready()`, these reject when an awaited upgrade fails. An element
4497
+ * defined by another Registry is awaited through that registry instead.
4832
4498
  */
4833
4499
  class Rendering {
4500
+ /**
4501
+ * Waits for the element and its descendants, the ones their own renders
4502
+ * enqueue included.
4503
+ * @param {Element} el
4504
+ * @returns {Promise<void>} rejecting with the first upgrade that failed
4505
+ */
4834
4506
  static waitFor(el) {
4835
4507
  return registry.settle((child) => el.contains(child));
4836
4508
  }
4509
+ /**
4510
+ * Waits for the element's descendants, the element itself excluded: what a
4511
+ * component awaits from inside its own render.
4512
+ * @param {Element} el
4513
+ * @returns {Promise<void>} rejecting with the first upgrade that failed
4514
+ */
4837
4515
  static waitForChildren(el) {
4838
4516
  return registry.settle((child) => el !== child && el.contains(child));
4839
4517
  }
@@ -4847,9 +4525,15 @@ class Rendering {
4847
4525
  * @property {(val: any, name: string, el: Element) => string|null} marshal
4848
4526
  */
4849
4527
 
4528
+ /**
4529
+ * The base of every fml element. A subclass declares its attributes, slots and
4530
+ * templates in statics and builds its dom in `render`; the base reads the light
4531
+ * slots, reads the declared attributes through their mappers, renders once, and
4532
+ * then forwards observed attribute writes to the matching properties. An element
4533
+ * resolves templates and components through the registry that defined it.
4534
+ */
4850
4535
  class ParsedElement extends HTMLElement {
4851
4536
  static BITS = {
4852
- //an element the registry never defined still answers the page's own
4853
4537
  registry,
4854
4538
  enqueue: (el) => {},
4855
4539
  SLOTS: false,
@@ -4873,14 +4557,12 @@ class ParsedElement extends HTMLElement {
4873
4557
  this.internals = this.attachInternals();
4874
4558
  }
4875
4559
  /**
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.
4560
+ * The element's ElementInternals, attached by the base for every element: a
4561
+ * subclass never calls attachInternals itself, since a second call throws.
4562
+ * A subclass declaring `static formAssociated` gets the form apis here.
4563
+ * A subclass must not redeclare the field: a class field without an
4564
+ * initializer assigns undefined after super() returns, wiping it.
4565
+ * @type {ElementInternals}
4884
4566
  */
4885
4567
  internals;
4886
4568
  #parsed = false;
@@ -4897,26 +4579,36 @@ class ParsedElement extends HTMLElement {
4897
4579
  #mapper(attr) {
4898
4580
  const mapper = this.#bits().ATTR_TO_MAPPER[attr];
4899
4581
  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
4582
  throw new Error(
4905
4583
  `${this.constructor.name} declares no attribute '${attr}': declare it in static observed or static attributes, or read it with getAttribute`,
4906
4584
  );
4907
4585
  }
4908
4586
  return mapper;
4909
4587
  }
4588
+ /**
4589
+ * Reads an attribute value through the mapper it was declared with.
4590
+ * @param {string} attr
4591
+ * @param {string|null} str
4592
+ * @returns {any}
4593
+ * @throws when the class declares no such attribute; read undeclared ones with getAttribute
4594
+ */
4910
4595
  unmarshal(attr, str) {
4911
4596
  return this.#mapper(attr).unmarshal(str, attr, this);
4912
4597
  }
4598
+ /**
4599
+ * Writes a value as the attribute string its declared mapper produces.
4600
+ * @param {string} attr
4601
+ * @param {any} value
4602
+ * @returns {string|null} null for a value that removes the attribute
4603
+ * @throws when the class declares no such attribute
4604
+ */
4913
4605
  marshal(attr, value) {
4914
4606
  return this.#mapper(attr).marshal(value, attr, this);
4915
4607
  }
4916
4608
  /**
4917
- * @param {string} [name] - The name of the template target, defaults to 'default'
4609
+ * The registry that defined this element, the page's own for an undefined one.
4610
+ * @returns {typeof registry}
4918
4611
  */
4919
- /** The registry that defined this element, the page's own for an undefined one. */
4920
4612
  get _registry() {
4921
4613
  return this.#bits().registry;
4922
4614
  }
@@ -4924,10 +4616,18 @@ class ParsedElement extends HTMLElement {
4924
4616
  * The component registered under the name on this element's registry, the
4925
4617
  * one every ful loader and mapper resolves through.
4926
4618
  * @param {string} name
4619
+ * @returns {any} undefined when none is registered
4927
4620
  */
4928
4621
  component(name) {
4929
4622
  return this.#bits().registry.component(name);
4930
4623
  }
4624
+ /**
4625
+ * One of the class's templates, bound to its registry's scope with the
4626
+ * class's `static config` overlaid as `config`.
4627
+ * @param {string} [name] 'default', the `static template`, when omitted
4628
+ * @returns {Template}
4629
+ * @throws when the class declares no template of that name
4630
+ */
4931
4631
  template(name) {
4932
4632
  const target = this.#bits().TEMPLATES[name ?? 'default'];
4933
4633
  if (!target) {
@@ -4947,15 +4647,9 @@ class ParsedElement extends HTMLElement {
4947
4647
  if (oldValue === newValue) {
4948
4648
  return;
4949
4649
  }
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
4650
  if (this.#reflecting.has(attr)) {
4954
4651
  return;
4955
4652
  }
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
4653
  if (!this.#parsed) {
4960
4654
  if (this.#pending !== null && attr in this.#pending) {
4961
4655
  this.#pending[attr] = this.unmarshal(attr, newValue);
@@ -4965,14 +4659,18 @@ class ParsedElement extends HTMLElement {
4965
4659
  this[this.#bits().ATTR_TO_PROPERTY[attr]] = this.unmarshal(attr, newValue);
4966
4660
  }
4967
4661
  /**
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.
4662
+ * Upgrades once, however often the element is connected: reads the declared
4663
+ * attributes, keeps the observed ones open to writes made while the render is
4664
+ * pending, renders, then applies the observed values to their properties in
4665
+ * the order the registry composed them, a base class's before a subclass's.
4666
+ * Only then do attribute writes forward to properties, and the element gains
4667
+ * the `rendered` custom state. A render that throws leaves the forwarding
4668
+ * shut and the state unset.
4971
4669
  *
4972
4670
  * An observed attribute drives the property the registry's `propertyOf`
4973
4671
  * 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.
4672
+ * a camelCase property.
4673
+ * @returns {Promise<void>} rejecting with what the render threw
4976
4674
  */
4977
4675
  async upgrade() {
4978
4676
  if (this.#started) {
@@ -4980,9 +4678,6 @@ class ParsedElement extends HTMLElement {
4980
4678
  }
4981
4679
  this.#started = true;
4982
4680
  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
4681
  const declared = Object.fromEntries(
4987
4682
  this.#bits().DECLARED.map((attribute) => [
4988
4683
  attribute,
@@ -4994,21 +4689,11 @@ class ParsedElement extends HTMLElement {
4994
4689
  this.#pending = declared;
4995
4690
  try {
4996
4691
  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
4692
  const properties = this.#bits().ATTR_TO_PROPERTY;
5001
4693
  for (const name of this.#bits().OBSERVED) {
5002
4694
  this[properties[name]] = declared[name];
5003
4695
  }
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
4696
  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
4697
  this.internals.states.add('rendered');
5013
4698
  } finally {
5014
4699
  this.#pending = null;
@@ -5019,8 +4704,9 @@ class ParsedElement extends HTMLElement {
5019
4704
  * drive, and the base applies the declared state onto the properties as
5020
4705
  * soon as it returns. A render needing a declared value while it builds
5021
4706
  * reads it through `declared(name)`. The slots are undefined for an element
5022
- * declaring no slots.
5023
- * @param {{ slots: any }} c
4707
+ * declaring no slots. It may be async; the upgrade awaits it.
4708
+ * @param {{ slots: Record<string, DocumentFragment> | undefined }} c
4709
+ * @returns {void | Promise<void>}
5024
4710
  */
5025
4711
  render(c) {}
5026
4712
  /**
@@ -5032,12 +4718,9 @@ class ParsedElement extends HTMLElement {
5032
4718
  * observed name answers the snapshot while the render is pending, kept
5033
4719
  * open to attribute writes landing in that window, and the live attribute
5034
4720
  * 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
4721
  * @param {string} name
4722
+ * @returns {any}
4723
+ * @throws when the class declares no such attribute
5041
4724
  */
5042
4725
  declared(name) {
5043
4726
  if (name in this.#frozen) {
@@ -5048,15 +4731,18 @@ class ParsedElement extends HTMLElement {
5048
4731
  }
5049
4732
  return this.unmarshal(name, this.getAttribute(name));
5050
4733
  }
5051
- /** Whether the element's render completed: the moment its properties went live. */
4734
+ /**
4735
+ * Whether the element's render completed: the moment its properties went
4736
+ * live, which css reads as `:state(rendered)`. Unlike `:defined`, which holds
4737
+ * from the constructor, it is false until the dom exists.
4738
+ * @returns {boolean}
4739
+ */
5052
4740
  get rendered() {
5053
4741
  return this.#parsed;
5054
4742
  }
5055
4743
  /**
5056
4744
  * 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.
4745
+ * the mapper the attribute was declared with.
5060
4746
  *
5061
4747
  * A value the attribute already carries is not written at all, so reflecting
5062
4748
  * what an attribute write just delivered ends there rather than looping, and
@@ -5087,18 +4773,37 @@ class ParsedElement extends HTMLElement {
5087
4773
  */
5088
4774
 
5089
4775
  /**
5090
- * The receiver contract of the module functions: bound to a proxy over the
5091
- * data stack when called from a template, or to a plain object by Localization.of().
4776
+ * The receiver contract of the module functions: a plain object carrying the
4777
+ * translations and the locale, as Localization.of() binds them, or the proxy
4778
+ * over the data stack a template calls them with.
5092
4779
  *
5093
4780
  * @typedef {{ l10n?: Messages, locale?: string }} Receiver
5094
4781
  */
5095
4782
 
4783
+ /**
4784
+ * The translations or the locale of a receiver. From a template they are read
4785
+ * from the bound data alone (the registry's overlays, or the data handed to
4786
+ * the Template factory): the rows and responses a render pushes on top are
4787
+ * what arrives later, and must not supply or replace the page's own messages.
4788
+ * @param {any} receiver
4789
+ * @param {'l10n' | 'locale'} name
4790
+ * @returns {any}
4791
+ */
4792
+ const configured = (receiver, name) => {
4793
+ const bound = receiver[Expressions.BOUND];
4794
+ return typeof bound === 'function' ? bound(name) : receiver[name];
4795
+ };
4796
+
5096
4797
  const PLACEHOLDER = /\{(\w+)\}/g;
5097
4798
  const POSITIONAL = /\{(\d+)\}/g;
5098
4799
  /** @param {any} v @returns {v is Record<string, string>} */
5099
- const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
4800
+ const isPlainObject = (v) =>
4801
+ typeof v === 'object' && v !== null && [Object.prototype, null].includes(Object.getPrototypeOf(v));
4802
+
4803
+ /** @param {any} problem @returns {string|undefined} the reason, unless it is missing or blank */
4804
+ const reasonOf = (problem) =>
4805
+ typeof problem?.reason === 'string' && problem.reason.trim() !== '' ? problem.reason : undefined;
5100
4806
 
5101
- /** every l10n problem is reported once per session: a missing key in a row template must not flood the console */
5102
4807
  const warned = new Set();
5103
4808
  const warnOnce = (problem) => {
5104
4809
  if (!warned.has(problem)) {
@@ -5109,11 +4814,7 @@ const warnOnce = (problem) => {
5109
4814
 
5110
4815
  const formatters = new BoundedCache(100);
5111
4816
  /**
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
- *
4817
+ * An Intl instance, memoized by constructor, locale and options.
5117
4818
  * @param {{ new (locale: string | undefined, options: any): any }} ctor
5118
4819
  * @param {string | undefined} locale
5119
4820
  * @param {any} [options]
@@ -5127,35 +4828,40 @@ const formatter = (ctor, locale, options) => {
5127
4828
  * The translations as a template module: `#l10n:t()` for messages, plus date,
5128
4829
  * number and bytes formatting in the page's locale. The template form and the
5129
4830
  * imperative `of()` facade both resolve through the registry's scope, so they
5130
- * always produce the same result for the same key.
4831
+ * always produce the same result for the same key. A template reads `l10n`
4832
+ * and `locale` from its bound data only, the most recent layer winning: an
4833
+ * overlay pushed while rendering (a row, a response) never reaches them.
5131
4834
  */
5132
4835
  class Localization {
5133
4836
  /**
5134
4837
  * 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}).
4838
+ * A single plain object argument (an object literal, or one with a null
4839
+ * prototype) interpolates named placeholders ({name}); anything else interpolates positional ones ({0}).
5137
4840
  * A plural leaf selects its form through Intl.PluralRules over the
5138
4841
  * numeric {count} named argument.
5139
4842
  *
5140
4843
  * @param {string} key
5141
4844
  * @param {...any} args
4845
+ * Each problem (a missing key, placeholder or plural form) is warned once
4846
+ * per page.
4847
+ *
5142
4848
  * @this {Receiver}
5143
4849
  * @returns {string} the message, or the key itself when the translations do not carry it
5144
4850
  */
5145
4851
  static t(key, ...args) {
5146
- const messages = this.l10n ?? {};
5147
- let message = messages[key];
4852
+ const messages = configured(this, 'l10n') ?? {};
4853
+ let message = Object.hasOwn(messages, key) ? messages[key] : undefined;
5148
4854
  if (message === undefined) {
5149
4855
  warnOnce(`missing message "${key}"`);
5150
4856
  return key;
5151
4857
  }
4858
+ const named = args.length === 1 && isPlainObject(args[0]) ? args[0] : null;
5152
4859
  if (isPlainObject(message)) {
5153
- const named = args.length === 1 && isPlainObject(args[0]) ? args[0] : {};
5154
- if (typeof named.count !== 'number') {
4860
+ if (typeof named?.count !== 'number') {
5155
4861
  warnOnce(`plural message "${key}" needs a numeric {count}`);
5156
4862
  message = message.other;
5157
4863
  } else {
5158
- const locale = this.locale ?? navigator?.language ?? 'en';
4864
+ const locale = configured(this, 'locale') ?? navigator?.language ?? 'en';
5159
4865
  const category = formatter(Intl.PluralRules, locale).select(named.count);
5160
4866
  message = message[category] ?? message.other;
5161
4867
  }
@@ -5164,10 +4870,9 @@ class Localization {
5164
4870
  warnOnce(`plural message "${key}" has no "other" form`);
5165
4871
  return key;
5166
4872
  }
5167
- if (args.length === 1 && isPlainObject(args[0])) {
5168
- const named = args[0];
4873
+ if (named) {
5169
4874
  return message.replace(PLACEHOLDER, (literal, name) => {
5170
- if (!(name in named)) {
4875
+ if (!Object.hasOwn(named, name)) {
5171
4876
  warnOnce(`message "${key}" wants {${name}}`);
5172
4877
  return literal;
5173
4878
  }
@@ -5193,9 +4898,10 @@ class Localization {
5193
4898
  * @param {Date | number} value
5194
4899
  * @param {Intl.DateTimeFormatOptions} [options]
5195
4900
  * @this {Receiver}
4901
+ * @returns {string}
5196
4902
  */
5197
4903
  static date(value, options) {
5198
- return formatter(Intl.DateTimeFormat, this.locale, options).format(value);
4904
+ return formatter(Intl.DateTimeFormat, configured(this, 'locale'), options).format(value);
5199
4905
  }
5200
4906
 
5201
4907
  /**
@@ -5204,9 +4910,10 @@ class Localization {
5204
4910
  * @param {number} value
5205
4911
  * @param {Intl.NumberFormatOptions} [options]
5206
4912
  * @this {Receiver}
4913
+ * @returns {string}
5207
4914
  */
5208
4915
  static number(value, options) {
5209
- return formatter(Intl.NumberFormat, this.locale, options).format(value);
4916
+ return formatter(Intl.NumberFormat, configured(this, 'locale'), options).format(value);
5210
4917
  }
5211
4918
 
5212
4919
  /**
@@ -5216,9 +4923,10 @@ class Localization {
5216
4923
  *
5217
4924
  * @param {number} value
5218
4925
  * @this {Receiver}
4926
+ * @returns {string}
5219
4927
  */
5220
4928
  static bytes(value) {
5221
- const format = formatter(Intl.NumberFormat, this.locale, { maximumFractionDigits: 2 }).format;
4929
+ const format = formatter(Intl.NumberFormat, configured(this, 'locale'), { maximumFractionDigits: 2 }).format;
5222
4930
  if (value >= 1024 * 1024 * 1024) {
5223
4931
  return `${format(value / 1024 / 1024 / 1024)}GiB`;
5224
4932
  }
@@ -5231,16 +4939,37 @@ class Localization {
5231
4939
  return `${format(value)}B`;
5232
4940
  }
5233
4941
 
4942
+ /**
4943
+ * What went wrong, as text for the reader. A value carrying a non-empty
4944
+ * `problems` array reads as their reasons, one per line, a blank or missing
4945
+ * reason reading as the `failure.no-reason` message. Any other value with a
4946
+ * `message` reads as that message; anything else as its string form, and
4947
+ * null or undefined as the empty string.
4948
+ *
4949
+ * @param {any} value what was thrown or rejected with
4950
+ * @this {Receiver}
4951
+ * @returns {string}
4952
+ */
4953
+ static failure(value) {
4954
+ if (Array.isArray(value?.problems) && value.problems.length > 0) {
4955
+ return value.problems.map((p) => reasonOf(p) ?? Localization.t.call(this, 'failure.no-reason')).join('\n');
4956
+ }
4957
+ if (value?.message !== undefined) {
4958
+ return String(value.message);
4959
+ }
4960
+ return value == null ? '' : String(value);
4961
+ }
4962
+
5234
4963
  /**
5235
4964
  * An imperative facade over the module functions, resolving the translations
5236
- * and the locale from the registry overlays on every call.
4965
+ * and the locale from the registry overlays on every call, the same lookup a
4966
+ * template makes: a facade taken at module scope, before the plugin
4967
+ * configures, still sees the translations.
5237
4968
  *
5238
4969
  * @param {{ locale?: string }} [overrides] an explicit locale, winning over the registry one
4970
+ * @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
4971
  */
5240
4972
  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
4973
  const resolve = (prop) => registry.evaluator().resolve(prop);
5245
4974
  /** @param {any} fn */
5246
4975
  const bind =
@@ -5252,6 +4981,7 @@ class Localization {
5252
4981
  date: bind(Localization.date),
5253
4982
  number: bind(Localization.number),
5254
4983
  bytes: bind(Localization.bytes),
4984
+ failure: bind(Localization.failure),
5255
4985
  };
5256
4986
  }
5257
4987
  }