@ztimson/utils 0.25.23 → 0.25.25

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);
@@ -580,17 +759,16 @@ class Cache {
580
759
  if (this.options.storage) {
581
760
  if (this.options.storage instanceof Table) {
582
761
  if (key == null) {
583
- const rows = this.entries();
762
+ let rows = this.entries();
584
763
  rows.forEach(([k, v]) => {
585
764
  var _a;
586
765
  return (_a = this.options.storage) == null ? void 0 : _a.put(k, v);
587
766
  });
588
- this.options.storage.getAllKeys().then((keys) => {
589
- rows.map(([k]) => k).filter((k) => !keys.includes(k)).forEach((k) => {
590
- var _a;
591
- return (_a = this.options.storage) == null ? void 0 : _a.delete(k);
592
- });
593
- });
767
+ rows = rows.map(([k]) => k);
768
+ this.options.storage.getAllKeys().then((keys) => keys.filter((k) => !rows.includes(k)).forEach((k) => {
769
+ var _a;
770
+ return (_a = this.options.storage) == null ? void 0 : _a.delete(k);
771
+ }));
594
772
  } else if (this.store[key] === void 0) this.options.storage.delete(key);
595
773
  else this.options.storage.put(key, this.store[key]);
596
774
  } else if (this.options.storageKey) {
@@ -993,160 +1171,6 @@ class PromiseProgress extends Promise {
993
1171
  return this.from(super.finally(res));
994
1172
  }
995
1173
  }
996
- function adjustedInterval(cb, ms) {
997
- let cancel = false, timeout = null;
998
- const p = async () => {
999
- if (cancel) return;
1000
- const start = (/* @__PURE__ */ new Date()).getTime();
1001
- await cb();
1002
- const end = (/* @__PURE__ */ new Date()).getTime();
1003
- timeout = setTimeout(() => p(), ms - (end - start) || 1);
1004
- };
1005
- p();
1006
- return () => {
1007
- cancel = true;
1008
- if (timeout) clearTimeout(timeout);
1009
- };
1010
- }
1011
- function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(), tz) {
1012
- const timezones = [
1013
- ["IDLW", -12],
1014
- ["SST", -11],
1015
- ["HST", -10],
1016
- ["AKST", -9],
1017
- ["PST", -8],
1018
- ["MST", -7],
1019
- ["CST", -6],
1020
- ["EST", -5],
1021
- ["AST", -4],
1022
- ["BRT", -3],
1023
- ["MAT", -2],
1024
- ["AZOT", -1],
1025
- ["UTC", 0],
1026
- ["CET", 1],
1027
- ["EET", 2],
1028
- ["MSK", 3],
1029
- ["AST", 4],
1030
- ["PKT", 5],
1031
- ["IST", 5.5],
1032
- ["BST", 6],
1033
- ["ICT", 7],
1034
- ["CST", 8],
1035
- ["JST", 9],
1036
- ["AEST", 10],
1037
- ["SBT", 11],
1038
- ["FJT", 12],
1039
- ["TOT", 13],
1040
- ["LINT", 14]
1041
- ];
1042
- function adjustTz(date2, gmt) {
1043
- const currentOffset = date2.getTimezoneOffset();
1044
- const adjustedOffset = gmt * 60;
1045
- return new Date(date2.getTime() + (adjustedOffset + currentOffset) * 6e4);
1046
- }
1047
- function day(num) {
1048
- return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][num] || "Unknown";
1049
- }
1050
- function doy(date2) {
1051
- const start = /* @__PURE__ */ new Date(`${date2.getFullYear()}-01-01 0:00:00`);
1052
- return Math.ceil((date2.getTime() - start.getTime()) / (1e3 * 60 * 60 * 24));
1053
- }
1054
- function month(num) {
1055
- return ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][num] || "Unknown";
1056
- }
1057
- function suffix(num) {
1058
- if (num % 100 >= 11 && num % 100 <= 13) return `${num}th`;
1059
- switch (num % 10) {
1060
- case 1:
1061
- return `${num}st`;
1062
- case 2:
1063
- return `${num}nd`;
1064
- case 3:
1065
- return `${num}rd`;
1066
- default:
1067
- return `${num}th`;
1068
- }
1069
- }
1070
- function tzOffset(offset) {
1071
- const hours = ~~(offset / 60);
1072
- const minutes = offset % 60;
1073
- return (offset > 0 ? "-" : "") + `${hours}:${minutes.toString().padStart(2, "0")}`;
1074
- }
1075
- if (typeof date == "number" || typeof date == "string" || date == null) date = new Date(date);
1076
- let t;
1077
- if (tz == null) tz = -(date.getTimezoneOffset() / 60);
1078
- t = timezones.find((t2) => isNaN(tz) ? t2[0] == tz : t2[1] == tz);
1079
- if (!t) throw new Error(`Unknown timezone: ${tz}`);
1080
- date = adjustTz(date, t[1]);
1081
- const tokens = {
1082
- "YYYY": date.getFullYear().toString(),
1083
- "YY": date.getFullYear().toString().slice(2),
1084
- "MMMM": month(date.getMonth()),
1085
- "MMM": month(date.getMonth()).slice(0, 3),
1086
- "MM": (date.getMonth() + 1).toString().padStart(2, "0"),
1087
- "M": (date.getMonth() + 1).toString(),
1088
- "DDD": doy(date).toString(),
1089
- "DD": date.getDate().toString().padStart(2, "0"),
1090
- "Do": suffix(date.getDate()),
1091
- "D": date.getDate().toString(),
1092
- "dddd": day(date.getDay()),
1093
- "ddd": day(date.getDay()).slice(0, 3),
1094
- "HH": date.getHours().toString().padStart(2, "0"),
1095
- "H": date.getHours().toString(),
1096
- "hh": (date.getHours() % 12 || 12).toString().padStart(2, "0"),
1097
- "h": (date.getHours() % 12 || 12).toString(),
1098
- "mm": date.getMinutes().toString().padStart(2, "0"),
1099
- "m": date.getMinutes().toString(),
1100
- "ss": date.getSeconds().toString().padStart(2, "0"),
1101
- "s": date.getSeconds().toString(),
1102
- "SSS": date.getMilliseconds().toString().padStart(3, "0"),
1103
- "A": date.getHours() >= 12 ? "PM" : "AM",
1104
- "a": date.getHours() >= 12 ? "pm" : "am",
1105
- "ZZ": tzOffset(t[1] * 60).replace(":", ""),
1106
- "Z": tzOffset(t[1] * 60),
1107
- "z": typeof tz == "string" ? tz : t[0]
1108
- };
1109
- 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]);
1110
- }
1111
- function instantInterval(fn2, interval) {
1112
- fn2();
1113
- return setInterval(fn2, interval);
1114
- }
1115
- function sleep(ms) {
1116
- return new Promise((res) => setTimeout(res, ms));
1117
- }
1118
- async function sleepWhile(fn2, checkInterval = 100) {
1119
- while (await fn2()) await sleep(checkInterval);
1120
- }
1121
- function timeUntil(date) {
1122
- return (date instanceof Date ? date.getTime() : date) - (/* @__PURE__ */ new Date()).getTime();
1123
- }
1124
- function timezoneOffset(tz, date = /* @__PURE__ */ new Date()) {
1125
- const dtf = new Intl.DateTimeFormat("en-US", {
1126
- timeZone: tz,
1127
- hour12: false,
1128
- year: "numeric",
1129
- month: "2-digit",
1130
- day: "2-digit",
1131
- hour: "2-digit",
1132
- minute: "2-digit",
1133
- second: "2-digit"
1134
- });
1135
- const parts = dtf.formatToParts(date);
1136
- const get = (type) => {
1137
- var _a;
1138
- return Number((_a = parts.find((v) => v.type === type)) == null ? void 0 : _a.value);
1139
- };
1140
- const y = get("year");
1141
- const mo = get("month");
1142
- const d = get("day");
1143
- const h = get("hour");
1144
- const m = get("minute");
1145
- const s = get("second");
1146
- const asUTC = Date.UTC(y, mo - 1, d, h, m, s);
1147
- const asLocal = date.getTime();
1148
- return Math.round((asLocal - asUTC) / 6e4);
1149
- }
1150
1174
  function downloadFile(blob, name) {
1151
1175
  if (!(blob instanceof Blob)) blob = new Blob(makeArray(blob));
1152
1176
  const url = URL.createObjectURL(blob);