@ztimson/utils 0.25.22 → 0.25.24

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
@@ -392,11 +392,179 @@ function makeUnique(arr) {
392
392
  function makeArray(value) {
393
393
  return Array.isArray(value) ? value : [value];
394
394
  }
395
+ function adjustedInterval(cb, ms) {
396
+ let cancel = false, timeout = null;
397
+ const p = async () => {
398
+ if (cancel) return;
399
+ const start = (/* @__PURE__ */ new Date()).getTime();
400
+ await cb();
401
+ const end = (/* @__PURE__ */ new Date()).getTime();
402
+ timeout = setTimeout(() => p(), ms - (end - start) || 1);
403
+ };
404
+ p();
405
+ return () => {
406
+ cancel = true;
407
+ if (timeout) clearTimeout(timeout);
408
+ };
409
+ }
410
+ function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(), tz) {
411
+ const timezones = [
412
+ ["IDLW", -12],
413
+ ["SST", -11],
414
+ ["HST", -10],
415
+ ["AKST", -9],
416
+ ["PST", -8],
417
+ ["MST", -7],
418
+ ["CST", -6],
419
+ ["EST", -5],
420
+ ["AST", -4],
421
+ ["BRT", -3],
422
+ ["MAT", -2],
423
+ ["AZOT", -1],
424
+ ["UTC", 0],
425
+ ["CET", 1],
426
+ ["EET", 2],
427
+ ["MSK", 3],
428
+ ["AST", 4],
429
+ ["PKT", 5],
430
+ ["IST", 5.5],
431
+ ["BST", 6],
432
+ ["ICT", 7],
433
+ ["CST", 8],
434
+ ["JST", 9],
435
+ ["AEST", 10],
436
+ ["SBT", 11],
437
+ ["FJT", 12],
438
+ ["TOT", 13],
439
+ ["LINT", 14]
440
+ ];
441
+ function adjustTz(date2, gmt) {
442
+ const currentOffset = date2.getTimezoneOffset();
443
+ const adjustedOffset = gmt * 60;
444
+ return new Date(date2.getTime() + (adjustedOffset + currentOffset) * 6e4);
445
+ }
446
+ function day(num) {
447
+ return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][num] || "Unknown";
448
+ }
449
+ function doy(date2) {
450
+ const start = /* @__PURE__ */ new Date(`${date2.getFullYear()}-01-01 0:00:00`);
451
+ return Math.ceil((date2.getTime() - start.getTime()) / (1e3 * 60 * 60 * 24));
452
+ }
453
+ function month(num) {
454
+ return ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][num] || "Unknown";
455
+ }
456
+ function suffix(num) {
457
+ if (num % 100 >= 11 && num % 100 <= 13) return `${num}th`;
458
+ switch (num % 10) {
459
+ case 1:
460
+ return `${num}st`;
461
+ case 2:
462
+ return `${num}nd`;
463
+ case 3:
464
+ return `${num}rd`;
465
+ default:
466
+ return `${num}th`;
467
+ }
468
+ }
469
+ function tzOffset(offset) {
470
+ const hours = ~~(offset / 60);
471
+ const minutes = offset % 60;
472
+ return (offset > 0 ? "-" : "") + `${hours}:${minutes.toString().padStart(2, "0")}`;
473
+ }
474
+ if (typeof date == "number" || typeof date == "string" || date == null) date = new Date(date);
475
+ let t;
476
+ if (tz == null) tz = -(date.getTimezoneOffset() / 60);
477
+ t = timezones.find((t2) => isNaN(tz) ? t2[0] == tz : t2[1] == tz);
478
+ if (!t) throw new Error(`Unknown timezone: ${tz}`);
479
+ date = adjustTz(date, t[1]);
480
+ const tokens = {
481
+ "YYYY": date.getFullYear().toString(),
482
+ "YY": date.getFullYear().toString().slice(2),
483
+ "MMMM": month(date.getMonth()),
484
+ "MMM": month(date.getMonth()).slice(0, 3),
485
+ "MM": (date.getMonth() + 1).toString().padStart(2, "0"),
486
+ "M": (date.getMonth() + 1).toString(),
487
+ "DDD": doy(date).toString(),
488
+ "DD": date.getDate().toString().padStart(2, "0"),
489
+ "Do": suffix(date.getDate()),
490
+ "D": date.getDate().toString(),
491
+ "dddd": day(date.getDay()),
492
+ "ddd": day(date.getDay()).slice(0, 3),
493
+ "HH": date.getHours().toString().padStart(2, "0"),
494
+ "H": date.getHours().toString(),
495
+ "hh": (date.getHours() % 12 || 12).toString().padStart(2, "0"),
496
+ "h": (date.getHours() % 12 || 12).toString(),
497
+ "mm": date.getMinutes().toString().padStart(2, "0"),
498
+ "m": date.getMinutes().toString(),
499
+ "ss": date.getSeconds().toString().padStart(2, "0"),
500
+ "s": date.getSeconds().toString(),
501
+ "SSS": date.getMilliseconds().toString().padStart(3, "0"),
502
+ "A": date.getHours() >= 12 ? "PM" : "AM",
503
+ "a": date.getHours() >= 12 ? "pm" : "am",
504
+ "ZZ": tzOffset(t[1] * 60).replace(":", ""),
505
+ "Z": tzOffset(t[1] * 60),
506
+ "z": typeof tz == "string" ? tz : t[0]
507
+ };
508
+ return format.replace(/YYYY|YY|MMMM|MMM|MM|M|DDD|DD|Do|D|dddd|ddd|HH|H|hh|h|mm|m|ss|s|SSS|A|a|ZZ|Z|z/g, (token) => tokens[token]);
509
+ }
510
+ function instantInterval(fn2, interval) {
511
+ fn2();
512
+ return setInterval(fn2, interval);
513
+ }
514
+ function sleep(ms) {
515
+ return new Promise((res) => setTimeout(res, ms));
516
+ }
517
+ async function sleepWhile(fn2, checkInterval = 100) {
518
+ while (await fn2()) await sleep(checkInterval);
519
+ }
520
+ function timeUntil(date) {
521
+ return (date instanceof Date ? date.getTime() : date) - (/* @__PURE__ */ new Date()).getTime();
522
+ }
523
+ function timezoneOffset(tz, date = /* @__PURE__ */ new Date()) {
524
+ const dtf = new Intl.DateTimeFormat("en-US", {
525
+ timeZone: tz,
526
+ hour12: false,
527
+ year: "numeric",
528
+ month: "2-digit",
529
+ day: "2-digit",
530
+ hour: "2-digit",
531
+ minute: "2-digit",
532
+ second: "2-digit"
533
+ });
534
+ const parts = dtf.formatToParts(date);
535
+ const get = (type) => {
536
+ var _a;
537
+ return Number((_a = parts.find((v) => v.type === type)) == null ? void 0 : _a.value);
538
+ };
539
+ const y = get("year");
540
+ const mo = get("month");
541
+ const d = get("day");
542
+ const h = get("hour");
543
+ const m = get("minute");
544
+ const s = get("second");
545
+ const asUTC = Date.UTC(y, mo - 1, d, h, m, s);
546
+ const asLocal = date.getTime();
547
+ return Math.round((asLocal - asUTC) / 6e4);
548
+ }
549
+ class AsyncLock {
550
+ constructor() {
551
+ __publicField(this, "p", Promise.resolve());
552
+ }
553
+ run(fn2) {
554
+ const res = this.p.then(fn2, fn2);
555
+ this.p = res.then(() => {
556
+ }, () => {
557
+ });
558
+ return res;
559
+ }
560
+ }
395
561
  class Database {
396
562
  constructor(database, tables, version) {
563
+ __publicField(this, "schemaLock", new AsyncLock());
564
+ __publicField(this, "upgrading", false);
397
565
  __publicField(this, "connection");
398
- __publicField(this, "ready", false);
399
566
  __publicField(this, "tables");
567
+ __publicField(this, "waitForUpgrade", () => sleepWhile(() => this.upgrading));
400
568
  this.database = database;
401
569
  this.version = version;
402
570
  this.connection = new Promise((resolve, reject) => {
@@ -423,9 +591,10 @@ class Database {
423
591
  this.version = db.version;
424
592
  resolve(db);
425
593
  }
426
- this.ready = true;
594
+ this.upgrading = false;
427
595
  };
428
596
  req.onupgradeneeded = () => {
597
+ this.upgrading = true;
429
598
  const db = req.result;
430
599
  const existingTables = new ASet(Array.from(db.objectStoreNames));
431
600
  if (tables) {
@@ -442,21 +611,30 @@ class Database {
442
611
  };
443
612
  });
444
613
  }
614
+ get ready() {
615
+ return !this.upgrading;
616
+ }
445
617
  async createTable(table) {
446
- if (typeof table == "string") table = { name: table };
447
- const conn = await this.connection;
448
- if (!this.includes(table.name)) {
449
- conn.close();
450
- Object.assign(this, new Database(this.database, [...this.tables, table], (this.version ?? 0) + 1));
451
- }
452
- return this.table(table.name);
618
+ return this.schemaLock.run(async () => {
619
+ if (typeof table == "string") table = { name: table };
620
+ const conn = await this.connection;
621
+ if (!this.includes(table.name)) {
622
+ conn.close();
623
+ Object.assign(this, new Database(this.database, [...this.tables, table], (this.version ?? 0) + 1));
624
+ await this.connection;
625
+ }
626
+ return this.table(table.name);
627
+ });
453
628
  }
454
629
  async deleteTable(table) {
455
- if (typeof table == "string") table = { name: table };
456
- if (!this.includes(table.name)) return;
457
- const conn = await this.connection;
458
- conn.close();
459
- Object.assign(this, new Database(this.database, this.tables.filter((t) => t.name != table.name), (this.version ?? 0) + 1));
630
+ return this.schemaLock.run(async () => {
631
+ if (typeof table == "string") table = { name: table };
632
+ if (!this.includes(table.name)) return;
633
+ const conn = await this.connection;
634
+ conn.close();
635
+ Object.assign(this, new Database(this.database, this.tables.filter((t) => t.name != table.name), (this.version ?? 0) + 1));
636
+ await this.connection;
637
+ });
460
638
  }
461
639
  includes(name) {
462
640
  return !!this.tables.find((t) => t.name == (typeof name == "object" ? name.name : name.toString()));
@@ -479,6 +657,7 @@ class Table {
479
657
  });
480
658
  }
481
659
  async tx(table, fn2, readonly = false) {
660
+ await this.database.waitForUpgrade();
482
661
  const db = await this.database.connection;
483
662
  const tx = db.transaction(table, readonly ? "readonly" : "readwrite");
484
663
  const store = tx.objectStore(table);
@@ -579,7 +758,20 @@ class Cache {
579
758
  save(key) {
580
759
  if (this.options.storage) {
581
760
  if (this.options.storage instanceof Table) {
582
- this.options.storage.put(key, this.store[key]);
761
+ if (key == null) {
762
+ const rows = this.entries();
763
+ rows.forEach(([k, v]) => {
764
+ var _a;
765
+ return (_a = this.options.storage) == null ? void 0 : _a.put(k, v);
766
+ });
767
+ this.options.storage.getAllKeys().then((keys) => {
768
+ rows.map(([k]) => k).filter((k) => !keys.includes(k)).forEach((k) => {
769
+ var _a;
770
+ return (_a = this.options.storage) == null ? void 0 : _a.delete(k);
771
+ });
772
+ });
773
+ } else if (this.store[key] === void 0) this.options.storage.delete(key);
774
+ else this.options.storage.put(key, this.store[key]);
583
775
  } else if (this.options.storageKey) {
584
776
  this.options.storage.setItem(this.options.storageKey, JSONSanitize(this.store));
585
777
  }
@@ -621,6 +813,7 @@ class Cache {
621
813
  clear() {
622
814
  this.complete = false;
623
815
  this.store = {};
816
+ this.save();
624
817
  return this;
625
818
  }
626
819
  /**
@@ -979,160 +1172,6 @@ class PromiseProgress extends Promise {
979
1172
  return this.from(super.finally(res));
980
1173
  }
981
1174
  }
982
- function adjustedInterval(cb, ms) {
983
- let cancel = false, timeout = null;
984
- const p = async () => {
985
- if (cancel) return;
986
- const start = (/* @__PURE__ */ new Date()).getTime();
987
- await cb();
988
- const end = (/* @__PURE__ */ new Date()).getTime();
989
- timeout = setTimeout(() => p(), ms - (end - start) || 1);
990
- };
991
- p();
992
- return () => {
993
- cancel = true;
994
- if (timeout) clearTimeout(timeout);
995
- };
996
- }
997
- function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(), tz) {
998
- const timezones = [
999
- ["IDLW", -12],
1000
- ["SST", -11],
1001
- ["HST", -10],
1002
- ["AKST", -9],
1003
- ["PST", -8],
1004
- ["MST", -7],
1005
- ["CST", -6],
1006
- ["EST", -5],
1007
- ["AST", -4],
1008
- ["BRT", -3],
1009
- ["MAT", -2],
1010
- ["AZOT", -1],
1011
- ["UTC", 0],
1012
- ["CET", 1],
1013
- ["EET", 2],
1014
- ["MSK", 3],
1015
- ["AST", 4],
1016
- ["PKT", 5],
1017
- ["IST", 5.5],
1018
- ["BST", 6],
1019
- ["ICT", 7],
1020
- ["CST", 8],
1021
- ["JST", 9],
1022
- ["AEST", 10],
1023
- ["SBT", 11],
1024
- ["FJT", 12],
1025
- ["TOT", 13],
1026
- ["LINT", 14]
1027
- ];
1028
- function adjustTz(date2, gmt) {
1029
- const currentOffset = date2.getTimezoneOffset();
1030
- const adjustedOffset = gmt * 60;
1031
- return new Date(date2.getTime() + (adjustedOffset + currentOffset) * 6e4);
1032
- }
1033
- function day(num) {
1034
- return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][num] || "Unknown";
1035
- }
1036
- function doy(date2) {
1037
- const start = /* @__PURE__ */ new Date(`${date2.getFullYear()}-01-01 0:00:00`);
1038
- return Math.ceil((date2.getTime() - start.getTime()) / (1e3 * 60 * 60 * 24));
1039
- }
1040
- function month(num) {
1041
- return ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][num] || "Unknown";
1042
- }
1043
- function suffix(num) {
1044
- if (num % 100 >= 11 && num % 100 <= 13) return `${num}th`;
1045
- switch (num % 10) {
1046
- case 1:
1047
- return `${num}st`;
1048
- case 2:
1049
- return `${num}nd`;
1050
- case 3:
1051
- return `${num}rd`;
1052
- default:
1053
- return `${num}th`;
1054
- }
1055
- }
1056
- function tzOffset(offset) {
1057
- const hours = ~~(offset / 60);
1058
- const minutes = offset % 60;
1059
- return (offset > 0 ? "-" : "") + `${hours}:${minutes.toString().padStart(2, "0")}`;
1060
- }
1061
- if (typeof date == "number" || typeof date == "string" || date == null) date = new Date(date);
1062
- let t;
1063
- if (tz == null) tz = -(date.getTimezoneOffset() / 60);
1064
- t = timezones.find((t2) => isNaN(tz) ? t2[0] == tz : t2[1] == tz);
1065
- if (!t) throw new Error(`Unknown timezone: ${tz}`);
1066
- date = adjustTz(date, t[1]);
1067
- const tokens = {
1068
- "YYYY": date.getFullYear().toString(),
1069
- "YY": date.getFullYear().toString().slice(2),
1070
- "MMMM": month(date.getMonth()),
1071
- "MMM": month(date.getMonth()).slice(0, 3),
1072
- "MM": (date.getMonth() + 1).toString().padStart(2, "0"),
1073
- "M": (date.getMonth() + 1).toString(),
1074
- "DDD": doy(date).toString(),
1075
- "DD": date.getDate().toString().padStart(2, "0"),
1076
- "Do": suffix(date.getDate()),
1077
- "D": date.getDate().toString(),
1078
- "dddd": day(date.getDay()),
1079
- "ddd": day(date.getDay()).slice(0, 3),
1080
- "HH": date.getHours().toString().padStart(2, "0"),
1081
- "H": date.getHours().toString(),
1082
- "hh": (date.getHours() % 12 || 12).toString().padStart(2, "0"),
1083
- "h": (date.getHours() % 12 || 12).toString(),
1084
- "mm": date.getMinutes().toString().padStart(2, "0"),
1085
- "m": date.getMinutes().toString(),
1086
- "ss": date.getSeconds().toString().padStart(2, "0"),
1087
- "s": date.getSeconds().toString(),
1088
- "SSS": date.getMilliseconds().toString().padStart(3, "0"),
1089
- "A": date.getHours() >= 12 ? "PM" : "AM",
1090
- "a": date.getHours() >= 12 ? "pm" : "am",
1091
- "ZZ": tzOffset(t[1] * 60).replace(":", ""),
1092
- "Z": tzOffset(t[1] * 60),
1093
- "z": typeof tz == "string" ? tz : t[0]
1094
- };
1095
- return format.replace(/YYYY|YY|MMMM|MMM|MM|M|DDD|DD|Do|D|dddd|ddd|HH|H|hh|h|mm|m|ss|s|SSS|A|a|ZZ|Z|z/g, (token) => tokens[token]);
1096
- }
1097
- function instantInterval(fn2, interval) {
1098
- fn2();
1099
- return setInterval(fn2, interval);
1100
- }
1101
- function sleep(ms) {
1102
- return new Promise((res) => setTimeout(res, ms));
1103
- }
1104
- async function sleepWhile(fn2, checkInterval = 100) {
1105
- while (await fn2()) await sleep(checkInterval);
1106
- }
1107
- function timeUntil(date) {
1108
- return (date instanceof Date ? date.getTime() : date) - (/* @__PURE__ */ new Date()).getTime();
1109
- }
1110
- function timezoneOffset(tz, date = /* @__PURE__ */ new Date()) {
1111
- const dtf = new Intl.DateTimeFormat("en-US", {
1112
- timeZone: tz,
1113
- hour12: false,
1114
- year: "numeric",
1115
- month: "2-digit",
1116
- day: "2-digit",
1117
- hour: "2-digit",
1118
- minute: "2-digit",
1119
- second: "2-digit"
1120
- });
1121
- const parts = dtf.formatToParts(date);
1122
- const get = (type) => {
1123
- var _a;
1124
- return Number((_a = parts.find((v) => v.type === type)) == null ? void 0 : _a.value);
1125
- };
1126
- const y = get("year");
1127
- const mo = get("month");
1128
- const d = get("day");
1129
- const h = get("hour");
1130
- const m = get("minute");
1131
- const s = get("second");
1132
- const asUTC = Date.UTC(y, mo - 1, d, h, m, s);
1133
- const asLocal = date.getTime();
1134
- return Math.round((asLocal - asUTC) / 6e4);
1135
- }
1136
1175
  function downloadFile(blob, name) {
1137
1176
  if (!(blob instanceof Blob)) blob = new Blob(makeArray(blob));
1138
1177
  const url = URL.createObjectURL(blob);