@optionfactory/fml 8.0.0 → 8.0.1

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 (45) hide show
  1. package/dist/client-errors.iife.js +11 -8
  2. package/dist/client-errors.iife.js.map +1 -1
  3. package/dist/client-errors.iife.min.js +1 -1
  4. package/dist/client-errors.iife.min.js.map +1 -1
  5. package/dist/fml.css +10 -1
  6. package/dist/fml.css.map +1 -1
  7. package/dist/fml.d.mts +73 -13
  8. package/dist/fml.iife.js +598 -208
  9. package/dist/fml.iife.js.map +1 -1
  10. package/dist/fml.iife.min.js +1 -1
  11. package/dist/fml.iife.min.js.map +1 -1
  12. package/dist/fml.min.mjs +1 -1
  13. package/dist/fml.min.mjs.map +1 -1
  14. package/dist/fml.mjs +598 -208
  15. package/dist/fml.mjs.map +1 -1
  16. package/dist/ftl.d.mts +2 -1
  17. package/dist/ftl.iife.js +60 -33
  18. package/dist/ftl.iife.js.map +1 -1
  19. package/dist/ftl.iife.min.js +1 -1
  20. package/dist/ftl.iife.min.js.map +1 -1
  21. package/dist/ftl.min.mjs +1 -1
  22. package/dist/ftl.min.mjs.map +1 -1
  23. package/dist/ftl.mjs +60 -33
  24. package/dist/ftl.mjs.map +1 -1
  25. package/dist/ful.css +10 -1
  26. package/dist/ful.css.map +1 -1
  27. package/dist/ful.d.mts +22 -6
  28. package/dist/ful.iife.js +496 -166
  29. package/dist/ful.iife.js.map +1 -1
  30. package/dist/ful.iife.min.js +1 -1
  31. package/dist/ful.iife.min.js.map +1 -1
  32. package/dist/ful.min.mjs +1 -1
  33. package/dist/ful.min.mjs.map +1 -1
  34. package/dist/ful.mjs +496 -166
  35. package/dist/ful.mjs.map +1 -1
  36. package/dist/httpc.d.mts +49 -6
  37. package/dist/httpc.iife.js +40 -9
  38. package/dist/httpc.iife.js.map +1 -1
  39. package/dist/httpc.iife.min.js +1 -1
  40. package/dist/httpc.iife.min.js.map +1 -1
  41. package/dist/httpc.min.mjs +1 -1
  42. package/dist/httpc.min.mjs.map +1 -1
  43. package/dist/httpc.mjs +40 -9
  44. package/dist/httpc.mjs.map +1 -1
  45. package/package.json +6 -7
