@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/cache.d.ts CHANGED
@@ -9,6 +9,8 @@ export type CacheOptions = {
9
9
  } | string;
10
10
  /** Keep or delete cached items once expired, defaults to delete */
11
11
  expiryPolicy?: 'delete' | 'keep';
12
+ /** Least Recently Used size limit */
13
+ sizeLimit?: number;
12
14
  };
13
15
  export type CachedValue<T> = T & {
14
16
  _expired?: boolean;
@@ -19,12 +21,14 @@ export type CachedValue<T> = T & {
19
21
  export declare class Cache<K extends string | number | symbol, T> {
20
22
  readonly key?: keyof T | undefined;
21
23
  readonly options: CacheOptions;
24
+ private _loading;
22
25
  private store;
26
+ private timers;
27
+ private lruOrder;
23
28
  /** Support index lookups */
24
29
  [key: string | number | symbol]: CachedValue<T> | any;
25
30
  /** Whether cache is complete */
26
31
  complete: boolean;
27
- private _loading;
28
32
  /** Await initial loading */
29
33
  loading: Promise<void>;
30
34
  /**
@@ -35,6 +39,8 @@ export declare class Cache<K extends string | number | symbol, T> {
35
39
  constructor(key?: keyof T | undefined, options?: CacheOptions);
36
40
  private getKey;
37
41
  private save;
42
+ private clearTimer;
43
+ private touchLRU;
38
44
  /**
39
45
  * Get all cached items
40
46
  * @return {T[]} Array of items
@@ -109,5 +115,5 @@ export declare class Cache<K extends string | number | symbol, T> {
109
115
  * Get all cached items
110
116
  * @return {T[]} Array of items
111
117
  */
112
- values: CachedValue<T>[];
118
+ values: (expired?: boolean) => CachedValue<T>[];
113
119
  }
package/dist/index.cjs CHANGED
@@ -456,17 +456,19 @@ ${opts.message || this.desc}`;
456
456
  * @param options
457
457
  */
458
458
  constructor(key, options = {}) {
459
- __publicField(this, "store", {});
459
+ __publicField(this, "_loading");
460
+ __publicField(this, "store", /* @__PURE__ */ new Map());
461
+ __publicField(this, "timers", /* @__PURE__ */ new Map());
462
+ __publicField(this, "lruOrder", []);
460
463
  /** Whether cache is complete */
461
464
  __publicField(this, "complete", false);
462
- __publicField(this, "_loading");
463
465
  /** Await initial loading */
464
466
  __publicField(this, "loading", new Promise((r) => this._loading = r));
465
467
  /**
466
468
  * Get all cached items
467
469
  * @return {T[]} Array of items
468
470
  */
469
- __publicField(this, "values", this.all());
471
+ __publicField(this, "values", this.all);
470
472
  var _a, _b, _c, _d;
471
473
  this.key = key;
472
474
  this.options = options;
@@ -478,14 +480,18 @@ ${opts.message || this.desc}`;
478
480
  const persists = this.options.persistentStorage;
479
481
  const table = await persists.storage.createTable({ name: persists.key, key: this.key });
480
482
  const rows = await table.getAll();
481
- Object.assign(this.store, rows.reduce((acc, row) => ({ ...acc, [this.getKey(row)]: row }), {}));
483
+ for (const row of rows) this.store.set(this.getKey(row), row);
482
484
  this._loading();
483
485
  })();
