tiny-essentials 1.19.0 → 1.19.2

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.
@@ -445,6 +445,22 @@ const customEncoders = new Map();
445
445
  /** @type {Map<any, DecodeFn>} */
446
446
  const customDecoders = new Map();
447
447
 
448
+ /** @type {Set<any>} */
449
+ const customTypesFreezed = new Set([
450
+ 'string',
451
+ 'boolean',
452
+ 'number',
453
+ 'object',
454
+ 'array',
455
+ String,
456
+ Boolean,
457
+ Number,
458
+ Object,
459
+ Array,
460
+ BigInt,
461
+ Symbol,
462
+ ]);
463
+
448
464
  /**
449
465
  * A function that encodes a value into a serializable JSON-compatible format.
450
466
  *
@@ -717,25 +733,48 @@ class TinyLocalStorage {
717
733
  ///////////////////////////////////////////////////
718
734
 
719
735
  /**
720
- * Registers a new JSON-serializable type with its encoder and decoder.
736
+ * Checks whether a JSON-serializable type is already registered.
721
737
  *
722
- * @param {any} type - The type or primitive type name (e.g. `"bigint"`, `"symbol"`, etc).
723
- * @param {EncodeFn} encodeFn - The function that encodes the value.
724
- * @param {DecodeFn} decodeFn - An object with `check` and `decode` methods for restoring the value.
738
+ * @param {any} type - The type identifier, which can be a string (e.g., `"bigint"`, `"symbol"`)
739
+ * or a reference to a class/constructor function.
740
+ * @returns {boolean} True if the type has both an encoder and decoder registered.
725
741
  */
726
- static registerJsonType(type, encodeFn, decodeFn) {
742
+ static hasJsonType(type) {
743
+ return (customEncoders.has(type) && customDecoders.has(type)) || customTypesFreezed.has(type);
744
+ }
745
+
746
+ /**
747
+ * Registers a new JSON-serializable type with its encoder and decoder.
748
+ *
749
+ * @param {any} type - The type identifier, which can be a string (e.g., `"bigint"`, `"symbol"`)
750
+ * or a reference to a class/constructor function.
751
+ * @param {EncodeFn} encodeFn - A function that receives a value of this type and returns a JSON-safe representation.
752
+ * @param {DecodeFn} decodeFn - An object with `check(value)` to identify this type and `decode(value)` to restore it.
753
+ * @param {boolean} [freezeType=false] - If true, prevents this type from being unregistered later.
754
+ * @throws {Error} Throws an error if the type is frozen and cannot be registered.
755
+ */
756
+ static registerJsonType(type, encodeFn, decodeFn, freezeType = false) {
757
+ if (customTypesFreezed.has(type))
758
+ throw new Error(`Cannot register type "${type}" because it is frozen.`);
727
759
  customEncoders.set(type, encodeFn);
728
760
  customDecoders.set(type, decodeFn);
761
+ if (freezeType) customTypesFreezed.add(type);
729
762
  }
730
763
 
731
764
  /**
732
- * Removes a previously registered custom type from the encoding/decoding system.
765
+ * Unregisters a previously registered custom type from the encoding/decoding system.
733
766
  *
734
- * @param {string} type - The primitive name or constructor reference used in registration.
767
+ * @param {any} type - The primitive name or constructor reference used in the registration.
768
+ * @returns {boolean} Returns true if the type existed and was removed, or false if it wasn't found.
769
+ * @throws {Error} Throws an error if the type is frozen and cannot be unregistered.
735
770
  */
736
771
  static deleteJsonType(type) {
737
- customEncoders.delete(type);
738
- customDecoders.delete(type);
772
+ if (customTypesFreezed.has(type))
773
+ throw new Error(`Cannot remove type "${type}" because it is frozen.`);
774
+ let isDeleted = false;
775
+ if (customEncoders.delete(type)) isDeleted = true;
776
+ if (customDecoders.delete(type)) isDeleted = true;
777
+ return isDeleted;
739
778
  }
740
779
 
741
780
  //////////////////////////////////////////////////////
@@ -882,7 +921,7 @@ class TinyLocalStorage {
882
921
  }
883
922
  const encoded = this.#setJson(name, data);
884
923
  this.emit('setJson', name, data);
885
- this.#localStorage.setItem(name, JSON.stringify(encoded));
924
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
886
925
  }
887
926
 
