@sv443-network/coreutils 3.5.0 → 3.6.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.
@@ -85,6 +85,7 @@ __export(lib_exports, {
85
85
  formatNumber: () => formatNumber,
86
86
  getCallStack: () => getCallStack,
87
87
  getListLength: () => getListLength,
88
+ getterifyObj: () => getterifyObj,
88
89
  hexToRgb: () => hexToRgb,
89
90
  insertValues: () => insertValues,
90
91
  joinArrayReadable: () => joinArrayReadable,
@@ -397,12 +398,12 @@ var NetworkError = class extends DatedError {
397
398
  };
398
399
 
399
400
  // lib/misc.ts
400
- async function consumeGen(valGen) {
401
- return await (typeof valGen === "function" ? valGen() : valGen);
401
+ async function consumeGen(valGen, ...args) {
402
+ return await (typeof valGen === "function" ? valGen(...args) : valGen);
402
403
  }
403
- async function consumeStringGen(strGen) {
404
+ async function consumeStringGen(strGen, ...args) {
404
405
  return typeof strGen === "string" ? strGen : String(
405
- typeof strGen === "function" ? await strGen() : strGen
406
+ typeof strGen === "function" ? await strGen(...args) : strGen
406
407
  );
407
408
  }
408
409
  async function fetchAdvanced(input, options = {}) {
@@ -441,6 +442,17 @@ function pauseFor(time, signal, rejectOnAbort = false) {
441
442
  function pureObj(obj) {
442
443
  return Object.assign(/* @__PURE__ */ Object.create(null), obj ?? {});
443
444
  }
445
+ function getterifyObj(obj, asCopy = false) {
446
+ const newObj = {};
447
+ for (const key in obj) {
448
+ Object.defineProperty(newObj, key, {
449
+ get: () => obj[key],
450
+ enumerable: true,
451
+ configurable: true
452
+ });
453
+ }
454
+ return asCopy ? structuredClone(newObj) : newObj;
455
+ }
444
456
  function setImmediateInterval(callback, interval, signal) {
445
457
  let intervalId;
446
458
  const cleanup = () => clearInterval(intervalId);
@@ -799,6 +811,8 @@ var NanoEmitter = class {
799
811
  events = createNanoEvents();
800
812
  eventUnsubscribes = [];
801
813
  emitterOptions;
814
+ /** Stores the last arguments for each event listed in `catchUpEvents` */
815
+ catchUpMemory = /* @__PURE__ */ new Map();
802
816
  /** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
803
817
  constructor(options = {}) {
804
818
  this.emitterOptions = {
@@ -806,6 +820,18 @@ var NanoEmitter = class {
806
820
  ...options
807
821
  };
808
822
  }
823
+ //#region emitEvent
824
+ /**
825
+ * Emits an event on this instance, bypassing the `publicEmit` guard.
826
+ * Prefer this over `this.events.emit()` in subclasses — it updates catch-up memory
827
+ * for any event listed in `catchUpEvents` so late listeners can still receive the last value.
828
+ */
829
+ emitEvent(event, ...args) {
830
+ var _a;
831
+ if ((_a = this.emitterOptions.catchUpEvents) == null ? void 0 : _a.includes(event))
832
+ this.catchUpMemory.set(event, args);
833
+ this.events.emit(event, ...args);
834
+ }
809
835
  //#region on
810
836
  /**
811
837
  * Subscribes to an event and calls the callback when it's emitted.
@@ -838,6 +864,9 @@ var NanoEmitter = class {
838
864
  };
839
865
  unsub = this.events.on(event, cb);
840
866
  this.eventUnsubscribes.push(unsub);
867
+ const memory = this.catchUpMemory.get(event);
868
+ if (memory)
869
+ cb(...memory);
841
870
  return unsubProxy;
842
871
  }
843
872
  //#region once
@@ -860,6 +889,12 @@ var NanoEmitter = class {
860
889
  * ```
861
890
  */
862
891
  once(event, cb) {
892
+ const memory = this.catchUpMemory.get(event);
893
+ if (memory) {
894
+ const args = memory;
895
+ cb == null ? void 0 : cb(...args);
896
+ return Promise.resolve(args);
897
+ }
863
898
  return new Promise((resolve) => {
864
899
  let unsub;
865
900
  const onceProxy = ((...args) => {
@@ -961,7 +996,7 @@ var NanoEmitter = class {
961
996
  */
962
997
  emit(event, ...args) {
963
998
  if (this.emitterOptions.publicEmit) {
964
- this.events.emit(event, ...args);
999
+ this.emitEvent(event, ...args);
965
1000
  return true;
966
1001
  }
967
1002
  return false;
@@ -1084,7 +1119,7 @@ var DataStore = class extends NanoEmitter {
1084
1119
  if (typeof storedDataRaw !== "string" && typeof storedDataRaw !== "object" || storedDataRaw === null || isNaN(storedFmtVer)) {
1085
1120
  await this.saveDefaultData(false);
1086
1121
  const data = this.engine.deepCopy(this.defaultData);
1087
- this.events.emit("loadData", data);
1122
+ this.emitEvent("loadData", data);
1088
1123
  return data;
1089
1124
  }
1090
1125
  const storedData = storedDataRaw ?? JSON.stringify(this.defaultData);
@@ -1094,12 +1129,12 @@ var DataStore = class extends NanoEmitter {
1094
1129
  if (storedFmtVer < this.formatVersion && this.migrations)
1095
1130
  parsed = await this.runMigrations(parsed, storedFmtVer);
1096
1131
  const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(parsed) : this.engine.deepCopy(parsed);
1097
- this.events.emit("loadData", result);
1132
+ this.emitEvent("loadData", result);
1098
1133
  return result;
1099
1134
  } catch (err) {
1100
1135
  const error = err instanceof Error ? err : new Error(String(err));
1101
1136
  console.warn("Error while parsing JSON data, resetting it to the default value.", err);
1102
- this.events.emit("error", error);
1137
+ this.emitEvent("error", error);
1103
1138
  await this.saveDefaultData();
1104
1139
  return this.defaultData;
1105
1140
  }
@@ -1121,7 +1156,7 @@ var DataStore = class extends NanoEmitter {
1121
1156
  const dataCopy = this.engine.deepCopy(data);
1122
1157
  if (this.memoryCache) {
1123
1158
  this.cachedData = data;
1124
- this.events.emit("updateDataSync", dataCopy);
1159
+ this.emitEvent("updateDataSync", dataCopy);
1125
1160
  }
1126
1161
  return new Promise(async (resolve) => {
1127
1162
  const results = await Promise.allSettled([
@@ -1130,11 +1165,11 @@ var DataStore = class extends NanoEmitter {
1130
1165
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
1131
1166
  ]);
1132
1167
  if (results.every((r) => r.status === "fulfilled"))
1133
- this.events.emit("updateData", dataCopy);
1168
+ this.emitEvent("updateData", dataCopy);
1134
1169
  else {
1135
1170
  const error = new Error("Error while saving data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
1136
1171
  console.error(error);
1137
- this.events.emit("error", error);
1172
+ this.emitEvent("error", error);
1138
1173
  }
1139
1174
  resolve();
1140
1175
  });
@@ -1153,11 +1188,11 @@ var DataStore = class extends NanoEmitter {
1153
1188
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
1154
1189
  ]);
1155
1190
  if (results.every((r) => r.status === "fulfilled"))
1156
- emitEvent && this.events.emit("setDefaultData", this.defaultData);
1191
+ emitEvent && this.emitEvent("setDefaultData", this.defaultData);
1157
1192
  else {
1158
1193
  const error = new Error("Error while saving default data to persistent storage: " + results.map((r) => r.status === "rejected" ? r.reason : null).filter(Boolean).join("; "));
1159
1194
  console.error(error);
1160
- this.events.emit("error", error);
1195
+ this.emitEvent("error", error);
1161
1196
  }
1162
1197
  }
1163
1198
  //#region deleteData
@@ -1174,7 +1209,7 @@ var DataStore = class extends NanoEmitter {
1174
1209
  this.engine.deleteValue(`${this.keyPrefix}${this.id}-enf`)
1175
1210
  ]);
1176
1211
  await ((_b = (_a = this.engine).deleteStorage) == null ? void 0 : _b.call(_a));
1177
- this.events.emit("deleteData");
1212
+ this.emitEvent("deleteData");
1178
1213
  }
1179
1214
  //#region encodingEnabled
1180
1215
  /** Returns whether encoding and decoding are enabled for this DataStore instance */
@@ -1204,11 +1239,11 @@ var DataStore = class extends NanoEmitter {
1204
1239
  newData = migRes instanceof Promise ? await migRes : migRes;
1205
1240
  lastFmtVer = oldFmtVer = ver;
1206
1241
  const isFinal = ver >= this.formatVersion || i === sortedMigrations.length - 1;
1207
- this.events.emit("migrateData", ver, newData, isFinal);
1242
+ this.emitEvent("migrateData", ver, newData, isFinal);
1208
1243
  } catch (err) {
1209
1244
  const migError = new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
1210
- this.events.emit("migrationError", ver, migError);
1211
- this.events.emit("error", migError);
1245
+ this.emitEvent("migrationError", ver, migError);
1246
+ this.emitEvent("error", migError);
1212
1247
  if (!resetOnError)
1213
1248
  throw migError;
1214
1249
  await this.saveDefaultData();
@@ -1222,7 +1257,7 @@ var DataStore = class extends NanoEmitter {
1222
1257
  this.engine.setValue(`${this.keyPrefix}${this.id}-enf`, this.compressionFormat)
1223
1258
  ]);
1224
1259
  const result = this.memoryCache ? this.cachedData = this.engine.deepCopy(newData) : this.engine.deepCopy(newData);
1225
- this.events.emit("updateData", result);
1260
+ this.emitEvent("updateData", result);
1226
1261
  return result;
1227
1262
  }
1228
1263
  //#region migrateId
@@ -1252,7 +1287,7 @@ var DataStore = class extends NanoEmitter {
1252
1287
  this.engine.deleteValue(`${this.keyPrefix}${id}-ver`),
1253
1288
  this.engine.deleteValue(`${this.keyPrefix}${id}-enf`)
1254
1289
  ]);
1255
- this.events.emit("migrateId", id, this.id);
1290
+ this.emitEvent("migrateId", id, this.id);
1256
1291
  }));
1257
1292
  }
1258
1293
  };
@@ -1495,15 +1530,20 @@ var DataStoreSerializer = class _DataStoreSerializer {
1495
1530
  addChecksum: true,
1496
1531
  ensureIntegrity: true,
1497
1532
  remapIds: {},
1533
+ stringifyData: true,
1498
1534
  ...options
1499
1535
  };
1500
1536
  }
1501
1537
  /**
1502
- * Calculates the checksum of a string. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
1538
+ * Calculates the checksum of a string or {@linkcode DataStoreData} object. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
1503
1539
  * Override this in a subclass if a custom checksum method is needed.
1504
1540
  */
1505
1541
  async calcChecksum(input) {
1506
- return computeHash(input, "SHA-256");
1542
+ try {
1543
+ return computeHash(typeof input === "string" ? input : JSON.stringify(input), "SHA-256");
1544
+ } catch (err) {
1545
+ throw new Error(`Failed to calculate checksum: ${err.message}`, { cause: err });
1546
+ }
1507
1547
  }
1508
1548
  /**
1509
1549
  * Serializes only a subset of the data stores into a string.
@@ -1518,7 +1558,7 @@ var DataStoreSerializer = class _DataStoreSerializer {
1518
1558
  for (const storeInst of filteredStores) {
1519
1559
  const encoded = Boolean(useEncoding && storeInst.encodingEnabled() && ((_a = storeInst.encodeData) == null ? void 0 : _a[1]));
1520
1560
  const rawData = storeInst.memoryCache ? storeInst.getData() : await storeInst.loadData();
1521
- const data = encoded ? await storeInst.encodeData[1](JSON.stringify(rawData)) : JSON.stringify(rawData);
1561
+ const data = encoded ? await storeInst.encodeData[1](JSON.stringify(rawData)) : this.options.stringifyData ? JSON.stringify(rawData) : rawData;
1522
1562
  serData.push({
1523
1563
  id: storeInst.id,
1524
1564
  data,
@@ -1564,11 +1604,11 @@ var DataStoreSerializer = class _DataStoreSerializer {
1564
1604
  Expected: ${storeData.checksum}
1565
1605
  Has: ${checksum}`);
1566
1606
  }
1567
- const decodedData = storeData.encoded && storeInst.encodingEnabled() ? await storeInst.decodeData[1](storeData.data) : storeData.data;
1607
+ const decodedData = storeData.encoded && storeInst.encodingEnabled() ? await storeInst.decodeData[1](typeof storeData.data === "string" ? storeData.data : JSON.stringify(storeData.data)) : storeData.data;
1568
1608
  if (storeData.formatVersion && !isNaN(Number(storeData.formatVersion)) && Number(storeData.formatVersion) < storeInst.formatVersion)
1569
- await storeInst.runMigrations(JSON.parse(decodedData), Number(storeData.formatVersion), false);
1609
+ await storeInst.runMigrations(typeof decodedData === "string" ? JSON.parse(decodedData) : decodedData, Number(storeData.formatVersion), false);
1570
1610
  else
1571
- await storeInst.setData(JSON.parse(decodedData));
1611
+ await storeInst.setData(typeof decodedData === "string" ? JSON.parse(decodedData) : decodedData);
1572
1612
  }
1573
1613
  }
1574
1614
  /**
@@ -1637,6 +1677,8 @@ var Debouncer = class extends NanoEmitter {
1637
1677
  this.timeout = timeout;
1638
1678
  this.type = type;
1639
1679
  }
1680
+ timeout;
1681
+ type;
1640
1682
  /** All registered listener functions and the time they were attached */
1641
1683
  listeners = [];
1642
1684
  /** The currently active timeout */
@@ -1664,7 +1706,7 @@ var Debouncer = class extends NanoEmitter {
1664
1706
  //#region timeout
1665
1707
  /** Sets the timeout for the debouncer */
1666
1708
  setTimeout(timeout) {
1667
- this.events.emit("change", this.timeout = timeout, this.type);
1709
+ this.emitEvent("change", this.timeout = timeout, this.type);
1668
1710
  }
1669
1711
  /** Returns the current timeout */
1670
1712
  getTimeout() {
@@ -1677,7 +1719,7 @@ var Debouncer = class extends NanoEmitter {
1677
1719
  //#region type
1678
1720
  /** Sets the edge type for the debouncer */
1679
1721
  setType(type) {
1680
- this.events.emit("change", this.timeout, this.type = type);
1722
+ this.emitEvent("change", this.timeout, this.type = type);
1681
1723
  }
1682
1724
  /** Returns the current edge type */
1683
1725
  getType() {
@@ -1688,7 +1730,7 @@ var Debouncer = class extends NanoEmitter {
1688
1730
  call(...args) {
1689
1731
  const cl = (...a) => {
1690
1732
  this.queuedCall = void 0;
1691
- this.events.emit("call", ...a);
1733
+ this.emitEvent("call", ...a);
1692
1734
  this.listeners.forEach((l) => l.call(this, ...a));
1693
1735
  };
1694
1736
  const setRepeatTimeout = () => {
@@ -1728,166 +1770,7 @@ function debounce(fn, timeout = 200, type = "immediate", nanoEmitterOptions) {
1728
1770
  func.debouncer = debouncer;
1729
1771
  return func;
1730
1772
  }
1731
- sistent data of the DataStore instances into the in-memory cache.
1732
- * Also triggers the migration process if the data format has changed.
1733
- * @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be loaded
1734
- * @returns Returns a PromiseSettledResult array with the results of each DataStore instance in the format `{ id: string, data: object }`
1735
- */
1736
- loadStoresData(stores) {
1737
- return __async(this, null, function* () {
1738
- return Promise.allSettled(
1739
- this.getStoresFiltered(stores).map((store) => __async(this, null, function* () {
1740
- return {
1741
- id: store.id,
1742
- data: yield store.loadData()
1743
- };
1744
- }))
1745
- );
1746
- });
1747
- }
1748
- /**
1749
- * Resets the persistent and in-memory data of the DataStore instances to their default values.
1750
- * @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be affected
1751
- */
1752
- resetStoresData(stores) {
1753
- return __async(this, null, function* () {
1754
- return Promise.allSettled(
1755
- this.getStoresFiltered(stores).map((store) => store.saveDefaultData())
1756
- );
1757
- });
1758
- }
1759
- /**
1760
- * Deletes the persistent data of the DataStore instances.
1761
- * Leaves the in-memory data untouched.
1762
- * @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be affected
1763
- */
1764
- deleteStoresData(stores) {
1765
- return __async(this, null, function* () {
1766
- return Promise.allSettled(
1767
- this.getStoresFiltered(stores).map((store) => store.deleteData())
1768
- );
1769
- });
1770
- }
1771
- /** Checks if a given value is an array of SerializedDataStore objects */
1772
- static isSerializedDataStoreObjArray(obj) {
1773
- return Array.isArray(obj) && obj.every((o) => typeof o === "object" && o !== null && "id" in o && "data" in o && "formatVersion" in o && "encoded" in o);
1774
- }
1775
- /** Checks if a given value is a SerializedDataStore object */
1776
- static isSerializedDataStoreObj(obj) {
1777
- return typeof obj === "object" && obj !== null && "id" in obj && "data" in obj && "formatVersion" in obj && "encoded" in obj;
1778
- }
1779
- /** Returns the DataStore instances whose IDs match the provided array or function */
1780
- getStoresFiltered(stores) {
1781
- return this.stores.filter((s) => typeof stores === "undefined" ? true : Array.isArray(stores) ? stores.includes(s.id) : stores(s.id));
1782
- }
1783
- };
1784
1773
 
1785
- // lib/Debouncer.ts
1786
- var Debouncer = class extends NanoEmitter {
1787
- /**
1788
- * Creates a new debouncer with the specified timeout and edge type.
1789
- * @param timeout Timeout in milliseconds between letting through calls - defaults to 200
1790
- * @param type The edge type to use for the debouncer - see {@linkcode DebouncerType} for details or [the documentation for an explanation and diagram](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#debouncer) - defaults to "immediate"
1791
- */
1792
- constructor(timeout = 200, type = "immediate", nanoEmitterOptions) {
1793
- super(nanoEmitterOptions);
1794
- this.timeout = timeout;
1795
- this.type = type;
1796
- /** All registered listener functions and the time they were attached */
1797
- __publicField(this, "listeners", []);
1798
- /** The currently active timeout */
1799
- __publicField(this, "activeTimeout");
1800
- /** The latest queued call */
1801
- __publicField(this, "queuedCall");
1802
- }
1803
- //#region listeners
1804
- /** Adds a listener function that will be called on timeout */
1805
- addListener(fn) {
1806
- this.listeners.push(fn);
1807
- }
1808
- /** Removes the listener with the specified function reference */
1809
- removeListener(fn) {
1810
- const idx = this.listeners.findIndex((l) => l === fn);
1811
- idx !== -1 && this.listeners.splice(idx, 1);
1812
- }
1813
- /** Removes all listeners */
1814
- removeAllListeners() {
1815
- this.listeners = [];
1816
- }
1817
- /** Returns all registered listeners */
1818
- getListeners() {
1819
- return this.listeners;
1820
- }
1821
- //#region timeout
1822
- /** Sets the timeout for the debouncer */
1823
- setTimeout(timeout) {
1824
- this.events.emit("change", this.timeout = timeout, this.type);
1825
- }
1826
- /** Returns the current timeout */
1827
- getTimeout() {
1828
- return this.timeout;
1829
- }
1830
- /** Whether the timeout is currently active, meaning any latest call to the {@linkcode call()} method will be queued */
1831
- isTimeoutActive() {
1832
- return typeof this.activeTimeout !== "undefined";
1833
- }
1834
- //#region type
1835
- /** Sets the edge type for the debouncer */
1836
- setType(type) {
1837
- this.events.emit("change", this.timeout, this.type = type);
1838
- }
1839
- /** Returns the current edge type */
1840
- getType() {
1841
- return this.type;
1842
- }
1843
- //#region call
1844
- /** Use this to call the debouncer with the specified arguments that will be passed to all listener functions registered with {@linkcode addListener()} */
1845
- call(...args) {
1846
- const cl = (...a) => {
1847
- this.queuedCall = void 0;
1848
- this.events.emit("call", ...a);
1849
- this.listeners.forEach((l) => l.call(this, ...a));
1850
- };
1851
- const setRepeatTimeout = () => {
1852
- this.activeTimeout = setTimeout(() => {
1853
- if (this.queuedCall) {
1854
- this.queuedCall();
1855
- setRepeatTimeout();
1856
- } else
1857
- this.activeTimeout = void 0;
1858
- }, this.timeout);
1859
- };
1860
- switch (this.type) {
1861
- case "immediate":
1862
- if (typeof this.activeTimeout === "undefined") {
1863
- cl(...args);
1864
- setRepeatTimeout();
1865
- } else
1866
- this.queuedCall = () => cl(...args);
1867
- break;
1868
- case "idle":
1869
- if (this.activeTimeout)
1870
- clearTimeout(this.activeTimeout);
1871
- this.activeTimeout = setTimeout(() => {
1872
- cl(...args);
1873
- this.activeTimeout = void 0;
1874
- }, this.timeout);
1875
- break;
1876
- default:
1877
- throw new TypeError(`Invalid debouncer type: ${this.type}`);
1878
- }
1879
- }
1880
- };
1881
- function debounce(fn, timeout = 200, type = "immediate", nanoEmitterOptions) {
1882
- const debouncer = new Debouncer(timeout, type, nanoEmitterOptions);
1883
- debouncer.addListener(fn);
1884
- const func = ((...args) => debouncer.call(...args));
1885
- func.debouncer = debouncer;
1886
- return func;
1887
- }
1888
-
1889
- if(__exports != exports)module.exports = exports;return module.exports}));
1890
- //# sourceMappingURL=CoreUtils.umd.js.map
1891
1774
 
1892
1775
 
1893
1776
  if (typeof module.exports == "object" && typeof exports == "object") {
@@ -2,7 +2,7 @@
2
2
  * @module DataStoreSerializer
3
3
  * This module contains the DataStoreSerializer class, which allows you to import and export serialized DataStore data - [see the documentation for more info](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#datastoreserializer)
4
4
  */
5
- import type { DataStore } from "./DataStore.ts";
5
+ import type { DataStore, DataStoreData } from "./DataStore.ts";
6
6
  /** Options for the DataStoreSerializer class */
7
7
  export type DataStoreSerializerOptions = {
8
8
  /** Whether to add a checksum to the exported data. Defaults to `true` */
@@ -11,13 +11,18 @@ export type DataStoreSerializerOptions = {
11
11
  ensureIntegrity?: boolean;
12
12
  /** If provided, all stores with an ID in the value's array will be remapped to the key's ID when deserialization is called. If they don't match a DataStore instance's ID, nothing will happen. */
13
13
  remapIds?: Record<string, string[]>;
14
+ /**
15
+ * Controls the type of the `data` property on {@linkcode SerializedDataStore} objects.
16
+ * When this is set to `false`, `data` will be the raw data object instead of a string, regardless of whether {@linkcode DataStoreSerializer.serialize()} or {@linkcode DataStoreSerializer.serializePartial()} are called with their `stringify` / `stringified` parameter set to `true` or `false`.
17
+ */
18
+ stringifyData?: boolean;
14
19
  };
15
20
  /** Meta object and serialized data of a DataStore instance */
16
- export type SerializedDataStore = {
21
+ export type SerializedDataStore<TData extends string | DataStoreData = string> = {
17
22
  /** The ID of the DataStore instance */
18
23
  id: string;
19
24
  /** The serialized data */
20
- data: string;
25
+ data: TData;
21
26
  /** The format version of the data */
22
27
  formatVersion: number;
23
28
  /** Whether the data is encoded */
@@ -48,10 +53,10 @@ export declare class DataStoreSerializer {
48
53
  protected options: Required<DataStoreSerializerOptions>;
49
54
  constructor(stores: DataStore<any, boolean>[], options?: DataStoreSerializerOptions);
50
55
  /**
51
- * Calculates the checksum of a string. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
56
+ * Calculates the checksum of a string or {@linkcode DataStoreData} object. Uses {@linkcode computeHash()} with SHA-256 and digests as a hex string by default.
52
57
  * Override this in a subclass if a custom checksum method is needed.
53
58
  */
54
- protected calcChecksum(input: string): Promise<string>;
59
+ protected calcChecksum(input: string | DataStoreData): Promise<string>;
55
60
  /**
56
61
  * Serializes only a subset of the data stores into a string.
57
62
  * @param stores An array of store IDs or functions that take a store ID and return a boolean
@@ -65,14 +70,14 @@ export declare class DataStoreSerializer {
65
70
  * @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
66
71
  * @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
67
72
  */
68
- serializePartial(stores: StoreFilter, useEncoding?: boolean, stringified?: false): Promise<SerializedDataStore[]>;
73
+ serializePartial(stores: StoreFilter, useEncoding?: boolean, stringified?: false): Promise<SerializedDataStore<string | DataStoreData>[]>;
69
74
  /**
70
75
  * Serializes only a subset of the data stores into a string.
71
76
  * @param stores An array of store IDs or functions that take a store ID and return a boolean
72
77
  * @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
73
78
  * @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
74
79
  */
75
- serializePartial(stores: StoreFilter, useEncoding?: boolean, stringified?: boolean): Promise<string | SerializedDataStore[]>;
80
+ serializePartial(stores: StoreFilter, useEncoding?: boolean, stringified?: boolean): Promise<string | SerializedDataStore<string | DataStoreData>[]>;
76
81
  /**
77
82
  * Serializes the data stores into a string.
78
83
  * @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
@@ -84,7 +89,7 @@ export declare class DataStoreSerializer {
84
89
  * @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
85
90
  * @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
86
91
  */
87
- serialize(useEncoding?: boolean, stringified?: false): Promise<SerializedDataStore[]>;
92
+ serialize(useEncoding?: boolean, stringified?: false): Promise<SerializedDataStore<string | DataStoreData>[]>;
88
93
  /**
89
94
  * Deserializes the data exported via {@linkcode serialize()} and imports only a subset into the DataStore instances.
90
95
  * Also triggers the migration process if the data format has changed.
@@ -7,6 +7,13 @@ import type { Prettify } from "./types.ts";
7
7
  export interface NanoEmitterOptions {
8
8
  /** If set to true, allows emitting events through the public method emit() */
9
9
  publicEmit: boolean;
10
+ /**
11
+ * When provided, the emitter will remember the last arguments of each listed event.
12
+ * Any listener attached via `on()` or `once()` after the event has already fired will
13
+ * be immediately called / resolved with the cached arguments (catch-up behaviour).
14
+ * Only works for emissions that go through the public `emit()` or the protected `emitEvent()` method.
15
+ */
16
+ catchUpEvents?: PropertyKey[];
10
17
  }
11
18
  type NanoEmitterOnMultiTriggerOptions<TEvtMap extends EventsMap, TKey extends keyof TEvtMap = keyof TEvtMap> = {
12
19
  /** Calls the callback when one of the given events is emitted. Either one of or both of `oneOf` and `allOf` need to be set. If both are set, they behave like an "AND" condition. */
@@ -25,14 +32,22 @@ export type NanoEmitterOnMultiOptions<TEvtMap extends EventsMap, TKey extends ke
25
32
  } & NanoEmitterOnMultiTriggerOptions<TEvtMap, TKey> & (Pick<Required<NanoEmitterOnMultiTriggerOptions<TEvtMap, TKey>>, "oneOf"> | Pick<Required<NanoEmitterOnMultiTriggerOptions<TEvtMap, TKey>>, "allOf">)>;
26
33
  /**
27
34
  * Class that can be extended or instantiated by itself to create a lightweight event emitter with helper methods and a strongly typed event map.
28
- * If extended from, you can use `this.events.emit()` to emit events, even if the `emit()` method doesn't work because `publicEmit` is not set to true in the constructor.
35
+ * If extended from, prefer using `this.emitEvent()` over `this.events.emit()` it updates the catch-up memory for any events listed in `catchUpEvents`.
29
36
  */
30
37
  export declare class NanoEmitter<TEvtMap extends EventsMap = DefaultEvents> {
31
38
  protected readonly events: Emitter<TEvtMap>;
32
39
  protected eventUnsubscribes: Unsubscribe[];
33
40
  protected emitterOptions: NanoEmitterOptions;
41
+ /** Stores the last arguments for each event listed in `catchUpEvents` */
42
+ protected catchUpMemory: Map<PropertyKey, unknown[]>;
34
43
  /** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
35
44
  constructor(options?: Partial<NanoEmitterOptions>);
45
+ /**
46
+ * Emits an event on this instance, bypassing the `publicEmit` guard.
47
+ * Prefer this over `this.events.emit()` in subclasses — it updates catch-up memory
48
+ * for any event listed in `catchUpEvents` so late listeners can still receive the last value.
49
+ */
50
+ protected emitEvent<TKey extends keyof TEvtMap>(event: TKey, ...args: Parameters<TEvtMap[TKey]>): void;
36
51
  /**
37
52
  * Subscribes to an event and calls the callback when it's emitted.
38
53
  * @param event The event to subscribe to. Use `as "_"` in case your event names aren't thoroughly typed (like when using a template literal, e.g. \`event-${val}\` as "_")
@@ -6,24 +6,32 @@ import type { ListLike, Prettify, Stringifiable } from "./types.ts";
6
6
  /**
7
7
  * A ValueGen value is either its type, a promise that resolves to its type, or a function that returns its type, either synchronous or asynchronous.
8
8
  * ValueGen allows for the utmost flexibility when applied to any type, as long as {@linkcode consumeGen()} is used to get the final value.
9
+ * The optional `TFn` parameter allows specifying the function variant's signature, enabling parametrized consumption via {@linkcode consumeGen()}.
9
10
  * @template TValueType The type of the value that the ValueGen should yield
11
+ * @template TFn The function signature for the function variant - defaults to a no-arg function for backwards compatibility
10
12
  */
11
- export type ValueGen<TValueType> = TValueType | Promise<TValueType> | (() => TValueType | Promise<TValueType>);
13
+ export type ValueGen<TValueType, TFn extends (...args: any[]) => TValueType | Promise<TValueType> = () => TValueType | Promise<TValueType>> = TValueType | Promise<TValueType> | TFn;
12
14
  /**
13
15
  * Turns a {@linkcode ValueGen} into its final value.
16
+ * @param args Optional arguments forwarded to the function, if the ValueGen is a parametrized function
14
17
  * @template TValueType The type of the value that the ValueGen should yield
18
+ * @template TFn The function signature for the function variant - inferred automatically
15
19
  */
16
- export declare function consumeGen<TValueType>(valGen: ValueGen<TValueType>): Promise<TValueType>;
20
+ export declare function consumeGen<TValueType, TFn extends (...args: any[]) => TValueType | Promise<TValueType> = () => TValueType | Promise<TValueType>>(valGen: ValueGen<TValueType, TFn>, ...args: Parameters<TFn>): Promise<TValueType>;
17
21
  /**
18
22
  * A StringGen value is either a string, anything that can be converted to a string, or a function that returns one of the previous two, either synchronous or asynchronous, or a promise that returns a string.
19
23
  * StringGen allows for the utmost flexibility when dealing with strings, as long as {@linkcode consumeStringGen()} is used to get the final string.
24
+ * The optional `TFn` parameter allows specifying the function variant's signature, enabling parametrized consumption via {@linkcode consumeStringGen()}.
25
+ * @template TFn The function signature for the function variant - defaults to a no-arg function for backwards compatibility
20
26
  */
21
- export type StringGen = ValueGen<Stringifiable>;
27
+ export type StringGen<TFn extends (...args: any[]) => Stringifiable | Promise<Stringifiable> = () => Stringifiable | Promise<Stringifiable>> = ValueGen<Stringifiable, TFn>;
22
28
  /**
23
29
  * Turns a {@linkcode StringGen} into its final string value.
30
+ * @param args Optional arguments forwarded to the function, if the StringGen is a parametrized function
24
31
  * @template TStrUnion The union of strings that the StringGen should yield - this allows for finer type control compared to {@linkcode consumeGen()}
32
+ * @template TFn The function signature for the function variant - inferred automatically
25
33
  */
26
- export declare function consumeStringGen<TStrUnion extends string>(strGen: StringGen): Promise<TStrUnion>;
34
+ export declare function consumeStringGen<TStrUnion extends string, TFn extends (...args: any[]) => Stringifiable | Promise<Stringifiable> = () => Stringifiable | Promise<Stringifiable>>(strGen: StringGen<TFn>, ...args: Parameters<TFn>): Promise<TStrUnion>;
27
35
  /** Options for the `fetchAdvanced()` function */
28
36
  export type FetchAdvancedOpts = Prettify<Partial<{
29
37
  /** Timeout in milliseconds after which the fetch call will be canceled with an AbortController signal */
@@ -50,6 +58,11 @@ export declare function pauseFor(time: number, signal?: AbortSignal, rejectOnAbo
50
58
  * If no object is passed, it will return an empty object without prototype chain.
51
59
  */
52
60
  export declare function pureObj<TObj extends object>(obj?: TObj): TObj;
61
+ /**
62
+ * Transforms an object's enumerable, own, string-keyed properties into getters that return the original values.
63
+ * Set `asCopy` to true to return a new object with the getterified properties instead of a live view of the original object.
64
+ */
65
+ export declare function getterifyObj<TObj extends object>(obj: TObj, asCopy?: boolean): TObj;
53
66
  /**
54
67
  * Works similarly to `setInterval()`, but the callback is also called immediately and can be aborted with an `AbortSignal`.
55
68
  * Uses `setInterval()` internally, which might cause overlapping calls if the callback's synchronous execution takes longer than the given interval time.
@@ -99,7 +99,7 @@ export type TableLineCharset = Record<TableLineStyle, TableLineStyleChars>;
99
99
  export declare const defaultTableLineCharset: TableLineCharset;
100
100
  /**
101
101
  * Creates an ASCII table string from the given rows and options.
102
- * Supports `\x1b` ANSI color codes in cell content: they are ignored for width calculation, included in the final output, and handled correctly during truncation (escape sequences are never split; any open color code is closed with a reset).
102
+ * Supports ANSI color codes in cell content: they are ignored for width calculation, included in the final output, and handled correctly during truncation (escape sequences are never split; any open color code is closed with a reset).
103
103
  * @param rows Array of tuples, where each tuple represents a row and its values. The first tuple is used to determine the column count.
104
104
  * @param options Object with options for customizing the table output, such as column alignment, truncation, padding and line styles.
105
105
  */
@@ -35,7 +35,7 @@ export type NonEmptyString<TString extends string> = TString extends "" ? never
35
35
  /** String constant that decides which set of number formatting options to use */
36
36
  export type NumberFormat = "short" | "long";
37
37
  /**
38
- * Makes the structure of a type more readable by expanding it.
38
+ * Makes the structure of a type more readable by expanding other types merged via intersections.
39
39
  * This can be useful for debugging or for improving the readability of complex types.
40
40
  */
41
41
  export type Prettify<T> = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sv443-network/coreutils",
3
3
  "libName": "@sv443-network/coreutils",
4
- "version": "3.5.0",
4
+ "version": "3.6.0",
5
5
  "description": "Cross-platform, general-purpose, JavaScript core library for Node, Deno and the browser. Intended to be used in conjunction with `@sv443-network/userutils` and `@sv443-network/djsutils`, but can be used independently as well.",
6
6
  "main": "dist/CoreUtils.cjs",
7
7
  "module": "dist/CoreUtils.mjs",