484
486
  } else if (((_d = (_c = this.options.persistentStorage) == null ? void 0 : _c.storage) == null ? void 0 : _d.getItem) != void 0) {
485
- const stored = this.options.persistentStorage.storage.getItem(this.options.persistentStorage.key);
486
- if (stored != null) try {
487
- Object.assign(this.store, JSON.parse(stored));
488
- } catch {
487
+ const { storage, key: key2 } = this.options.persistentStorage;
488
+ const stored = storage.getItem(key2);
489
+ if (stored != null) {
490
+ try {
491
+ const obj = JSON.parse(stored);
492
+ for (const k of Object.keys(obj)) this.store.set(k, obj[k]);
493
+ } catch {
494
+ }
489
495
  }
490
496
  this._loading();
491
497
  }
@@ -515,26 +521,50 @@ ${opts.message || this.desc}`;
515
521
  if (!!(persists == null ? void 0 : persists.storage)) {
516
522
  if (((_a = persists.storage) == null ? void 0 : _a.database) != void 0) {
517
523
  persists.storage.createTable({ name: persists.key, key: this.key }).then((table) => {
518
- if (key) {
519
- const value = this.get(key);
524
+ if (key !== void 0) {
525
+ const value = this.get(key, true);
520
526
  if (value != null) table.set(value, key);
521
527
  else table.delete(key);
522
528
  } else {
523
529
  table.clear();
524
- this.all().forEach((row) => table.add(row));
530
+ this.all(true).forEach((row) => table.add(row));
525
531
  }
526
532
  });
527
533
  } else if (((_b = persists.storage) == null ? void 0 : _b.setItem) != void 0) {
528
- persists.storage.setItem(persists.storage.key, JSONSanitize(this.all(true)));
534
+ const obj = {};
535
+ for (const [k, v] of this.store.entries()) obj[k] = v;
536
+ persists.storage.setItem(persists.key, JSONSanitize(obj));
529
537
  }
530
538
  }
531
539
  }
540
+ clearTimer(key) {
541
+ const t = this.timers.get(key);
542
+ if (t) {
543
+ clearTimeout(t);
544
+ this.timers.delete(key);
545
+ }
546
+ }
547
+ touchLRU(key) {
548
+ if (!this.options.sizeLimit || this.options.sizeLimit <= 0) return;
549
+ const idx = this.lruOrder.indexOf(key);
550
+ if (idx >= 0) this.lruOrder.splice(idx, 1);
551
+ this.lruOrder.push(key);
552
+ while (this.lruOrder.length > (this.options.sizeLimit || 0)) {
553
+ const lru = this.lruOrder.shift();
554
+ if (lru !== void 0) this.delete(lru);
555
+ }
556
+ }
532
557
  /**
533
558
  * Get all cached items
534
559
  * @return {T[]} Array of items
535
560
  */
536
561
  all(expired) {
537
- return deepCopy(Object.values(this.store).filter((v) => expired || !v._expired));
562
+ const out = [];
563
+ for (const v of this.store.values()) {
564
+ const val = v;
565
+ if (expired || !(val == null ? void 0 : val._expired)) out.push(deepCopy(val));
566
+ }
567
+ return out;
538
568
  }
539
569
  /**
540
570
  * Add a new item to the cache. Like set, but finds key automatically
@@ -564,7 +594,10 @@ ${opts.message || this.desc}`;
564
594
  */
565
595
  clear() {
566
596
  this.complete = false;
567
- this.store = {};
597
+ for (const [k, t] of this.timers) clearTimeout(t);
598
+ this.timers.clear();
599
+ this.lruOrder = [];
600
+ this.store.clear();
568
601
  this.save();
569
602
  return this;
570
603
  }
@@ -573,7 +606,10 @@ ${opts.message || this.desc}`;
573
606
  * @param {K} key Item's primary key
574
607
  */
575
608
  delete(key) {
576
- delete this.store[key];
609
+ this.clearTimer(key);
610
+ const idx = this.lruOrder.indexOf(key);
611
+ if (idx >= 0) this.lruOrder.splice(idx, 1);
612
+ this.store.delete(key);
577
613
  this.save(key);
578
614
  return this;
579
615
  }
@@ -582,7 +618,12 @@ ${opts.message || this.desc}`;
582
618
  * @return {[K, T][]} Key-value pairs array
583
619
  */
584
620
  entries(expired) {
585
- return deepCopy(Object.entries(this.store).filter((v) => expired || !(v == null ? void 0 : v._expired)));
621
+ const out = [];
622
+ for (const [k, v] of this.store.entries()) {
623
+ const val = v;
624
+ if (expired || !(val == null ? void 0 : val._expired)) out.push([k, deepCopy(val)]);
625
+ }
626
+ return out;
586
627
  }
587
628
  /**
588
629
  * Manually expire a cached item
@@ -591,8 +632,12 @@ ${opts.message || this.desc}`;
591
632
  expire(key) {
592
633
  this.complete = false;
593
634
  if (this.options.expiryPolicy == "keep") {
594
- this.store[key]._expired = true;
595
- this.save(key);
635
+ const v = this.store.get(key);
636
+ if (v) {
637
+ v._expired = true;
638
+ this.store.set(key, v);
639
+ this.save(key);
640
+ }
596
641
  } else this.delete(key);
597
642
  return this;
598
643
  }
@@ -603,7 +648,11 @@ ${opts.message || this.desc}`;
603
648
  * @returns {T | undefined} Cached item or undefined if nothing matched
604
649
  */
605
650
  find(filter, expired) {
606
- return Object.values(this.store).find((row) => (expired || !row._expired) && includes(row, filter));
651
+ for (const v of this.store.values()) {
652
+ const row = v;
653
+ if ((expired || !row._expired) && includes(row, filter)) return deepCopy(row);
654
+ }
655
+ return void 0;
607
656
  }
608
657
  /**
609
658
  * Get item from the cache
@@ -612,7 +661,10 @@ ${opts.message || this.desc}`;
612
661
  * @return {T} Cached item
613
662
  */
614
663
  get(key, expired) {
615
- const cached = deepCopy(this.store[key] ?? null);
664
+ const raw = this.store.get(key);
665
+ if (raw == null) return null;
666
+ const cached = deepCopy(raw);
667
+ this.touchLRU(key);
616
668
  if (expired || !(cached == null ? void 0 : cached._expired)) return cached;
617
669
  return null;
618
670
  }
@@ -621,17 +673,23 @@ ${opts.message || this.desc}`;
621
673
  * @return {K[]} Array of keys
622
674
  */
623
675
  keys(expired) {
624
- return Object.keys(this.store).filter((k) => expired || !this.store[k]._expired);
676
+ const out = [];
677
+ for (const [k, v] of this.store.entries()) {
678
+ const val = v;
679
+ if (expired || !(val == null ? void 0 : val._expired)) out.push(k);
680
+ }
681
+ return out;
625
682
  }
626
683
  /**
627
684
  * Get map of cached items
628
685
  * @return {Record<K, T>}
629
686
  */
630
687
  map(expired) {
631
- const copy = deepCopy(this.store);
632
- if (!expired) Object.keys(copy).forEach((k) => {
633
- if (copy[k]._expired) delete copy[k];
634
- });
688
+ const copy = {};
689
+ for (const [k, v] of this.store.entries()) {
690
+ const val = v;
691
+ if (expired || !(val == null ? void 0 : val._expired)) copy[k] = deepCopy(val);
692
+ }
635
693
  return copy;
636
694
  }
637
695
  /**
@@ -643,12 +701,17 @@ ${opts.message || this.desc}`;
643
701
  */
644
702
  set(key, value, ttl = this.options.ttl) {
645
703
  if (this.options.expiryPolicy == "keep") delete value._expired;
646
- this.store[key] = value;
704
+ this.clearTimer(key);
705
+ this.store.set(key, value);
706
+ this.touchLRU(key);
647
707
  this.save(key);
648
- if (ttl) setTimeout(() => {
649
- this.expire(key);
650
- this.save(key);
651
- }, (ttl || 0) * 1e3);
708
+ if (ttl) {
709
+ const t = setTimeout(() => {
710
+ this.expire(key);
711
+ this.save(key);
712
+ }, (ttl || 0) * 1e3);
713
+ this.timers.set(key, t);
714
+ }
652
715
  return this;
653
716
  }
654
717
  }