888
927
  /**
@@ -952,8 +991,8 @@ class TinyLocalStorage {
952
991
  setDate(name, data) {
953
992
  if (!(data instanceof Date)) throw new Error('Value must be a Date.');
954
993
  const encoded = this.#setJson(name, data);
955
- this.#localStorage.setItem(name, JSON.stringify(encoded));
956
994
  this.emit('setDate', name, data);
995
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
957
996
  }
958
997
 
959
998
  /**
@@ -974,8 +1013,8 @@ class TinyLocalStorage {
974
1013
  setRegExp(name, data) {
975
1014
  if (!(data instanceof RegExp)) throw new Error('Value must be a RegExp.');
976
1015
  const encoded = this.#setJson(name, data);
977
- this.#localStorage.setItem(name, JSON.stringify(encoded));
978
1016
  this.emit('setRegExp', name, data);
1017
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
979
1018
  }
980
1019
 
981
1020
  /**
@@ -996,8 +1035,8 @@ class TinyLocalStorage {
996
1035
  setBigInt(name, data) {
997
1036
  if (typeof data !== 'bigint') throw new Error('Value must be a BigInt.');
998
1037
  const encoded = this.#setJson(name, data);
999
- this.#localStorage.setItem(name, JSON.stringify(encoded));
1000
1038
  this.emit('setBigInt', name, data);
1039
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
1001
1040
  }
1002
1041
 
1003
1042
  /**
@@ -1019,8 +1058,8 @@ class TinyLocalStorage {
1019
1058
  setSymbol(name, data) {
1020
1059
  if (typeof data !== 'symbol') throw new Error('Value must be a Symbol.');
1021
1060
  const encoded = this.#setJson(name, data);
1022
- this.#localStorage.setItem(name, JSON.stringify(encoded));
1023
1061
  this.emit('setSymbol', name, data);
1062
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
1024
1063
  }
1025
1064
 
1026
1065
  /**
@@ -1080,7 +1119,13 @@ class TinyLocalStorage {
1080
1119
  if (typeof data !== 'string') throw new Error('Value must be a string.');
1081
1120
 
1082
1121
  this.emit('setString', name, data);
1083
- return this.#localStorage.setItem(name, data);
1122
+ return this.#localStorage.setItem(
1123
+ name,
1124
+ JSON.stringify({
1125
+ __string__: true,
1126
+ value: data,
1127
+ }),
1128
+ );
1084
1129
  }
1085
1130
 
1086
1131
  /**
@@ -1093,6 +1138,15 @@ class TinyLocalStorage {
1093
1138
  if (typeof name !== 'string' || !name.length)
1094
1139
  throw new Error('Key must be a non-empty string.');
1095
1140
  let value = this.#localStorage.getItem(name);
1141
+ try {
1142
+ /** @type {{ value: string; __string__: boolean }} */
1143
+ // @ts-ignore
1144
+ value = JSON.parse(value);
1145
+ if (!isJsonObject(value) || !value.__string__ || typeof value.value !== 'string') return null;
1146
+ value = value.value;
1147
+ } catch {
1148
+ value = null;
1149
+ }
1096
1150
  if (typeof value === 'string') return value;
1097
1151
  return null;
1098
1152
  }
