@ztimson/utils 0.25.27 → 0.26.0

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,315 +392,6 @@ 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
- }
561
- class Database {
562
- constructor(database, tables, version) {
563
- __publicField(this, "schemaLock", new AsyncLock());
564
- __publicField(this, "upgrading", false);
565
- __publicField(this, "connection");
566
- __publicField(this, "tables");
567
- __publicField(this, "waitForUpgrade", () => sleepWhile(() => this.upgrading));
568
- this.database = database;
569
- this.version = version;
570
- this.connection = new Promise((resolve, reject) => {
571
- const req = indexedDB.open(this.database, this.version);
572
- this.tables = !tables ? [] : tables.map((t) => {
573
- t = typeof t == "object" ? t : { name: t };
574
- return { ...t, name: t.name.toString() };
575
- });
576
- req.onerror = () => reject(req.error);
577
- req.onsuccess = () => {
578
- const db = req.result;
579
- const existing = Array.from(db.objectStoreNames);
580
- if (!tables) this.tables = existing.map((t) => {
581
- const tx = db.transaction(t, "readonly");
582
- const store = tx.objectStore(t);
583
- return { name: t, key: store.keyPath };
584
- });
585
- const desired = new ASet((tables || []).map((t) => typeof t == "string" ? t : t.name));
586
- if (tables && desired.symmetricDifference(new ASet(existing)).length) {
587
- db.close();
588
- Object.assign(this, new Database(this.database, this.tables, db.version + 1));
589
- this.connection.then(resolve);
590
- } else {
591
- this.version = db.version;
592
- resolve(db);
593
- }
594
- this.upgrading = false;
595
- };
596
- req.onupgradeneeded = () => {
597
- this.upgrading = true;
598
- const db = req.result;
599
- const existingTables = new ASet(Array.from(db.objectStoreNames));
600
- if (tables) {
601
- const desired = new ASet((tables || []).map((t) => typeof t == "string" ? t : t.name));
602
- existingTables.difference(desired).forEach((name) => db.deleteObjectStore(name));
603
- desired.difference(existingTables).forEach((name) => {
604
- const t = this.tables.find(findByProp("name", name));
605
- db.createObjectStore(name, {
606
- keyPath: t == null ? void 0 : t.key,
607
- autoIncrement: (t == null ? void 0 : t.autoIncrement) || !(t == null ? void 0 : t.key)
608
- });
609
- });
610
- }
611
- };
612
- });
613
- }
614
- get ready() {
615
- return !this.upgrading;
616
- }
617
- async createTable(table) {
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
- const newDb = new Database(this.database, [...this.tables, table], (this.version ?? 0) + 1);
623
- conn.close();
624
- Object.assign(this, newDb);
625
- await this.connection;
626
- }
627
- return this.table(table.name);
628
- });
629
- }
630
- async deleteTable(table) {
631
- return this.schemaLock.run(async () => {
632
- if (typeof table == "string") table = { name: table };
633
- if (!this.includes(table.name)) return;
634
- const conn = await this.connection;
635
- const newDb = new Database(this.database, this.tables.filter((t) => t.name != table.name), (this.version ?? 0) + 1);
636
- conn.close();
637
- Object.assign(this, newDb);
638
- await this.connection;
639
- });
640
- }
641
- includes(name) {
642
- return !!this.tables.find((t) => t.name == (typeof name == "object" ? name.name : name.toString()));
643
- }
644
- table(name) {
645
- return new Table(this, name.toString());
646
- }
647
- }
648
- class Table {
649
- constructor(database, name, key = "id") {
650
- __publicField(this, "all", this.getAll);
651
- __publicField(this, "create", this.add);
652
- __publicField(this, "update", this.set);
653
- this.database = database;
654
- this.name = name;
655
- this.key = key;
656
- this.database.connection.then(() => {
657
- const exists = !!this.database.tables.find(findByProp("name", this.name));
658
- if (!exists) this.database.createTable(this.name);
659
- });
660
- }
661
- async tx(table, fn2, readonly = false) {
662
- await this.database.waitForUpgrade();
663
- const db = await this.database.connection;
664
- const tx = db.transaction(table, readonly ? "readonly" : "readwrite");
665
- const store = tx.objectStore(table);
666
- return new Promise((resolve, reject) => {
667
- const request = fn2(store);
668
- request.onsuccess = () => resolve(request.result);
669
- request.onerror = () => reject(request.error);
670
- });
671
- }
672
- add(value, key) {
673
- return this.tx(this.name, (store) => store.add(value, key));
674
- }
675
- clear() {
676
- return this.tx(this.name, (store) => store.clear());
677
- }
678
- count() {
679
- return this.tx(this.name, (store) => store.count(), true);
680
- }
681
- delete(key) {
682
- return this.tx(this.name, (store) => store.delete(key));
683
- }
684
- get(key) {
685
- return this.tx(this.name, (store) => store.get(key), true);
686
- }
687
- getAll() {
688
- return this.tx(this.name, (store) => store.getAll(), true);
689
- }
690
- getAllKeys() {
691
- return this.tx(this.name, (store) => store.getAllKeys(), true);
692
- }
693
- put(key, value) {
694
- return this.tx(this.name, (store) => store.put(value, key));
695
- }
696
- read(key) {
697
- return key ? this.get(key) : this.getAll();
698
- }
699
- set(value, key) {
700
- if (!key && !value[this.key]) return this.add(value);
701
- return this.put(key || value[this.key], value);
702
- }
703
- }
704
395
  class Cache {
705
396
  /**
706
397
  * Create new cache
@@ -761,9 +452,10 @@ class Cache {
761
452
  return value[this.key];
762
453
  }
763
454
  save(key) {
455
+ var _a, _b;
764
456
  const persists = this.options.persistentStorage;
765
457
  if (!!(persists == null ? void 0 : persists.storage)) {
766
- if (persists.storage instanceof Database) {
458
+ if (((_a = persists.storage) == null ? void 0 : _a.constructor.name) == "Database") {
767
459
  persists.storage.createTable({ name: persists.storage.key, key: this.key }).then((table) => {
768
460
  if (key) {
769
461
  table.set(key, this.get(key));
@@ -772,7 +464,7 @@ class Cache {
772
464
  this.all().forEach((row) => table.add(row));
773
465
  }
774
466
  });
775
- } else {
467
+ } else if (((_b = persists.storage) == null ? void 0 : _b.constructor.name) == "Storage") {
776
468
  persists.storage.setItem(persists.storage.key, JSONSanitize(this.all(true)));
777
469
  }
778
470
  }
@@ -1048,87 +740,396 @@ function md5_ff(d, _, m, f, r, i, n) {
1048
740
  function md5_gg(d, _, m, f, r, i, n) {
1049
741
  return md5_cmn(_ & f | m & ~f, d, _, r, i, n);
1050
742
  }
1051
- function md5_hh(d, _, m, f, r, i, n) {
1052
- return md5_cmn(_ ^ m ^ f, d, _, r, i, n);
743
+ function md5_hh(d, _, m, f, r, i, n) {
744
+ return md5_cmn(_ ^ m ^ f, d, _, r, i, n);
745
+ }
746
+ function md5_ii(d, _, m, f, r, i, n) {
747
+ return md5_cmn(m ^ (_ | ~f), d, _, r, i, n);
748
+ }
749
+ function safe_add(d, _) {
750
+ var m = (65535 & d) + (65535 & _);
751
+ return (d >> 16) + (_ >> 16) + (m >> 16) << 16 | 65535 & m;
752
+ }
753
+ function bit_rol(d, _) {
754
+ return d << _ | d >>> 32 - _;
755
+ }
756
+ function wordSegments(str) {
757
+ if (!str) return [];
758
+ return str.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([0-9]+)([a-zA-Z])/g, "$1 $2").replace(/([a-zA-Z])([0-9]+)/g, "$1 $2").replace(/[_\-\s]+/g, " ").trim().split(/\s+/).filter(Boolean);
759
+ }
760
+ function validateEmail(email) {
761
+ return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(email);
762
+ }
763
+ function fromCsv(csv, hasHeaders = true) {
764
+ var _a;
765
+ function parseLine(line) {
766
+ const columns = [];
767
+ let current = "", inQuotes2 = false;
768
+ for (let i = 0; i < line.length; i++) {
769
+ const char = line[i];
770
+ const nextChar = line[i + 1];
771
+ if (char === '"') {
772
+ if (inQuotes2 && nextChar === '"') {
773
+ current += '"';
774
+ i++;
775
+ } else inQuotes2 = !inQuotes2;
776
+ } else if (char === "," && !inQuotes2) {
777
+ columns.push(current.trim());
778
+ current = "";
779
+ } else current += char;
780
+ }
781
+ columns.push(current.trim());
782
+ return columns.map((col) => col.replace(/^"|"$/g, "").replace(/""/g, '"'));
783
+ }
784
+ const rows = [];
785
+ let currentRow = "", inQuotes = false;
786
+ for (const char of csv.replace(/\r\n/g, "\n")) {
787
+ if (char === '"') inQuotes = !inQuotes;
788
+ if (char === "\n" && !inQuotes) {
789
+ rows.push(currentRow.trim());
790
+ currentRow = "";
791
+ } else currentRow += char;
792
+ }
793
+ if (currentRow) rows.push(currentRow.trim());
794
+ let headers = hasHeaders ? rows.splice(0, 1)[0] : null;
795
+ if (headers) headers = (_a = headers.match(/(?:[^,"']+|"(?:[^"]|"")*"|'(?:[^']|'')*')+/g)) == null ? void 0 : _a.map((h) => h.trim());
796
+ return rows.map((r) => {
797
+ const props = parseLine(r);
798
+ const h = headers || Array(props.length).fill(null).map((_, i) => {
799
+ let letter = "";
800
+ const first = i / 26;
801
+ if (first > 1) letter += LETTER_LIST[Math.floor(first - 1)];
802
+ letter += LETTER_LIST[i % 26];
803
+ return letter;
804
+ });
805
+ return h.reduce((acc, h2, i) => {
806
+ dotNotation(acc, h2, props[i]);
807
+ return acc;
808
+ }, {});
809
+ });
810
+ }
811
+ function toCsv(target, flatten = true) {
812
+ const t = makeArray(target);
813
+ const headers = new ASet(t.reduce((acc, row) => [...acc, ...Object.keys(flatten ? flattenObj(row) : row)], []));
814
+ return [
815
+ headers.join(","),
816
+ ...t.map((row) => headers.map((h) => {
817
+ const value = dotNotation(row, h);
818
+ if (value == null) return "";
819
+ if (typeof value == "object") return `"${JSONSanitize(value).replaceAll('"', '""')}"`;
820
+ if (typeof value == "string" && /[\n",]/g.test(value)) return `"${value.replaceAll('"', '""')}"`;
821
+ return value;
822
+ }).join(","))
823
+ ].join("\n");
824
+ }
825
+ function adjustedInterval(cb, ms) {
826
+ let cancel = false, timeout = null;
827
+ const p = async () => {
828
+ if (cancel) return;
829
+ const start = (/* @__PURE__ */ new Date()).getTime();
830
+ await cb();
831
+ const end = (/* @__PURE__ */ new Date()).getTime();
832
+ timeout = setTimeout(() => p(), ms - (end - start) || 1);
833
+ };
834
+ p();
835
+ return () => {
836
+ cancel = true;
837
+ if (timeout) clearTimeout(timeout);
838
+ };
839
+ }
840
+ function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(), tz) {
841
+ const timezones = [
842
+ ["IDLW", -12],
843
+ ["SST", -11],
844
+ ["HST", -10],
845
+ ["AKST", -9],
846
+ ["PST", -8],
847
+ ["MST", -7],
848
+ ["CST", -6],
849
+ ["EST", -5],
850
+ ["AST", -4],
851
+ ["BRT", -3],
852
+ ["MAT", -2],
853
+ ["AZOT", -1],
854
+ ["UTC", 0],
855
+ ["CET", 1],
856
+ ["EET", 2],
857
+ ["MSK", 3],
858
+ ["AST", 4],
859
+ ["PKT", 5],
860
+ ["IST", 5.5],
861
+ ["BST", 6],
862
+ ["ICT", 7],
863
+ ["CST", 8],
864
+ ["JST", 9],
865
+ ["AEST", 10],
866
+ ["SBT", 11],
867
+ ["FJT", 12],
868
+ ["TOT", 13],
869
+ ["LINT", 14]
870
+ ];
871
+ function adjustTz(date2, gmt) {
872
+ const currentOffset = date2.getTimezoneOffset();
873
+ const adjustedOffset = gmt * 60;
874
+ return new Date(date2.getTime() + (adjustedOffset + currentOffset) * 6e4);
875
+ }
876
+ function day(num) {
877
+ return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][num] || "Unknown";
878
+ }
879
+ function doy(date2) {
880
+ const start = /* @__PURE__ */ new Date(`${date2.getFullYear()}-01-01 0:00:00`);
881
+ return Math.ceil((date2.getTime() - start.getTime()) / (1e3 * 60 * 60 * 24));
882
+ }
883
+ function month(num) {
884
+ return ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][num] || "Unknown";
885
+ }
886
+ function suffix(num) {
887
+ if (num % 100 >= 11 && num % 100 <= 13) return `${num}th`;
888
+ switch (num % 10) {
889
+ case 1:
890
+ return `${num}st`;
891
+ case 2:
892
+ return `${num}nd`;
893
+ case 3:
894
+ return `${num}rd`;
895
+ default:
896
+ return `${num}th`;
897
+ }
898
+ }
899
+ function tzOffset(offset) {
900
+ const hours = ~~(offset / 60);
901
+ const minutes = offset % 60;
902
+ return (offset > 0 ? "-" : "") + `${hours}:${minutes.toString().padStart(2, "0")}`;
903
+ }
904
+ if (typeof date == "number" || typeof date == "string" || date == null) date = new Date(date);
905
+ let t;
906
+ if (tz == null) tz = -(date.getTimezoneOffset() / 60);
907
+ t = timezones.find((t2) => isNaN(tz) ? t2[0] == tz : t2[1] == tz);
908
+ if (!t) throw new Error(`Unknown timezone: ${tz}`);
909
+ date = adjustTz(date, t[1]);
910
+ const tokens = {
911
+ "YYYY": date.getFullYear().toString(),
912
+ "YY": date.getFullYear().toString().slice(2),
913
+ "MMMM": month(date.getMonth()),
914
+ "MMM": month(date.getMonth()).slice(0, 3),
915
+ "MM": (date.getMonth() + 1).toString().padStart(2, "0"),
916
+ "M": (date.getMonth() + 1).toString(),
917
+ "DDD": doy(date).toString(),
918
+ "DD": date.getDate().toString().padStart(2, "0"),
919
+ "Do": suffix(date.getDate()),
920
+ "D": date.getDate().toString(),
921
+ "dddd": day(date.getDay()),
922
+ "ddd": day(date.getDay()).slice(0, 3),
923
+ "HH": date.getHours().toString().padStart(2, "0"),
924
+ "H": date.getHours().toString(),
925
+ "hh": (date.getHours() % 12 || 12).toString().padStart(2, "0"),
926
+ "h": (date.getHours() % 12 || 12).toString(),
927
+ "mm": date.getMinutes().toString().padStart(2, "0"),
928
+ "m": date.getMinutes().toString(),
929
+ "ss": date.getSeconds().toString().padStart(2, "0"),
930
+ "s": date.getSeconds().toString(),
931
+ "SSS": date.getMilliseconds().toString().padStart(3, "0"),
932
+ "A": date.getHours() >= 12 ? "PM" : "AM",
933
+ "a": date.getHours() >= 12 ? "pm" : "am",
934
+ "ZZ": tzOffset(t[1] * 60).replace(":", ""),
935
+ "Z": tzOffset(t[1] * 60),
936
+ "z": typeof tz == "string" ? tz : t[0]
937
+ };
938
+ 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]);
939
+ }
940
+ function instantInterval(fn2, interval) {
941
+ fn2();
942
+ return setInterval(fn2, interval);
1053
943
  }