@@ -893,33 +956,6 @@ ${opts.message || this.desc}`;
893
956
  }).join(","))
894
957
  ].join("\n");
895
958
  }
896
- function dec2Frac(num, maxDen = 1e3) {
897
- let sign = Math.sign(num);
898
- num = Math.abs(num);
899
- if (Number.isInteger(num)) return sign * num + "";
900
- let closest = { n: 0, d: 1, diff: Math.abs(num) };
901
- for (let d = 1; d <= maxDen; d++) {
902
- let n = Math.round(num * d);
903
- let diff = Math.abs(num - n / d);
904
- if (diff < closest.diff) {
905
- closest = { n, d, diff };
906
- if (diff < 1e-8) break;
907
- }
908
- }
909
- let integer = Math.floor(closest.n / closest.d);
910
- let numerator = closest.n - integer * closest.d;
911
- return (sign < 0 ? "-" : "") + (integer ? integer + " " : "") + (numerator ? numerator + "/" + closest.d : "");
912
- }
913
- function fracToDec(frac) {
914
- let split = frac.split(" ");
915
- const whole = split.length == 2 ? Number(split[0]) : 0;
916
- split = split.pop().split("/");
917
- return whole + Number(split[0]) / Number(split[1]);
918
- }
919
- function numSuffix(n) {
920
- const s = ["th", "st", "nd", "rd"], v = n % 100;
921
- return `${n}${s[(v - 20) % 10] || s[v] || s[0]}`;
922
- }
923
959
  function adjustedInterval(cb, ms) {
924
960
  let cancel = false, timeout = null;
925
961
  const p = async () => {
@@ -957,28 +993,56 @@ ${opts.message || this.desc}`;
957
993
  }
