@ztimson/utils 0.27.3 → 0.27.5

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.
package/dist/index.mjs CHANGED
@@ -452,17 +452,19 @@ class Cache {
452
452
  * @param options
453
453
  */
454
454
  constructor(key, options = {}) {
455
- __publicField(this, "store", {});
455
+ __publicField(this, "_loading");
456
+ __publicField(this, "store", /* @__PURE__ */ new Map());
457
+ __publicField(this, "timers", /* @__PURE__ */ new Map());
458
+ __publicField(this, "lruOrder", []);
456
459
  /** Whether cache is complete */
457
460
  __publicField(this, "complete", false);
458
- __publicField(this, "_loading");
459
461
  /** Await initial loading */
460
462
  __publicField(this, "loading", new Promise((r) => this._loading = r));
461
463
  /**
462
464
  * Get all cached items
463
465
  * @return {T[]} Array of items
464
466
  */
465
- __publicField(this, "values", this.all());
467
+ __publicField(this, "values", this.all);
466
468
  var _a, _b, _c, _d;
467
469
  this.key = key;
468
470
  this.options = options;
@@ -474,14 +476,18 @@ class Cache {
474
476
  const persists = this.options.persistentStorage;
475
477
  const table = await persists.storage.createTable({ name: persists.key, key: this.key });
476
478
  const rows = await table.getAll();
477
- Object.assign(this.store, rows.reduce((acc, row) => ({ ...acc, [this.getKey(row)]: row }), {}));
479
+ for (const row of rows) this.store.set(this.getKey(row), row);
478
480
  this._loading();
479
481
  })();
480
482
  } else if (((_d = (_c = this.options.persistentStorage) == null ? void 0 : _c.storage) == null ? void 0 : _d.getItem) != void 0) {
481
- const stored = this.options.persistentStorage.storage.getItem(this.options.persistentStorage.key);
482
- if (stored != null) try {
483
- Object.assign(this.store, JSON.parse(stored));
484
- } catch {
483
+ const { storage, key: key2 } = this.options.persistentStorage;
484
+ const stored = storage.getItem(key2);
485
+ if (stored != null) {
486
+ try {
487
+ const obj = JSON.parse(stored);
488
+ for (const k of Object.keys(obj)) this.store.set(k, obj[k]);
489
+ } catch {
490
+ }
485
491
  }
486
492
  this._loading();
487
493
  }
@@ -511,26 +517,50 @@ class Cache {
511
517
  if (!!(persists == null ? void 0 : persists.storage)) {
512
518
  if (((_a = persists.storage) == null ? void 0 : _a.database) != void 0) {
513
519
  persists.storage.createTable({ name: persists.key, key: this.key }).then((table) => {
514
- if (key) {
515
- const value = this.get(key);
520
+ if (key !== void 0) {
521
+ const value = this.get(key, true);
516
522
  if (value != null) table.set(value, key);
517
523
  else table.delete(key);
518
524
  } else {
519
525
  table.clear();
520
- this.all().forEach((row) => table.add(row));
526
+ this.all(true).forEach((row) => table.add(row));
521
527
  }
522
528
  });
523
529
  } else if (((_b = persists.storage) == null ? void 0 : _b.setItem) != void 0) {
524
- persists.storage.setItem(persists.storage.key, JSONSanitize(this.all(true)));
530
+ const obj = {};
531
+ for (const [k, v] of this.store.entries()) obj[k] = v;
532
+ persists.storage.setItem(persists.key, JSONSanitize(obj));
525
533
  }
526
534
  }
527
535
  }
536
+ clearTimer(key) {
537
+ const t = this.timers.get(key);
538
+ if (t) {
539
+ clearTimeout(t);
540
+ this.timers.delete(key);
541
+ }
542
+ }
543
+ touchLRU(key) {
544
+ if (!this.options.sizeLimit || this.options.sizeLimit <= 0) return;
545
+ const idx = this.lruOrder.indexOf(key);
546
+ if (idx >= 0) this.lruOrder.splice(idx, 1);
547
+ this.lruOrder.push(key);
548
+ while (this.lruOrder.length > (this.options.sizeLimit || 0)) {
549
+ const lru = this.lruOrder.shift();
550
+ if (lru !== void 0) this.delete(lru);
551
+ }
552
+ }
528
553
  /**
529
554
  * Get all cached items
530
555
  * @return {T[]} Array of items
531
556
  */
532
557
  all(expired) {
533
- return deepCopy(Object.values(this.store).filter((v) => expired || !v._expired));
558
+ const out = [];
559
+ for (const v of this.store.values()) {
560
+ const val = v;
561
+ if (expired || !(val == null ? void 0 : val._expired)) out.push(deepCopy(val));
562
+ }
563
+ return out;
534
564
  }
535
565
  /**
536
566
  * Add a new item to the cache. Like set, but finds key automatically
@@ -560,7 +590,10 @@ class Cache {
560
590
  */
561
591
  clear() {
562
592
  this.complete = false;
563
- this.store = {};
593
+ for (const [k, t] of this.timers) clearTimeout(t);
594
+ this.timers.clear();
595
+ this.lruOrder = [];
596
+ this.store.clear();
564
597
  this.save();
565
598
  return this;
566
599
  }
@@ -569,7 +602,10 @@ class Cache {
569
602
  * @param {K} key Item's primary key
570
603
  */
571
604
  delete(key) {
572
- delete this.store[key];
605
+ this.clearTimer(key);
606
+ const idx = this.lruOrder.indexOf(key);
607
+ if (idx >= 0) this.lruOrder.splice(idx, 1);
608
+ this.store.delete(key);
573
609
  this.save(key);
574
610
  return this;
575
611
  }
@@ -578,7 +614,12 @@ class Cache {
578
614
  * @return {[K, T][]} Key-value pairs array
579
615
  */
580
616
  entries(expired) {
581
- return deepCopy(Object.entries(this.store).filter((v) => expired || !(v == null ? void 0 : v._expired)));
617
+ const out = [];
618
+ for (const [k, v] of this.store.entries()) {
619
+ const val = v;
620
+ if (expired || !(val == null ? void 0 : val._expired)) out.push([k, deepCopy(val)]);
621
+ }
622
+ return out;
582
623
  }
583
624
  /**
584
625
  * Manually expire a cached item
@@ -587,8 +628,12 @@ class Cache {
587
628
  expire(key) {
588
629
  this.complete = false;
589
630
  if (this.options.expiryPolicy == "keep") {
590
- this.store[key]._expired = true;
591
- this.save(key);
631
+ const v = this.store.get(key);
632
+ if (v) {
633
+ v._expired = true;
634
+ this.store.set(key, v);
635
+ this.save(key);
636
+ }
592
637
  } else this.delete(key);
593
638
  return this;
594
639
  }
@@ -599,7 +644,11 @@ class Cache {
599
644
  * @returns {T | undefined} Cached item or undefined if nothing matched
600
645
  */
601
646
  find(filter, expired) {
602
- return Object.values(this.store).find((row) => (expired || !row._expired) && includes(row, filter));
647
+ for (const v of this.store.values()) {
648
+ const row = v;
649
+ if ((expired || !row._expired) && includes(row, filter)) return deepCopy(row);
650
+ }
651
+ return void 0;
603
652
  }
604
653
  /**
605
654
  * Get item from the cache
@@ -608,7 +657,10 @@ class Cache {
608
657
  * @return {T} Cached item
609
658
  */
610
659
  get(key, expired) {
611
- const cached = deepCopy(this.store[key] ?? null);
660
+ const raw = this.store.get(key);
661
+ if (raw == null) return null;
662
+ const cached = deepCopy(raw);
663
+ this.touchLRU(key);
612
664
  if (expired || !(cached == null ? void 0 : cached._expired)) return cached;
613
665
  return null;
614
666
  }
@@ -617,17 +669,23 @@ class Cache {
617
669
  * @return {K[]} Array of keys
618
670
  */
619
671
  keys(expired) {
620
- return Object.keys(this.store).filter((k) => expired || !this.store[k]._expired);
672
+ const out = [];
673
+ for (const [k, v] of this.store.entries()) {
674
+ const val = v;
675
+ if (expired || !(val == null ? void 0 : val._expired)) out.push(k);
676
+ }
677
+ return out;
621
678
  }
622
679
  /**
623
680
  * Get map of cached items
624
681
  * @return {Record<K, T>}
625
682
  */
626
683
  map(expired) {
627
- const copy = deepCopy(this.store);
628
- if (!expired) Object.keys(copy).forEach((k) => {
629
- if (copy[k]._expired) delete copy[k];
630
- });
684
+ const copy = {};
685
+ for (const [k, v] of this.store.entries()) {
686
+ const val = v;
687
+ if (expired || !(val == null ? void 0 : val._expired)) copy[k] = deepCopy(val);
688
+ }
631
689
  return copy;
632
690
  }
633
691
  /**
@@ -639,12 +697,17 @@ class Cache {
639
697
  */
640
698
  set(key, value, ttl = this.options.ttl) {
641
699
  if (this.options.expiryPolicy == "keep") delete value._expired;
642
- this.store[key] = value;
700
+ this.clearTimer(key);
701
+ this.store.set(key, value);
702
+ this.touchLRU(key);
643
703
  this.save(key);
644
- if (ttl) setTimeout(() => {
645
- this.expire(key);
646
- this.save(key);
647
- }, (ttl || 0) * 1e3);
704
+ if (ttl) {
705
+ const t = setTimeout(() => {
706
+ this.expire(key);
707
+ this.save(key);
708
+ }, (ttl || 0) * 1e3);
709
+ this.timers.set(key, t);
710
+ }
648
711
  return this;
649
712
  }
650
713
  }
@@ -889,33 +952,6 @@ function toCsv(target, flatten = true) {
889
952
  }).join(","))
890
953
  ].join("\n");
891
954
  }
