@optionfactory/fml 9.0.0-rc3 → 9.0.0-rc30

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