958
994
  }
959
995
  let zonedDate = new Date(date);
996
+ let get;
997
+ const partsMap = {};
960
998
  if (!numericTz && tzName !== "UTC") {
961
999
  const parts = new Intl.DateTimeFormat("en-US", {
962
1000
  timeZone: tzName,
963
1001
  year: "numeric",
964
1002
  month: "2-digit",
965
1003
  day: "2-digit",
1004
+ weekday: "long",
966
1005
  hour: "2-digit",
967
1006
  minute: "2-digit",
968
1007
  second: "2-digit",
969
1008
  hour12: false
970
1009
  }).formatToParts(date);
971
- const get2 = (type) => {
972
- var _a2;
973
- return (_a2 = parts.find((p) => p.type === type)) == null ? void 0 : _a2.value;
1010
+ parts.forEach((p) => {
1011
+ partsMap[p.type] = p.value;
1012
+ });
1013
+ const monthValue = parseInt(partsMap.month) - 1;
1014
+ const dayOfWeekValue = (/* @__PURE__ */ new Date(`${partsMap.year}-${partsMap.month}-${partsMap.day}`)).getDay();
1015
+ const hourValue = parseInt(partsMap.hour);
1016
+ get = (fn2) => {
1017
+ switch (fn2) {
1018
+ case "FullYear":
1019
+ return parseInt(partsMap.year);
1020
+ case "Month":
1021
+ return monthValue;
1022
+ case "Date":
1023
+ return parseInt(partsMap.day);
1024
+ case "Day":
1025
+ return dayOfWeekValue;
1026
+ case "Hours":
1027
+ return hourValue;
1028
+ case "Minutes":
1029
+ return parseInt(partsMap.minute);
1030
+ case "Seconds":
1031
+ return parseInt(partsMap.second);
1032
+ default:
1033
+ return 0;
1034
+ }
974
1035
  };
975
- const build = `${get2("year")}-${get2("month")}-${get2("day")}T${get2("hour")}:${get2("minute")}:${get2("second")}Z`;
976
- zonedDate = new Date(build);
977
- } else if (numericTz || tzName === "UTC") {
1036
+ } else {
978
1037
  const offset = numericTz ? tz : 0;
979
1038
  zonedDate = new Date(date.getTime() + offset * 60 * 60 * 1e3);
1039
+ get = (fn2) => zonedDate[`getUTC${fn2}`]();
1040
+ }
1041
+ function numSuffix2(n) {
1042
+ const s = ["th", "st", "nd", "rd"];
1043
+ const v = n % 100;
1044
+ return n + (s[(v - 20) % 10] || s[v] || s[0]);
980
1045
  }
981
- const get = (fn2) => numericTz || tzName === "UTC" ? zonedDate[`getUTC${fn2}`]() : zonedDate[`get${fn2}`]();
982
1046
  function getTZOffset() {
983
1047
  var _a2, _b;
984
1048
  if (numericTz) {
@@ -1012,7 +1076,7 @@ ${opts.message || this.desc}`;
1012
1076
  M: (get("Month") + 1).toString(),
1013
1077
  DDD: dayOfYear(zonedDate).toString(),
1014
1078
  DD: get("Date").toString().padStart(2, "0"),
1015
- Do: numSuffix(get("Date")),
1079
+ Do: numSuffix2(get("Date")),
1016
1080
  D: get("Date").toString(),
1017
1081
  dddd: dayOfWeek(get("Day")),
1018
1082
  ddd: dayOfWeek(get("Day")).slice(0, 3),
@@ -1024,7 +1088,7 @@ ${opts.message || this.desc}`;
1024
1088
  m: get("Minutes").toString(),
1025
1089
  ss: get("Seconds").toString().padStart(2, "0"),
1026
1090
  s: get("Seconds").toString(),
1027
- SSS: get("Milliseconds").toString().padStart(3, "0"),
1091
+ SSS: zonedDate[`getUTC${"Milliseconds"}`]().toString().padStart(3, "0"),
1028
1092
  A: get("Hours") >= 12 ? "PM" : "AM",
1029
1093
  a: get("Hours") >= 12 ? "pm" : "am",
1030
1094
  ZZ: getTZOffset().replace(":", ""),
@@ -1788,6 +1852,33 @@ ${opts.message || this.desc}`;
1788
1852
  };
1789
1853
  __publicField(_Logger, "LOG_LEVEL", 4);
1790
1854
  let Logger = _Logger;
1855
+ function dec2Frac(num, maxDen = 1e3) {
1856
+ let sign = Math.sign(num);
1857
+ num = Math.abs(num);
1858
+ if (Number.isInteger(num)) return sign * num + "";
1859
+ let closest = { n: 0, d: 1, diff: Math.abs(num) };
1860
+ for (let d = 1; d <= maxDen; d++) {
1861
+ let n = Math.round(num * d);
1862
+ let diff = Math.abs(num - n / d);
1863
+ if (diff < closest.diff) {
1864
+ closest = { n, d, diff };
1865
+ if (diff < 1e-8) break;
1866
+ }
1867
+ }
1868
+ let integer = Math.floor(closest.n / closest.d);
1869
+ let numerator = closest.n - integer * closest.d;
1870
+ return (sign < 0 ? "-" : "") + (integer ? integer + " " : "") + (numerator ? numerator + "/" + closest.d : "");
1871
+ }
1872
+ function fracToDec(frac) {
1873
+ let split = frac.split(" ");
1874
+ const whole = split.length == 2 ? Number(split[0]) : 0;
1875
+ split = split.pop().split("/");
1876
+ return whole + Number(split[0]) / Number(split[1]);
1877
+ }
1878
+ function numSuffix(n) {
1879
+ const s = ["th", "st", "nd", "rd"], v = n % 100;
1880
+ return `${n}${s[(v - 20) % 10] || s[v] || s[0]}`;
1881
+ }
1791
1882
  function compareVersions(target, vs) {
1792
1883
  const [tMajor, tMinor, tPatch] = target.split(".").map((v) => +v.replace(/[^0-9]/g, ""));
1793
1884
  const [vMajor, vMinor, vPatch] = vs.split(".").map((v) => +v.replace(/[^0-9]/g, ""));