892
- function dec2Frac(num, maxDen = 1e3) {
893
- let sign = Math.sign(num);
894
- num = Math.abs(num);
895
- if (Number.isInteger(num)) return sign * num + "";
896
- let closest = { n: 0, d: 1, diff: Math.abs(num) };
897
- for (let d = 1; d <= maxDen; d++) {
898
- let n = Math.round(num * d);
899
- let diff = Math.abs(num - n / d);
900
- if (diff < closest.diff) {
901
- closest = { n, d, diff };
902
- if (diff < 1e-8) break;
903
- }
904
- }
905
- let integer = Math.floor(closest.n / closest.d);
906
- let numerator = closest.n - integer * closest.d;
907
- return (sign < 0 ? "-" : "") + (integer ? integer + " " : "") + (numerator ? numerator + "/" + closest.d : "");
908
- }
909
- function fracToDec(frac) {
910
- let split = frac.split(" ");
911
- const whole = split.length == 2 ? Number(split[0]) : 0;
912
- split = split.pop().split("/");
913
- return whole + Number(split[0]) / Number(split[1]);
914
- }
915
- function numSuffix(n) {
916
- const s = ["th", "st", "nd", "rd"], v = n % 100;
917
- return `${n}${s[(v - 20) % 10] || s[v] || s[0]}`;
918
- }
919
955
  function adjustedInterval(cb, ms) {
920
956
  let cancel = false, timeout = null;
921
957
  const p = async () => {
@@ -953,28 +989,56 @@ function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(
953
989
  }
954
990
  }
955
991
  let zonedDate = new Date(date);
992
+ let get;
993
+ const partsMap = {};
956
994
  if (!numericTz && tzName !== "UTC") {
957
995
  const parts = new Intl.DateTimeFormat("en-US", {
958
996
  timeZone: tzName,
959
997
  year: "numeric",
960
998
  month: "2-digit",
961
999
  day: "2-digit",
1000
+ weekday: "long",
962
1001
  hour: "2-digit",
963
1002
  minute: "2-digit",
964
1003
  second: "2-digit",
965
1004
  hour12: false
966
1005
  }).formatToParts(date);
967
- const get2 = (type) => {
968
- var _a2;
969
- return (_a2 = parts.find((p) => p.type === type)) == null ? void 0 : _a2.value;
1006
+ parts.forEach((p) => {
1007
+ partsMap[p.type] = p.value;
1008
+ });
1009
+ const monthValue = parseInt(partsMap.month) - 1;
1010
+ const dayOfWeekValue = (/* @__PURE__ */ new Date(`${partsMap.year}-${partsMap.month}-${partsMap.day}`)).getDay();
1011
+ const hourValue = parseInt(partsMap.hour);
1012
+ get = (fn2) => {
1013
+ switch (fn2) {
1014
+ case "FullYear":
1015
+ return parseInt(partsMap.year);
1016
+ case "Month":
1017
+ return monthValue;
1018
+ case "Date":
1019
+ return parseInt(partsMap.day);
1020
+ case "Day":
1021
+ return dayOfWeekValue;
1022
+ case "Hours":
1023
+ return hourValue;
1024
+ case "Minutes":
1025
+ return parseInt(partsMap.minute);
1026
+ case "Seconds":
1027
+ return parseInt(partsMap.second);
1028
+ default:
1029
+ return 0;
1030
+ }
970
1031
  };
971
- const build = `${get2("year")}-${get2("month")}-${get2("day")}T${get2("hour")}:${get2("minute")}:${get2("second")}Z`;
972
- zonedDate = new Date(build);
973
- } else if (numericTz || tzName === "UTC") {
1032
+ } else {
974
1033
  const offset = numericTz ? tz : 0;
975
1034
  zonedDate = new Date(date.getTime() + offset * 60 * 60 * 1e3);
1035
+ get = (fn2) => zonedDate[`getUTC${fn2}`]();
1036
+ }
1037
+ function numSuffix2(n) {
1038
+ const s = ["th", "st", "nd", "rd"];
1039
+ const v = n % 100;
1040
+ return n + (s[(v - 20) % 10] || s[v] || s[0]);
976
1041
  }
977
- const get = (fn2) => numericTz || tzName === "UTC" ? zonedDate[`getUTC${fn2}`]() : zonedDate[`get${fn2}`]();
978
1042
  function getTZOffset() {
979
1043
  var _a2, _b;
980
1044
  if (numericTz) {
@@ -1008,7 +1072,7 @@ function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(
1008
1072
  M: (get("Month") + 1).toString(),
1009
1073
  DDD: dayOfYear(zonedDate).toString(),
1010
1074
  DD: get("Date").toString().padStart(2, "0"),
1011
- Do: numSuffix(get("Date")),
1075
+ Do: numSuffix2(get("Date")),
1012
1076
  D: get("Date").toString(),
1013
1077
  dddd: dayOfWeek(get("Day")),
1014
1078
  ddd: dayOfWeek(get("Day")).slice(0, 3),
@@ -1020,7 +1084,7 @@ function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(
1020
1084
  m: get("Minutes").toString(),
1021
1085
  ss: get("Seconds").toString().padStart(2, "0"),
1022
1086
  s: get("Seconds").toString(),
1023
- SSS: get("Milliseconds").toString().padStart(3, "0"),
1087
+ SSS: zonedDate[`getUTC${"Milliseconds"}`]().toString().padStart(3, "0"),
1024
1088
  A: get("Hours") >= 12 ? "PM" : "AM",
1025
1089
  a: get("Hours") >= 12 ? "pm" : "am",
1026
1090
  ZZ: getTZOffset().replace(":", ""),
@@ -1784,6 +1848,33 @@ const _Logger = class _Logger extends TypedEmitter {
1784
1848
  };
1785
1849
  __publicField(_Logger, "LOG_LEVEL", 4);
1786
1850
  let Logger = _Logger;
1851
+ function dec2Frac(num, maxDen = 1e3) {
1852
+ let sign = Math.sign(num);
1853
+ num = Math.abs(num);
1854
+ if (Number.isInteger(num)) return sign * num + "";
1855
+ let closest = { n: 0, d: 1, diff: Math.abs(num) };
1856
+ for (let d = 1; d <= maxDen; d++) {
1857
+ let n = Math.round(num * d);
1858
+ let diff = Math.abs(num - n / d);
1859
+ if (diff < closest.diff) {
1860
+ closest = { n, d, diff };
1861
+ if (diff < 1e-8) break;
1862
+ }
1863
+ }
1864
+ let integer = Math.floor(closest.n / closest.d);
1865
+ let numerator = closest.n - integer * closest.d;
1866
+ return (sign < 0 ? "-" : "") + (integer ? integer + " " : "") + (numerator ? numerator + "/" + closest.d : "");
1867
+ }
1868
+ function fracToDec(frac) {
1869
+ let split = frac.split(" ");
1870
+ const whole = split.length == 2 ? Number(split[0]) : 0;
1871
+ split = split.pop().split("/");
1872
+ return whole + Number(split[0]) / Number(split[1]);
1873
+ }
1874
+ function numSuffix(n) {
1875
+ const s = ["th", "st", "nd", "rd"], v = n % 100;
1876
+ return `${n}${s[(v - 20) % 10] || s[v] || s[0]}`;
1877
+ }
1787
1878
  function compareVersions(target, vs) {
1788
1879
  const [tMajor, tMinor, tPatch] = target.split(".").map((v) => +v.replace(/[^0-9]/g, ""));
1789
1880
  const [vMajor, vMinor, vPatch] = vs.split(".").map((v) => +v.replace(/[^0-9]/g, ""));