package/dist/ftl.d.mts CHANGED
@@ -252,8 +252,9 @@ declare class Template {
252
252
  * @param {string} expression
253
253
  * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
254
254
  * @param {...*} data
255
+ * @returns the evaluated expression result
255
256
  */
256
- evaluate(expression: string, mode?: (typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED) | null, ...data: any[]): void;
257
+ evaluate(expression: string, mode?: (typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED) | null, ...data: any[]): any;
257
258
  /**
258
259
  * Returns an expression evaluator with bound modules and dataStack.
259
260
  */
package/dist/ftl.iife.js CHANGED
@@ -3460,7 +3460,7 @@ var ftl = (function (exports) {
3460
3460
  case '<=':
3461
3461
  return lhs <= rhs;
3462
3462
  default:
3463
- throw new Error('unknown cmp op ' + node.op);
3463
+ throw new Error(`unknown cmp op ${node.op}`);
3464
3464
  }
3465
3465
  }
3466
3466
  [nodes.call](node) {
@@ -3471,7 +3471,7 @@ var ftl = (function (exports) {
3471
3471
  }
3472
3472
  const fn = module[fnRef.value];
3473
3473
  if (!fn) {
3474
- throw new Error(`Function "#${fnRef.module === null ? '' : fnRef.module + ':'}${fnRef.value}" not found`);
3474
+ throw new Error(`Function "#${fnRef.module === null ? '' : `${fnRef.module}:`}${fnRef.value}" not found`);
3475
3475
  }
3476
3476
  const args = node.args.map((arg) => this.visit(arg));
3477
3477
  return fn.apply(this.#resolve_proxy(), args);
@@ -3507,14 +3507,14 @@ var ftl = (function (exports) {
3507
3507
  return cond ? cond : this.visit(node.ifFalse);
3508
3508
  }
3509
3509
  [nodes.access](node) {
3510
- let prev = undefined;
3510
+ let prev ;
3511
3511
  let cur = this.visit(node.lhs);
3512
3512
  for (let i = 0; i !== node.rhs.length; ++i) {
3513
3513
  const rhs = node.rhs[i];
3514
3514
  if (rhs.ns && cur == null) {
3515
3515
  return undefined;
3516
3516
  }
3517
- let value = undefined;
3517
+ let value ;
3518
3518
  switch (rhs.type) {
3519
3519
  case nodes.member: {
3520
3520
  value = cur[rhs.rhs];
@@ -3555,7 +3555,7 @@ var ftl = (function (exports) {
3555
3555
  case nodes.templated.ten:
3556
3556
  return { type: nodes.dom.n, value: this.visit(node.value) };
3557
3557
  default:
3558
- throw new Error('unknown node type ' + node.type.toString());
3558
+ throw new Error(`unknown node type ${node.type.toString()}`);
3559
3559
  }
3560
3560
  });
3561
3561
  }
@@ -3731,7 +3731,7 @@ var ftl = (function (exports) {
3731
3731
  .forEach((a) => {
3732
3732
  const target = a.substring(prefix.length);
3733
3733
  if (target === 'class') {
3734
- const classes = from.getAttribute(prefix + 'class')?.split(/\s+/).filter((a) => a.length) ?? [];
3734
+ const classes = from.getAttribute(`${prefix}class`)?.split(/\s+/).filter((a) => a.length) ?? [];
3735
3735
  to.classList.add(...classes);
3736
3736
  return;
3737
3737
  }
@@ -3823,8 +3823,7 @@ var ftl = (function (exports) {
3823
3823
  * @returns
3824
3824
  */
3825
3825
  static isParsed(el) {
3826
- //@ts-ignore
3827
- for (let c = el; c; c = c.parentNode) {
3826
+ for (let c = /** @type {Node | null} */ (el); c; c = c.parentNode) {
3828
3827
  if (c.nextSibling) {
3829
3828
  return true;
3830
3829
  }
@@ -3887,19 +3886,18 @@ var ftl = (function (exports) {
3887
3886
  }
3888
3887
 
3889
3888
  class NodeOperations {
3890
- #forRemoval;
3891
- constructor() {
3892
- this.#forRemoval = [];
3893
- }
3889
+ #forRemoval = new Set();
3894
3890
  removed(node) {
3895
- return this.#forRemoval.includes(node);
3891
+ return this.#forRemoval.has(node);
3896
3892
  }
3897
3893
  remove(node) {
3898
3894
  node.replaceChildren?.();
3899
3895
  Object.keys(node.dataset || {})
3900
3896
  .filter((k) => k.startsWith('tpl'))
3901
- .forEach((k) => delete node.dataset[k]);
3902
- this.#forRemoval.push(node);
3897
+ .forEach((k) => {
3898
+ delete node.dataset[k];
3899
+ });
3900
+ this.#forRemoval.add(node);
3903
3901
  }
3904
3902
  popData(node, key) {
3905
3903
  const v = node.dataset[key];
@@ -3917,9 +3915,10 @@ var ftl = (function (exports) {
3917
3915
  this.remove(ref);
3918
3916
  }
3919
3917
  cleanup() {
3920
- while (this.#forRemoval.length) {
3921
- this.#forRemoval.pop().remove();
3918
+ for (const node of this.#forRemoval) {
3919
+ node.remove();
3922
3920
  }
3921
+ this.#forRemoval.clear();
3923
3922
  }
3924
3923
  }
3925
3924
 
@@ -3974,7 +3973,7 @@ var ftl = (function (exports) {
3974
3973
  }
3975
3974
  static tplRemove(node, value, ops, modules, dataStack) {
3976
3975
  switch (value.toLowerCase()) {
3977
- case 'tag':
3976
+ case 'tag': {
3978
3977
  const fragment = new DocumentFragment();
3979
3978
  while (node.firstChild) {
3980
3979
  fragment.appendChild(node.firstChild);
@@ -3988,6 +3987,7 @@ var ftl = (function (exports) {
3988
3987
  ops.remove(node);
3989
3988
  }
3990
3989
  break;
3990
+ }
3991
3991
  case 'body':
3992
3992
  node.replaceChildren();
3993
3993
  break;
@@ -4053,8 +4053,10 @@ var ftl = (function (exports) {
4053
4053
  }
4054
4054
  }
4055
4055
 
4056
- // Module-isolated string cache for dataset-to-attribute conversions
4056
+ // Module-isolated string cache for dataset-to-attribute conversions, bounded with
4057
+ // FIFO eviction like the expression ast cache
4057
4058
  const attributeCache = new Map();
4059
+ const ATTRIBUTE_CACHE_MAX_SIZE = 1000;
4058
4060
 
4059
4061
  /**
4060
4062
  * Converts a tpl camelCase dataset key into a kebab-case attribute name.
@@ -4065,6 +4067,9 @@ var ftl = (function (exports) {
4065
4067
  function toAttr(dataSetKey) {
4066
4068
  let cached = attributeCache.get(dataSetKey);
4067
4069
  if (!cached) {
4070
+ if (attributeCache.size >= ATTRIBUTE_CACHE_MAX_SIZE) {
4071
+ attributeCache.delete(attributeCache.keys().next().value);
4072
+ }
4068
4073
  cached = dataSetKey
4069
4074
  .substring(3)
4070
4075
  .split(/(?=[A-Z])/)
@@ -4199,9 +4204,10 @@ var ftl = (function (exports) {
4199
4204
  * @param {string} expression
4200
4205
  * @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
4201
4206
  * @param {...*} data
4207
+ * @returns the evaluated expression result
4202
4208
  */
4203
4209
  evaluate(expression, mode, ...data) {
4204
- new ExpressionEvaluator(this.#modules, this.#dataStack).withOverlay(...data).evaluate(expression, mode);
4210
+ return new ExpressionEvaluator(this.#modules, this.#dataStack).withOverlay(...data).evaluate(expression, mode);
4205
4211
  }
4206
4212
  /**
4207
4213
  * Returns an expression evaluator with bound modules and dataStack.
@@ -4346,8 +4352,8 @@ var ftl = (function (exports) {
4346
4352
  if (node.nodeType === Node.TEXT_NODE) {
4347
4353
  node.nodeValue = node.nodeValue.trim();
4348
4354
  }
4349
- for (var n = 0; n < node.childNodes.length; n++) {
4350
- var child = node.childNodes[n];
4355
+ for (let n = 0; n < node.childNodes.length; n++) {
4356
+ const child = node.childNodes[n];
4351
4357
  if (child.nodeType === Node.TEXT_NODE) {
4352
4358
  child.nodeValue = child.nodeValue.trim();
4353
4359
  if (child.nodeValue.length === 0) {
@@ -4366,8 +4372,7 @@ var ftl = (function (exports) {
4366
4372
  #q = new Map();
4367
4373
  constructor() {
4368
4374
  document.addEventListener('DOMContentLoaded', async () => {
4369
- const pending = Array.from(this.entries).map(([child, promise]) => promise);
4370
- await Promise.all(pending);
4375
+ await this.settle();
4371
4376
  document.dispatchEvent(
4372
4377
  new CustomEvent('ftl:ready', {
4373
4378
  bubbles: false,
@@ -4387,6 +4392,16 @@ var ftl = (function (exports) {
4387
4392
  .finally(() => this.#q.delete(el));
4388
4393
  this.#q.set(el, promise);
4389
4394
  }
4395
+ /**
4396
+ * Waits for every queued upgrade, including the ones enqueued while waiting:
4397
+ * a component is only queued once its parent connects it, so a single pass would
4398
+ * miss everything nested.
4399
+ */
4400
+ async settle() {
4401
+ while (this.#q.size !== 0) {
4402
+ await Promise.all(Array.from(this.#q.values()));
4403
+ }
4404
+ }
4390
4405
  get entries() {
4391
4406
  return this.#q.entries();
4392
4407
  }
@@ -4581,18 +4596,30 @@ var ftl = (function (exports) {
4581
4596
  }
4582
4597
  }
4583
4598
 
4584
- class Rendering {
4585
- static async waitFor(el) {
4599
+ /**
4600
+ * Waits for the queued upgrades matching the filter, including the ones enqueued while
4601
+ * waiting: a component is only queued once its parent connects it, so a single pass
4602
+ * would miss everything nested.
4603
+ * @param {(el: Element) => boolean} accept
4604
+ */
4605
+ const settle = async (accept) => {
4606
+ for (;;) {
4586
4607
  const pending = Array.from(registry.upgrades)
4587
- .filter(([child, promise]) => el.contains(child))
4588
- .map(([child, promise]) => promise);
4608
+ .filter(([child]) => accept(child))
4609
+ .map(([, promise]) => promise);
4610
+ if (pending.length === 0) {
4611
+ return;
4612
+ }
4589
4613
  await Promise.all(pending);
4590
4614
  }
4615
+ };
4616
+
4617
+ class Rendering {
4618
+ static async waitFor(el) {
4619
+ await settle((child) => el.contains(child));
4620
+ }
4591
4621
  static async waitForChildren(el) {
4592
- const pending = Array.from(registry.upgrades)
4593
- .filter(([child, promise]) => el !== child && el.contains(child))
4594
- .map(([child, promise]) => promise);
4595
- await Promise.all(pending);
4622
+ await settle((child) => el !== child && el.contains(child));
4596
4623
  }
4597
4624
  }
4598
4625
 
@@ -4634,7 +4661,7 @@ var ftl = (function (exports) {
4634
4661
  template(name) {
4635
4662
  const { modules, data } = registry.context();
4636
4663
  let t = this.#bits().TEMPLATES[name ?? 'default'].withData(data).withModules(modules);
4637
- for (let k of ['l10n', 'config']) {
4664
+ for (const k of ['l10n', 'config']) {
4638
4665
  const v = this.constructor[k];
4639
4666
  if (v) {
4640
4667
  t = t.withOverlay({ [k]: v });