@ztimson/utils 0.25.27 → 0.25.28

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