1054
- function md5_ii(d, _, m, f, r, i, n) {
1055
- return md5_cmn(m ^ (_ | ~f), d, _, r, i, n);
944
+ function sleep(ms) {
945
+ return new Promise((res) => setTimeout(res, ms));
1056
946
  }
1057
- function safe_add(d, _) {
1058
- var m = (65535 & d) + (65535 & _);
1059
- return (d >> 16) + (_ >> 16) + (m >> 16) << 16 | 65535 & m;
947
+ async function sleepWhile(fn2, checkInterval = 100) {
948
+ while (await fn2()) await sleep(checkInterval);
1060
949
  }
1061
- function bit_rol(d, _) {
1062
- return d << _ | d >>> 32 - _;
950
+ function timeUntil(date) {
951
+ return (date instanceof Date ? date.getTime() : date) - (/* @__PURE__ */ new Date()).getTime();
1063
952
  }
1064
- function wordSegments(str) {
1065
- if (!str) return [];
1066
- return str.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([0-9]+)([a-zA-Z])/g, "$1 $2").replace(/([a-zA-Z])([0-9]+)/g, "$1 $2").replace(/[_\-\s]+/g, " ").trim().split(/\s+/).filter(Boolean);
953
+ function timezoneOffset(tz, date = /* @__PURE__ */ new Date()) {
954
+ const dtf = new Intl.DateTimeFormat("en-US", {
955
+ timeZone: tz,
956
+ hour12: false,
957
+ year: "numeric",
958
+ month: "2-digit",
959
+ day: "2-digit",
960
+ hour: "2-digit",
961
+ minute: "2-digit",
962
+ second: "2-digit"
963
+ });
964
+ const parts = dtf.formatToParts(date);
965
+ const get = (type) => {
966
+ var _a;
967
+ return Number((_a = parts.find((v) => v.type === type)) == null ? void 0 : _a.value);
968
+ };
969
+ const y = get("year");
970
+ const mo = get("month");
971
+ const d = get("day");
972
+ const h = get("hour");
973
+ const m = get("minute");
974
+ const s = get("second");
975
+ const asUTC = Date.UTC(y, mo - 1, d, h, m, s);
976
+ const asLocal = date.getTime();
977
+ return Math.round((asLocal - asUTC) / 6e4);
1067
978
  }
1068
- function validateEmail(email) {
1069
- return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(email);
979
+ class AsyncLock {
980
+ constructor() {
981
+ __publicField(this, "p", Promise.resolve());
982
+ }
983
+ run(fn2) {
984
+ const res = this.p.then(fn2, fn2);
985
+ this.p = res.then(() => {
986
+ }, () => {
987
+ });
988
+ return res;
989
+ }
1070
990
  }
1071
- function fromCsv(csv, hasHeaders = true) {
1072
- var _a;
1073
- function parseLine(line) {
1074
- const columns = [];
1075
- let current = "", inQuotes2 = false;
1076
- for (let i = 0; i < line.length; i++) {
1077
- const char = line[i];
1078
- const nextChar = line[i + 1];
1079
- if (char === '"') {
1080
- if (inQuotes2 && nextChar === '"') {
1081
- current += '"';
1082
- i++;
1083
- } else inQuotes2 = !inQuotes2;
1084
- } else if (char === "," && !inQuotes2) {
1085
- columns.push(current.trim());
1086
- current = "";
1087
- } else current += char;
1088
- }
1089
- columns.push(current.trim());
1090
- return columns.map((col) => col.replace(/^"|"$/g, "").replace(/""/g, '"'));
991
+ class Database {
992
+ constructor(database, tables, version) {
993
+ __publicField(this, "schemaLock", new AsyncLock());
994
+ __publicField(this, "upgrading", false);
995
+ __publicField(this, "connection");
996
+ __publicField(this, "tables");
997
+ __publicField(this, "waitForUpgrade", () => sleepWhile(() => this.upgrading));
998
+ this.database = database;
999
+ this.version = version;
1000
+ this.connection = new Promise((resolve, reject) => {
1001
+ const req = indexedDB.open(this.database, this.version);
1002
+ this.tables = !tables ? [] : tables.map((t) => {
1003
+ t = typeof t == "object" ? t : { name: t };
1004
+ return { ...t, name: t.name.toString() };
1005
+ });
1006
+ req.onerror = () => reject(req.error);
1007
+ req.onsuccess = () => {
1008
+ const db = req.result;
1009
+ const existing = Array.from(db.objectStoreNames);
1010
+ if (!tables) this.tables = existing.map((t) => {
1011
+ const tx = db.transaction(t, "readonly");
1012
+ const store = tx.objectStore(t);
1013
+ return { name: t, key: store.keyPath };
1014
+ });
1015
+ const desired = new ASet((tables || []).map((t) => typeof t == "string" ? t : t.name));
1016
+ if (tables && desired.symmetricDifference(new ASet(existing)).length) {
1017
+ db.close();
1018
+ Object.assign(this, new Database(this.database, this.tables, db.version + 1));
1019
+ this.connection.then(resolve);
1020
+ } else {
1021
+ this.version = db.version;
1022
+ resolve(db);
1023
+ }
1024
+ this.upgrading = false;
1025
+ };
1026
+ req.onupgradeneeded = () => {
1027
+ this.upgrading = true;
1028
+ const db = req.result;
1029
+ const existingTables = new ASet(Array.from(db.objectStoreNames));
1030
+ if (tables) {
1031
+ const desired = new ASet((tables || []).map((t) => typeof t == "string" ? t : t.name));
1032
+ existingTables.difference(desired).forEach((name) => db.deleteObjectStore(name));
1033
+ desired.difference(existingTables).forEach((name) => {
1034
+ const t = this.tables.find(findByProp("name", name));
1035
+ db.createObjectStore(name, {
1036
+ keyPath: t == null ? void 0 : t.key,
1037
+ autoIncrement: (t == null ? void 0 : t.autoIncrement) || !(t == null ? void 0 : t.key)
1038
+ });
1039
+ });
1040
+ }
1041
+ };
1042
+ });
1091
1043
  }
1092
- const rows = [];
1093
- let currentRow = "", inQuotes = false;
1094
- for (const char of csv.replace(/\r\n/g, "\n")) {
1095
- if (char === '"') inQuotes = !inQuotes;
1096
- if (char === "\n" && !inQuotes) {
1097
- rows.push(currentRow.trim());
1098
- currentRow = "";
1099
- } else currentRow += char;
1044
+ get ready() {
1045
+ return !this.upgrading;
1100
1046
  }
1101
- if (currentRow) rows.push(currentRow.trim());
1102
- let headers = hasHeaders ? rows.splice(0, 1)[0] : null;
1103
- if (headers) headers = (_a = headers.match(/(?:[^,"']+|"(?:[^"]|"")*"|'(?:[^']|'')*')+/g)) == null ? void 0 : _a.map((h) => h.trim());
1104
- return rows.map((r) => {
1105
- const props = parseLine(r);
1106
- const h = headers || Array(props.length).fill(null).map((_, i) => {
1107
- let letter = "";
1108
- const first = i / 26;
1109
- if (first > 1) letter += LETTER_LIST[Math.floor(first - 1)];
1110
- letter += LETTER_LIST[i % 26];
1111
- return letter;
1047
+ async createTable(table) {
1048
+ return this.schemaLock.run(async () => {
1049
+ if (typeof table == "string") table = { name: table };
1050
+ const conn = await this.connection;
1051
+ if (!this.includes(table.name)) {
1052
+ const newDb = new Database(this.database, [...this.tables, table], (this.version ?? 0) + 1);
1053
+ conn.close();
1054
+ Object.assign(this, newDb);
1055
+ await this.connection;
1056
+ }
1057
+ return this.table(table.name);
1112
1058
  });
1113
- return h.reduce((acc, h2, i) => {
1114
- dotNotation(acc, h2, props[i]);
1115
- return acc;
1116
- }, {});
1117
- });
1059
+ }
1060
+ async deleteTable(table) {
1061
+ return this.schemaLock.run(async () => {
1062
+ if (typeof table == "string") table = { name: table };
1063
+ if (!this.includes(table.name)) return;
1064
+ const conn = await this.connection;
1065
+ const newDb = new Database(this.database, this.tables.filter((t) => t.name != table.name), (this.version ?? 0) + 1);
1066
+ conn.close();
1067
+ Object.assign(this, newDb);
1068
+ await this.connection;
1069
+ });
1070
+ }
1071
+ includes(name) {
1072
+ return !!this.tables.find((t) => t.name == (typeof name == "object" ? name.name : name.toString()));
1073
+ }
1074
+ table(name) {
1075
+ return new Table(this, name.toString());
1076
+ }
1118
1077
  }
1119
- function toCsv(target, flatten = true) {
1120
- const t = makeArray(target);
1121
- const headers = new ASet(t.reduce((acc, row) => [...acc, ...Object.keys(flatten ? flattenObj(row) : row)], []));
1122
- return [
1123
- headers.join(","),
1124
- ...t.map((row) => headers.map((h) => {
1125
- const value = dotNotation(row, h);
1126
- if (value == null) return "";
1127
- if (typeof value == "object") return `"${JSONSanitize(value).replaceAll('"', '""')}"`;
1128
- if (typeof value == "string" && /[\n",]/g.test(value)) return `"${value.replaceAll('"', '""')}"`;
1129
- return value;
1130
- }).join(","))
1131
- ].join("\n");
1078
+ class Table {
1079
+ constructor(database, name, key = "id") {
1080
+ __publicField(this, "all", this.getAll);
1081
+ __publicField(this, "create", this.add);
1082
+ __publicField(this, "update", this.set);
1083
+ this.database = database;
1084
+ this.name = name;
1085
+ this.key = key;
1086
+ this.database.connection.then(() => {
1087
+ const exists = !!this.database.tables.find(findByProp("name", this.name));
1088
+ if (!exists) this.database.createTable(this.name);
1089
+ });
1090
+ }
1091
+ async tx(table, fn2, readonly = false) {
1092
+ await this.database.waitForUpgrade();
1093
+ const db = await this.database.connection;
1094
+ const tx = db.transaction(table, readonly ? "readonly" : "readwrite");
1095
+ const store = tx.objectStore(table);
1096
+ return new Promise((resolve, reject) => {
1097
+ const request = fn2(store);
1098
+ request.onsuccess = () => resolve(request.result);
1099
+ request.onerror = () => reject(request.error);
1100
+ });
1101
+ }
1102
+ add(value, key) {
1103
+ return this.tx(this.name, (store) => store.add(value, key));
1104
+ }
1105
+ clear() {
1106
+ return this.tx(this.name, (store) => store.clear());
1107
+ }
1108
+ count() {
1109
+ return this.tx(this.name, (store) => store.count(), true);
1110
+ }
1111
+ delete(key) {
1112
+ return this.tx(this.name, (store) => store.delete(key));
1113
+ }
1114
+ get(key) {
1115
+ return this.tx(this.name, (store) => store.get(key), true);
1116
+ }
1117
+ getAll() {
1118
+ return this.tx(this.name, (store) => store.getAll(), true);
1119
+ }
1120
+ getAllKeys() {
1121
+ return this.tx(this.name, (store) => store.getAllKeys(), true);
1122
+ }
1123
+ put(key, value) {
1124
+ return this.tx(this.name, (store) => store.put(value, key));
1125
+ }
1126
+ read(key) {
1127
+ return key ? this.get(key) : this.getAll();
1128
+ }
1129
+ set(value, key) {
1130
+ if (!key && !value[this.key]) return this.add(value);
1131
+ return this.put(key || value[this.key], value);
1132
+ }
1132
1133
  }
1133
1134
  class PromiseProgress extends Promise {
1134
1135
  constructor(executor) {