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