@@ -1182,7 +1236,7 @@ class TinyLocalStorage {
1182
1236
  * Clears all data from `localStorage`.
1183
1237
  */
1184
1238
  clearLocalStorage() {
1185
- this.#localStorage.clear();
1239
+ return this.#localStorage.clear();
1186
1240
  }
1187
1241
 
1188
1242
  /**
@@ -1209,6 +1263,7 @@ TinyLocalStorage.registerJsonType(
1209
1263
  decode: (value, decodeSpecialJson) =>
1210
1264
  new Map(value.data.map(([k, v]) => [k, decodeSpecialJson(v)])),
1211
1265
  },
1266
+ true,
1212
1267
  );
1213
1268
 
1214
1269
  // Set
@@ -1222,6 +1277,7 @@ TinyLocalStorage.registerJsonType(
1222
1277
  check: (value) => value.__set__,
1223
1278
  decode: (value, decodeSpecialJson) => new Set(value.data.map(decodeSpecialJson)),
1224
1279
  },
1280
+ true,
1225
1281
  );
1226
1282
 
1227
1283
  // Date
@@ -1235,6 +1291,7 @@ TinyLocalStorage.registerJsonType(
1235
1291
  check: (value) => value.__date__,
1236
1292
  decode: (value) => new Date(value.value),
1237
1293
  },
1294
+ true,
1238
1295
  );
1239
1296
 
1240
1297
  // Regex
@@ -1249,6 +1306,7 @@ TinyLocalStorage.registerJsonType(
1249
1306
  check: (value) => value.__regexp__,
1250
1307
  decode: (value) => new RegExp(value.source, value.flags),
1251
1308
  },
1309
+ true,
1252
1310
  );
1253
1311
 
1254
1312
  // Big Int
@@ -1262,6 +1320,7 @@ TinyLocalStorage.registerJsonType(
1262
1320
  check: (value) => value.__bigint__,
1263
1321
  decode: (value) => BigInt(value.value),
1264
1322
  },
1323
+ true,
1265
1324
  );
1266
1325
 
1267
1326
  // Symbol
@@ -1278,6 +1337,7 @@ TinyLocalStorage.registerJsonType(
1278
1337
  return key != null ? Symbol.for(key) : Symbol();
1279
1338
  },
1280
1339
  },
1340
+ true,
1281
1341
  );
1282
1342
 
1283
1343
  /* harmony default export */ const libs_TinyLocalStorage = (TinyLocalStorage);
@@ -1 +1 @@
1
- (()=>{"use strict";var e={d:(t,r)=>{for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};function r(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)&&"[object Object]"===Object.prototype.toString.call(e)}e.d(t,{TinyLocalStorage:()=>a});const n=class{#e=new Map;#t=10;#r=!1;setThrowOnMaxListeners(e){if("boolean"!=typeof e)throw new TypeError("setThrowOnMaxListeners(value): value must be a boolean");this.#r=e}getThrowOnMaxListeners(){return this.#r}#n(e,t,{once:r=!1}={}){let n=this.#e.get(e);Array.isArray(n)||(n=[],this.#e.set(e,n)),n.unshift({handler:t,config:{once:r}});const s=this.#t;if(s>0&&n.length>s){const t=`Possible memory leak detected. ${n.length} "${e}" listeners added. Use setMaxListeners() to increase limit.`;if(this.#r)throw new Error(t);console.warn(t)}}prependListener(e,t){if("string"!=typeof e)throw new TypeError("prepend(event, handler): event name must be a string");if("function"!=typeof t)throw new TypeError("prepend(event, handler): handler must be a function");this.#n(e,t)}prependListenerOnce(e,t){if("string"!=typeof e)throw new TypeError("prependOnceListener(event, handler): event name must be a string");if("function"!=typeof t)throw new TypeError("prependOnceListener(event, handler): handler must be a function");const r=(...n)=>{this.off(e,r),t(...n)};return this.#n(e,r,{once:!0}),r}#s(e,t,{once:r=!1}={}){let n=this.#e.get(e);Array.isArray(n)||(n=[],this.#e.set(e,n)),n.push({handler:t,config:{once:r}});const s=this.#t;if(s>0&&n.length>s){const t=`Possible memory leak detected. ${n.length} "${e}" listeners added. Use setMaxListeners() to increase limit.`;if(this.#r)throw new Error(t);console.warn(t)}}on(e,t){if("string"!=typeof e)throw new TypeError("on(event, handler): event name must be a string");if("function"!=typeof t)throw new TypeError("on(event, handler): handler must be a function");return this.#s(e,t)}once(e,t){if("string"!=typeof e)throw new TypeError("The event name must be a string.");if("function"!=typeof t)throw new TypeError("once(event, handler): handler must be a function");const r=n=>{this.off(e,r),"function"==typeof t&&t(n)};return this.#s(e,r,{once:!0}),r}appendListener(e,t){return this.on(e,t)}appendListenerOnce(e,t){return this.once(e,t)}off(e,t){if("string"!=typeof e)throw new TypeError("off(event, handler): event name must be a string");if("function"!=typeof t)throw new TypeError("off(event, handler): handler must be a function");const r=this.#e.get(e);if(!Array.isArray(r))return;const n=r.findIndex(e=>e.handler===t);-1!==n&&r.splice(n,1),0===r.length&&this.#e.delete(e)}offAll(e){if("string"!=typeof e)throw new TypeError("The event name must be a string.");this.#e.delete(e)}offAllTypes(){this.#e.clear()}listenerCount(e){if("string"!=typeof e)throw new TypeError("listenerCount(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?t.length:0}listeners(e){if("string"!=typeof e)throw new TypeError("listeners(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?[...t].filter(e=>!e.config.once).map(e=>e.handler):[]}onceListeners(e){if("string"!=typeof e)throw new TypeError("onceListeners(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?[...t].filter(e=>e.config.once).map(e=>e.handler):[]}allListeners(e){if("string"!=typeof e)throw new TypeError("allListeners(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?[...t].map(e=>e.handler):[]}eventNames(){return[...this.#e.keys()]}emit(e,...t){if("string"!=typeof e)throw new TypeError("emit(event, data): event name must be a string");const r=this.#e.get(e);return!(!Array.isArray(r)||0===r.length||(r.forEach(e=>e.handler(...t)),0))}setMaxListeners(e){if(!Number.isInteger(e)||e<0)throw new TypeError("setMaxListeners(n): n must be a non-negative integer");this.#t=e}getMaxListeners(){return this.#t}},s=new Map,o=new Map;class i{#o=new n;setThrowOnMaxListeners(e){return this.#o.setThrowOnMaxListeners(e)}getThrowOnMaxListeners(){return this.#o.getThrowOnMaxListeners()}prependListener(e,t){return this.#o.prependListener(e,t)}prependListenerOnce(e,t){return this.#o.prependListenerOnce(e,t)}appendListener(e,t){return this.#o.appendListener(e,t)}appendListenerOnce(e,t){return this.#o.appendListenerOnce(e,t)}on(e,t){return this.#o.on(e,t)}once(e,t){return this.#o.once(e,t)}off(e,t){return this.#o.off(e,t)}offAll(e){return this.#o.offAll(e)}offAllTypes(){return this.#o.offAllTypes()}listenerCount(e){return this.#o.listenerCount(e)}listeners(e){return this.#o.listeners(e)}onceListeners(e){return this.#o.onceListeners(e)}allListeners(e){return this.#o.allListeners(e)}eventNames(){return this.#o.eventNames()}emit(e,...t){return this.#o.emit(e,...t)}setMaxListeners(e){return this.#o.setMaxListeners(e)}getMaxListeners(){return this.#o.getMaxListeners()}static registerJsonType(e,t,r){s.set(e,t),o.set(e,r)}static deleteJsonType(e){s.delete(e),o.delete(e)}static encodeSpecialJson(e){if(void 0===e)return{__undefined__:!0};if(null===e)return{__null__:!0};for(const[t,r]of s.entries())if("string"!=typeof t&&e instanceof t||typeof e===t)return r(e,i.encodeSpecialJson);if(Array.isArray(e))return e.map(i.encodeSpecialJson);if(r(e)){const t={};for(const r in e)t[r]=i.encodeSpecialJson(e[r]);return t}return e}static decodeSpecialJson(e){const t=r(e);if(t){if(e.__undefined__)return;if(e.__null__)return null}if(Array.isArray(e))return e.map(i.decodeSpecialJson);if(t){for(const[t,r]of o.entries())if(r.check&&r.check(e))return r.decode(e,i.decodeSpecialJson);const t={};for(const r in e)t[r]=i.decodeSpecialJson(e[r]);return t}return e}#i=window.localStorage;#a=e=>this.emit("storage",e);constructor(){window.addEventListener("storage",this.#a)}setLocalStorage(e){if(!(e instanceof Storage))throw new Error("Argument must be a valid instance of Storage.");this.#i=e}localStorageExists(){return this.#i instanceof Storage}#l(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");return i.encodeSpecialJson(t)}setJson(e,t){if(!(r(t)||Array.isArray(t)||t instanceof Map||t instanceof Set))throw new Error("The storage value is not a valid JSON-compatible structure.");const n=this.#l(e,t);this.emit("setJson",e,t),this.#i.setItem(e,JSON.stringify(n))}#c(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");const r=this.#i.getItem(e),n={obj:()=>({}),array:()=>[],map:()=>new Map,set:()=>new Set},s="function"==typeof n[t]?n[t]():null;let o;try{o=JSON.parse(r)}catch{return s}return{decoded:i.decodeSpecialJson(o),fallback:s}}getJson(e,t){const{decoded:n,fallback:s}=this.#c(e,t);return n instanceof Map||n instanceof Set||Array.isArray(n)||r(n)?n:s}setDate(e,t){if(!(t instanceof Date))throw new Error("Value must be a Date.");const r=this.#l(e,t);this.#i.setItem(e,JSON.stringify(r)),this.emit("setDate",e,t)}getDate(e){const t=this.#c(e).decoded;return t instanceof Date?t:null}setRegExp(e,t){if(!(t instanceof RegExp))throw new Error("Value must be a RegExp.");const r=this.#l(e,t);this.#i.setItem(e,JSON.stringify(r)),this.emit("setRegExp",e,t)}getRegExp(e){const t=this.#c(e).decoded;return t instanceof RegExp?t:null}setBigInt(e,t){if("bigint"!=typeof t)throw new Error("Value must be a BigInt.");const r=this.#l(e,t);this.#i.setItem(e,JSON.stringify(r)),this.emit("setBigInt",e,t)}getBigInt(e){const t=this.#c(e).decoded;return"bigint"==typeof t?t:null}setSymbol(e,t){if("symbol"!=typeof t)throw new Error("Value must be a Symbol.");const r=this.#l(e,t);this.#i.setItem(e,JSON.stringify(r)),this.emit("setSymbol",e,t)}getSymbol(e){const t=this.#c(e).decoded;return"symbol"==typeof t?t:null}getValue(e){return this.#c(e).decoded??null}setItem(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");return this.emit("setItem",e,t),this.#i.setItem(e,t)}getItem(e){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");return this.#i.getItem(e)}setString(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");if("string"!=typeof t)throw new Error("Value must be a string.");return this.emit("setString",e,t),this.#i.setItem(e,t)}getString(e){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");let t=this.#i.getItem(e);return"string"==typeof t?t:null}setNumber(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");if("number"!=typeof t)throw new Error("Value must be a number.");return this.emit("setNumber",e,t),this.#i.setItem(e,String(t))}getNumber(e){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");let t=this.#i.getItem(e);return"number"==typeof t||"string"==typeof t&&t.length>0&&(t=parseFloat(t),!Number.isNaN(t))?t:null}setBool(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");if("boolean"!=typeof t)throw new Error("Value must be a boolean.");return this.emit("setBool",e,t),this.#i.setItem(e,String(t))}getBool(e){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");const t=this.#i.getItem(e);if("boolean"==typeof t)return t;if("string"==typeof t){if("true"===t)return!0;if("false"===t)return!1}return null}removeItem(e){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");return this.emit("removeItem",e),this.#i.removeItem(e)}clearLocalStorage(){this.#i.clear()}destroy(){window.removeEventListener("storage",this.#a),this.#o.offAllTypes()}}i.registerJsonType(Map,(e,t)=>({__map__:!0,data:Array.from(e.entries()).map(([e,r])=>[e,t(r)])}),{check:e=>e.__map__,decode:(e,t)=>new Map(e.data.map(([e,r])=>[e,t(r)]))}),i.registerJsonType(Set,(e,t)=>({__set__:!0,data:Array.from(e).map(t)}),{check:e=>e.__set__,decode:(e,t)=>new Set(e.data.map(t))}),i.registerJsonType(Date,e=>({__date__:!0,value:e.toISOString()}),{check:e=>e.__date__,decode:e=>new Date(e.value)}),i.registerJsonType(RegExp,e=>({__regexp__:!0,source:e.source,flags:e.flags}),{check:e=>e.__regexp__,decode:e=>new RegExp(e.source,e.flags)}),i.registerJsonType("bigint",e=>({__bigint__:!0,value:e.toString()}),{check:e=>e.__bigint__,decode:e=>BigInt(e.value)}),i.registerJsonType("symbol",e=>({__symbol__:!0,key:Symbol.keyFor(e)??e.description??null}),{check:e=>e.__symbol__,decode:e=>{const t=e.key;return null!=t?Symbol.for(t):Symbol()}});const a=i;window.TinyLocalStorage=t.TinyLocalStorage})();
1
+ (()=>{"use strict";var e={d:(t,r)=>{for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};function r(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)&&"[object Object]"===Object.prototype.toString.call(e)}e.d(t,{TinyLocalStorage:()=>l});const n=class{#e=new Map;#t=10;#r=!1;setThrowOnMaxListeners(e){if("boolean"!=typeof e)throw new TypeError("setThrowOnMaxListeners(value): value must be a boolean");this.#r=e}getThrowOnMaxListeners(){return this.#r}#n(e,t,{once:r=!1}={}){let n=this.#e.get(e);Array.isArray(n)||(n=[],this.#e.set(e,n)),n.unshift({handler:t,config:{once:r}});const s=this.#t;if(s>0&&n.length>s){const t=`Possible memory leak detected. ${n.length} "${e}" listeners added. Use setMaxListeners() to increase limit.`;if(this.#r)throw new Error(t);console.warn(t)}}prependListener(e,t){if("string"!=typeof e)throw new TypeError("prepend(event, handler): event name must be a string");if("function"!=typeof t)throw new TypeError("prepend(event, handler): handler must be a function");this.#n(e,t)}prependListenerOnce(e,t){if("string"!=typeof e)throw new TypeError("prependOnceListener(event, handler): event name must be a string");if("function"!=typeof t)throw new TypeError("prependOnceListener(event, handler): handler must be a function");const r=(...n)=>{this.off(e,r),t(...n)};return this.#n(e,r,{once:!0}),r}#s(e,t,{once:r=!1}={}){let n=this.#e.get(e);Array.isArray(n)||(n=[],this.#e.set(e,n)),n.push({handler:t,config:{once:r}});const s=this.#t;if(s>0&&n.length>s){const t=`Possible memory leak detected. ${n.length} "${e}" listeners added. Use setMaxListeners() to increase limit.`;if(this.#r)throw new Error(t);console.warn(t)}}on(e,t){if("string"!=typeof e)throw new TypeError("on(event, handler): event name must be a string");if("function"!=typeof t)throw new TypeError("on(event, handler): handler must be a function");return this.#s(e,t)}once(e,t){if("string"!=typeof e)throw new TypeError("The event name must be a string.");if("function"!=typeof t)throw new TypeError("once(event, handler): handler must be a function");const r=n=>{this.off(e,r),"function"==typeof t&&t(n)};return this.#s(e,r,{once:!0}),r}appendListener(e,t){return this.on(e,t)}appendListenerOnce(e,t){return this.once(e,t)}off(e,t){if("string"!=typeof e)throw new TypeError("off(event, handler): event name must be a string");if("function"!=typeof t)throw new TypeError("off(event, handler): handler must be a function");const r=this.#e.get(e);if(!Array.isArray(r))return;const n=r.findIndex(e=>e.handler===t);-1!==n&&r.splice(n,1),0===r.length&&this.#e.delete(e)}offAll(e){if("string"!=typeof e)throw new TypeError("The event name must be a string.");this.#e.delete(e)}offAllTypes(){this.#e.clear()}listenerCount(e){if("string"!=typeof e)throw new TypeError("listenerCount(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?t.length:0}listeners(e){if("string"!=typeof e)throw new TypeError("listeners(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?[...t].filter(e=>!e.config.once).map(e=>e.handler):[]}onceListeners(e){if("string"!=typeof e)throw new TypeError("onceListeners(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?[...t].filter(e=>e.config.once).map(e=>e.handler):[]}allListeners(e){if("string"!=typeof e)throw new TypeError("allListeners(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?[...t].map(e=>e.handler):[]}eventNames(){return[...this.#e.keys()]}emit(e,...t){if("string"!=typeof e)throw new TypeError("emit(event, data): event name must be a string");const r=this.#e.get(e);return!(!Array.isArray(r)||0===r.length||(r.forEach(e=>e.handler(...t)),0))}setMaxListeners(e){if(!Number.isInteger(e)||e<0)throw new TypeError("setMaxListeners(n): n must be a non-negative integer");this.#t=e}getMaxListeners(){return this.#t}},s=new Map,o=new Map,i=new Set(["string","boolean","number","object","array",String,Boolean,Number,Object,Array,BigInt,Symbol]);class a{#o=new n;setThrowOnMaxListeners(e){return this.#o.setThrowOnMaxListeners(e)}getThrowOnMaxListeners(){return this.#o.getThrowOnMaxListeners()}prependListener(e,t){return this.#o.prependListener(e,t)}prependListenerOnce(e,t){return this.#o.prependListenerOnce(e,t)}appendListener(e,t){return this.#o.appendListener(e,t)}appendListenerOnce(e,t){return this.#o.appendListenerOnce(e,t)}on(e,t){return this.#o.on(e,t)}once(e,t){return this.#o.once(e,t)}off(e,t){return this.#o.off(e,t)}offAll(e){return this.#o.offAll(e)}offAllTypes(){return this.#o.offAllTypes()}listenerCount(e){return this.#o.listenerCount(e)}listeners(e){return this.#o.listeners(e)}onceListeners(e){return this.#o.onceListeners(e)}allListeners(e){return this.#o.allListeners(e)}eventNames(){return this.#o.eventNames()}emit(e,...t){return this.#o.emit(e,...t)}setMaxListeners(e){return this.#o.setMaxListeners(e)}getMaxListeners(){return this.#o.getMaxListeners()}static hasJsonType(e){return s.has(e)&&o.has(e)||i.has(e)}static registerJsonType(e,t,r,n=!1){if(i.has(e))throw new Error(`Cannot register type "${e}" because it is frozen.`);s.set(e,t),o.set(e,r),n&&i.add(e)}static deleteJsonType(e){if(i.has(e))throw new Error(`Cannot remove type "${e}" because it is frozen.`);let t=!1;return s.delete(e)&&(t=!0),o.delete(e)&&(t=!0),t}static encodeSpecialJson(e){if(void 0===e)return{__undefined__:!0};if(null===e)return{__null__:!0};for(const[t,r]of s.entries())if("string"!=typeof t&&e instanceof t||typeof e===t)return r(e,a.encodeSpecialJson);if(Array.isArray(e))return e.map(a.encodeSpecialJson);if(r(e)){const t={};for(const r in e)t[r]=a.encodeSpecialJson(e[r]);return t}return e}static decodeSpecialJson(e){const t=r(e);if(t){if(e.__undefined__)return;if(e.__null__)return null}if(Array.isArray(e))return e.map(a.decodeSpecialJson);if(t){for(const[t,r]of o.entries())if(r.check&&r.check(e))return r.decode(e,a.decodeSpecialJson);const t={};for(const r in e)t[r]=a.decodeSpecialJson(e[r]);return t}return e}#i=window.localStorage;#a=e=>this.emit("storage",e);constructor(){window.addEventListener("storage",this.#a)}setLocalStorage(e){if(!(e instanceof Storage))throw new Error("Argument must be a valid instance of Storage.");this.#i=e}localStorageExists(){return this.#i instanceof Storage}#l(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");return a.encodeSpecialJson(t)}setJson(e,t){if(!(r(t)||Array.isArray(t)||t instanceof Map||t instanceof Set))throw new Error("The storage value is not a valid JSON-compatible structure.");const n=this.#l(e,t);return this.emit("setJson",e,t),this.#i.setItem(e,JSON.stringify(n))}#c(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");const r=this.#i.getItem(e),n={obj:()=>({}),array:()=>[],map:()=>new Map,set:()=>new Set},s="function"==typeof n[t]?n[t]():null;let o;try{o=JSON.parse(r)}catch{return s}return{decoded:a.decodeSpecialJson(o),fallback:s}}getJson(e,t){const{decoded:n,fallback:s}=this.#c(e,t);return n instanceof Map||n instanceof Set||Array.isArray(n)||r(n)?n:s}setDate(e,t){if(!(t instanceof Date))throw new Error("Value must be a Date.");const r=this.#l(e,t);return this.emit("setDate",e,t),this.#i.setItem(e,JSON.stringify(r))}getDate(e){const t=this.#c(e).decoded;return t instanceof Date?t:null}setRegExp(e,t){if(!(t instanceof RegExp))throw new Error("Value must be a RegExp.");const r=this.#l(e,t);return this.emit("setRegExp",e,t),this.#i.setItem(e,JSON.stringify(r))}getRegExp(e){const t=this.#c(e).decoded;return t instanceof RegExp?t:null}setBigInt(e,t){if("bigint"!=typeof t)throw new Error("Value must be a BigInt.");const r=this.#l(e,t);return this.emit("setBigInt",e,t),this.#i.setItem(e,JSON.stringify(r))}getBigInt(e){const t=this.#c(e).decoded;return"bigint"==typeof t?t:null}setSymbol(e,t){if("symbol"!=typeof t)throw new Error("Value must be a Symbol.");const r=this.#l(e,t);return this.emit("setSymbol",e,t),this.#i.setItem(e,JSON.stringify(r))}getSymbol(e){const t=this.#c(e).decoded;return"symbol"==typeof t?t:null}getValue(e){return this.#c(e).decoded??null}setItem(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");return this.emit("setItem",e,t),this.#i.setItem(e,t)}getItem(e){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");return this.#i.getItem(e)}setString(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");if("string"!=typeof t)throw new Error("Value must be a string.");return this.emit("setString",e,t),this.#i.setItem(e,JSON.stringify({__string__:!0,value:t}))}getString(e){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");let t=this.#i.getItem(e);try{if(t=JSON.parse(t),!r(t)||!t.__string__||"string"!=typeof t.value)return null;t=t.value}catch{t=null}return"string"==typeof t?t:null}setNumber(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");if("number"!=typeof t)throw new Error("Value must be a number.");return this.emit("setNumber",e,t),this.#i.setItem(e,String(t))}getNumber(e){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");let t=this.#i.getItem(e);return"number"==typeof t||"string"==typeof t&&t.length>0&&(t=parseFloat(t),!Number.isNaN(t))?t:null}setBool(e,t){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");if("boolean"!=typeof t)throw new Error("Value must be a boolean.");return this.emit("setBool",e,t),this.#i.setItem(e,String(t))}getBool(e){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");const t=this.#i.getItem(e);if("boolean"==typeof t)return t;if("string"==typeof t){if("true"===t)return!0;if("false"===t)return!1}return null}removeItem(e){if("string"!=typeof e||!e.length)throw new Error("Key must be a non-empty string.");return this.emit("removeItem",e),this.#i.removeItem(e)}clearLocalStorage(){return this.#i.clear()}destroy(){window.removeEventListener("storage",this.#a),this.#o.offAllTypes()}}a.registerJsonType(Map,(e,t)=>({__map__:!0,data:Array.from(e.entries()).map(([e,r])=>[e,t(r)])}),{check:e=>e.__map__,decode:(e,t)=>new Map(e.data.map(([e,r])=>[e,t(r)]))},!0),a.registerJsonType(Set,(e,t)=>({__set__:!0,data:Array.from(e).map(t)}),{check:e=>e.__set__,decode:(e,t)=>new Set(e.data.map(t))},!0),a.registerJsonType(Date,e=>({__date__:!0,value:e.toISOString()}),{check:e=>e.__date__,decode:e=>new Date(e.value)},!0),a.registerJsonType(RegExp,e=>({__regexp__:!0,source:e.source,flags:e.flags}),{check:e=>e.__regexp__,decode:e=>new RegExp(e.source,e.flags)},!0),a.registerJsonType("bigint",e=>({__bigint__:!0,value:e.toString()}),{check:e=>e.__bigint__,decode:e=>BigInt(e.value)},!0),a.registerJsonType("symbol",e=>({__symbol__:!0,key:Symbol.keyFor(e)??e.description??null}),{check:e=>e.__symbol__,decode:e=>{const t=e.key;return null!=t?Symbol.for(t):Symbol()}},!0);const l=a;window.TinyLocalStorage=t.TinyLocalStorage})();
@@ -12198,6 +12198,22 @@ const customEncoders = new Map();
12198
12198
  /** @type {Map<any, DecodeFn>} */
12199
12199
  const customDecoders = new Map();
12200
12200
 
12201
+ /** @type {Set<any>} */
12202
+ const customTypesFreezed = new Set([
12203
+ 'string',
12204
+ 'boolean',
12205
+ 'number',
12206
+ 'object',
12207
+ 'array',
12208
+ String,
12209
+ Boolean,
12210
+ Number,
12211
+ Object,
12212
+ Array,
12213
+ BigInt,
12214
+ Symbol,
12215
+ ]);
12216
+
12201
12217
  /**
12202
12218
  * A function that encodes a value into a serializable JSON-compatible format.
12203
12219
  *
@@ -12469,26 +12485,49 @@ class TinyLocalStorage {
12469
12485
 
12470
12486
  ///////////////////////////////////////////////////
12471
12487
 
12488
+ /**
12489
+ * Checks whether a JSON-serializable type is already registered.
12490
+ *
12491
+ * @param {any} type - The type identifier, which can be a string (e.g., `"bigint"`, `"symbol"`)
12492
+ * or a reference to a class/constructor function.
12493
+ * @returns {boolean} True if the type has both an encoder and decoder registered.
12494
+ */
12495
+ static hasJsonType(type) {
12496
+ return (customEncoders.has(type) && customDecoders.has(type)) || customTypesFreezed.has(type);
12497
+ }
12498
+
12472
12499
  /**
12473
12500
  * Registers a new JSON-serializable type with its encoder and decoder.
12474
12501
  *
12475
- * @param {any} type - The type or primitive type name (e.g. `"bigint"`, `"symbol"`, etc).
12476
- * @param {EncodeFn} encodeFn - The function that encodes the value.
12477
- * @param {DecodeFn} decodeFn - An object with `check` and `decode` methods for restoring the value.
12502
+ * @param {any} type - The type identifier, which can be a string (e.g., `"bigint"`, `"symbol"`)
12503
+ * or a reference to a class/constructor function.
12504
+ * @param {EncodeFn} encodeFn - A function that receives a value of this type and returns a JSON-safe representation.
12505
+ * @param {DecodeFn} decodeFn - An object with `check(value)` to identify this type and `decode(value)` to restore it.
12506
+ * @param {boolean} [freezeType=false] - If true, prevents this type from being unregistered later.
12507
+ * @throws {Error} Throws an error if the type is frozen and cannot be registered.
12478
12508
  */
12479
- static registerJsonType(type, encodeFn, decodeFn) {
12509
+ static registerJsonType(type, encodeFn, decodeFn, freezeType = false) {
12510
+ if (customTypesFreezed.has(type))
12511
+ throw new Error(`Cannot register type "${type}" because it is frozen.`);
12480
12512
  customEncoders.set(type, encodeFn);
12481
12513
  customDecoders.set(type, decodeFn);
12514
+ if (freezeType) customTypesFreezed.add(type);
12482
12515
  }
12483
12516
 
12484
12517
  /**
12485
- * Removes a previously registered custom type from the encoding/decoding system.
12518
+ * Unregisters a previously registered custom type from the encoding/decoding system.
12486
12519
  *
12487
- * @param {string} type - The primitive name or constructor reference used in registration.
12520
+ * @param {any} type - The primitive name or constructor reference used in the registration.
12521
+ * @returns {boolean} Returns true if the type existed and was removed, or false if it wasn't found.
12522
+ * @throws {Error} Throws an error if the type is frozen and cannot be unregistered.
12488
12523
  */
12489
12524
  static deleteJsonType(type) {
12490
- customEncoders.delete(type);
12491
- customDecoders.delete(type);
12525
+ if (customTypesFreezed.has(type))
12526
+ throw new Error(`Cannot remove type "${type}" because it is frozen.`);
12527
+ let isDeleted = false;
12528
+ if (customEncoders.delete(type)) isDeleted = true;
12529
+ if (customDecoders.delete(type)) isDeleted = true;
12530
+ return isDeleted;
12492
12531
  }
12493
12532
 
12494
12533
  //////////////////////////////////////////////////////
@@ -12635,7 +12674,7 @@ class TinyLocalStorage {
12635
12674
  }
12636
12675
  const encoded = this.#setJson(name, data);
12637
12676
  this.emit('setJson', name, data);
12638
- this.#localStorage.setItem(name, JSON.stringify(encoded));
12677
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
12639
12678
  }
12640
12679
 
12641
12680
  /**
@@ -12705,8 +12744,8 @@ class TinyLocalStorage {
12705
12744
  setDate(name, data) {
12706
12745
  if (!(data instanceof Date)) throw new Error('Value must be a Date.');
12707
12746
  const encoded = this.#setJson(name, data);
12708
- this.#localStorage.setItem(name, JSON.stringify(encoded));
12709
12747
  this.emit('setDate', name, data);
12748
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
12710
12749
  }
12711
12750
 
12712
12751
  /**
@@ -12727,8 +12766,8 @@ class TinyLocalStorage {
12727
12766
  setRegExp(name, data) {
12728
12767
  if (!(data instanceof RegExp)) throw new Error('Value must be a RegExp.');
12729
12768
  const encoded = this.#setJson(name, data);
12730
- this.#localStorage.setItem(name, JSON.stringify(encoded));
12731
12769
  this.emit('setRegExp', name, data);
12770
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
12732
12771
  }
12733
12772
 
12734
12773
  /**
@@ -12749,8 +12788,8 @@ class TinyLocalStorage {
12749
12788
  setBigInt(name, data) {
12750
12789
  if (typeof data !== 'bigint') throw new Error('Value must be a BigInt.');
12751
12790
  const encoded = this.#setJson(name, data);
12752
- this.#localStorage.setItem(name, JSON.stringify(encoded));
12753
12791
  this.emit('setBigInt', name, data);
12792
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
12754
12793
  }
12755
12794
 
12756
12795
  /**
@@ -12772,8 +12811,8 @@ class TinyLocalStorage {
12772
12811
  setSymbol(name, data) {
12773
12812
  if (typeof data !== 'symbol') throw new Error('Value must be a Symbol.');
12774
12813
  const encoded = this.#setJson(name, data);
12775
- this.#localStorage.setItem(name, JSON.stringify(encoded));
12776
12814
  this.emit('setSymbol', name, data);
12815
+ return this.#localStorage.setItem(name, JSON.stringify(encoded));
12777
12816
  }
12778
12817
 
12779
12818
  /**
@@ -12833,7 +12872,13 @@ class TinyLocalStorage {
12833
12872
  if (typeof data !== 'string') throw new Error('Value must be a string.');
12834
12873
 
12835
12874
  this.emit('setString', name, data);
12836
- return this.#localStorage.setItem(name, data);
12875
+ return this.#localStorage.setItem(
12876
+ name,
12877
+ JSON.stringify({
12878
+ __string__: true,
12879
+ value: data,
12880
+ }),
12881
+ );
12837
12882
  }
12838
12883
 
12839
12884
  /**
@@ -12846,6 +12891,15 @@ class TinyLocalStorage {
12846
12891
  if (typeof name !== 'string' || !name.length)
12847
12892
  throw new Error('Key must be a non-empty string.');
12848
12893
  let value = this.#localStorage.getItem(name);
12894
+ try {
12895
+ /** @type {{ value: string; __string__: boolean }} */
12896
+ // @ts-ignore
12897
+ value = JSON.parse(value);
12898
+ if (!isJsonObject(value) || !value.__string__ || typeof value.value !== 'string') return null;
12899
+ value = value.value;
12900
+ } catch {
12901
+ value = null;
12902
+ }
12849
12903
  if (typeof value === 'string') return value;
12850
12904
  return null;
12851
12905
  }
@@ -12935,7 +12989,7 @@ class TinyLocalStorage {
12935
12989
  * Clears all data from `localStorage`.
12936
12990
  */
12937
12991
  clearLocalStorage() {
12938
- this.#localStorage.clear();
12992
+ return this.#localStorage.clear();
12939
12993
  }
12940
12994
 
12941
12995
  /**
@@ -12962,6 +13016,7 @@ TinyLocalStorage.registerJsonType(
12962
13016
  decode: (value, decodeSpecialJson) =>
12963
13017
  new Map(value.data.map(([k, v]) => [k, decodeSpecialJson(v)])),
12964
13018
  },
13019
+ true,
12965
13020
  );
12966
13021
 
12967
13022
  // Set
@@ -12975,6 +13030,7 @@ TinyLocalStorage.registerJsonType(
12975
13030
  check: (value) => value.__set__,
12976
13031
  decode: (value, decodeSpecialJson) => new Set(value.data.map(decodeSpecialJson)),
12977
13032
  },
13033
+ true,
12978
13034
  );
12979
13035
 
12980
13036
  // Date
@@ -12988,6 +13044,7 @@ TinyLocalStorage.registerJsonType(
12988
13044
  check: (value) => value.__date__,
12989
13045
  decode: (value) => new Date(value.value),
12990
13046
  },
13047
+ true,
12991
13048
  );
12992
13049
 
12993
13050
  // Regex
@@ -13002,6 +13059,7 @@ TinyLocalStorage.registerJsonType(
13002
13059
  check: (value) => value.__regexp__,
13003
13060
  decode: (value) => new RegExp(value.source, value.flags),
13004
13061
  },
13062
+ true,
13005
13063
  );
13006
13064
 
13007
13065
  // Big Int
@@ -13015,6 +13073,7 @@ TinyLocalStorage.registerJsonType(
13015
13073
  check: (value) => value.__bigint__,
13016
13074
  decode: (value) => BigInt(value.value),
13017
13075
  },
13076
+ true,
13018
13077
  );
13019
13078
 
13020
13079
  // Symbol
@@ -13031,6 +13090,7 @@ TinyLocalStorage.registerJsonType(
13031
13090
  return key != null ? Symbol.for(key) : Symbol();
13032
13091
  },
13033
13092
  },
13093
+ true,
13034
13094
  );
13035
13095
 
13036
13096
  /* harmony default export */ const libs_TinyLocalStorage = ((/* unused pure expression or super */ null && (TinyLocalStorage)));