tiny-essentials 1.19.2 → 1.19.3

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.
@@ -857,6 +857,12 @@ class TinyLocalStorage {
857
857
  /** @type {Storage} */
858
858
  #localStorage = window.localStorage;
859
859
 
860
+ /** @type {string|null} */
861
+ #dbKey = null;
862
+
863
+ /** @type {number} */
864
+ #version = 0;
865
+
860
866
  /** @type {(ev: StorageEvent) => any} */
861
867
  #storageEvent = (ev) => this.emit('storage', ev);
862
868
 
@@ -864,11 +870,84 @@ class TinyLocalStorage {
864
870
  * Initializes the TinyLocalStorage instance and sets up cross-tab sync.
865
871
  *
866
872
  * Adds listener for the native `storage` event to support tab synchronization.
873
+ * @param {string} [dbName] - Unique database name.
867
874
  */
868
- constructor() {
875
+ constructor(dbName) {
876
+ if (typeof dbName !== 'undefined' && typeof dbName !== 'string')
877
+ throw new TypeError('TinyLocalStorage: dbName must be a string if provided.');
878
+ if (typeof dbName === 'string') this.#dbKey = `LSDB::${dbName}`;
869
879
  window.addEventListener('storage', this.#storageEvent);
870
880
  }
871
881
 
882
+ /**
883
+ * Validates that a given key does not conflict with internal database keys.
884
+ *
885
+ * This method is used to prevent accidental overwriting of reserved `LSDB::` keys
886
+ * in `localStorage`, which are used internally by TinyLocalStorage for versioning
887
+ * and data management.
888
+ *
889
+ * @param {string} name - The key to validate before writing to localStorage.
890
+ * @throws {Error} If the key starts with `LSDB::`.
891
+ */
892
+ #isProtectedDbKey(name) {
893
+ if (typeof name === 'string' && name.startsWith('LSDB::'))
894
+ throw new Error(`TinyLocalStorage: Key "${name}" may conflict with reserved dbKeys.`);
895
+ }
896
+
897
+ /**
898
+ * Updates the version of the storage and triggers migration if needed.
899
+ *
900
+ * @param {number} version - Desired version of the database.
901
+ * @param {(oldVersion: number, newVersion: number) => void} onUpgrade - Callback to perform migration logic.
902
+ * @throws {Error} If the database key has not been initialized.
903
+ * @throws {TypeError} If `version` is not a valid positive number.
904
+ * @throws {TypeError} If `onUpgrade` is not a function.
905
+ */
906
+ updateStorageVersion(version, onUpgrade) {
907
+ if (typeof this.#dbKey !== 'string')
908
+ throw new Error(
909
+ 'TinyLocalStorage: Database key is not initialized. Set a valid dbName in the constructor.',
910
+ );
911
+ if (typeof version !== 'number' || Number.isNaN(version) || version < 1)
912
+ throw new TypeError('TinyLocalStorage: version must be a positive number.');
913
+ if (typeof onUpgrade !== 'function')
914
+ throw new TypeError('TinyLocalStorage: onUpgrade must be a function.');
915
+
916
+ // @ts-ignore
917
+ const savedVersion = parseInt(localStorage.getItem(this.#dbKey), 10) || 0;
918
+ if (typeof savedVersion !== 'number' || Number.isNaN(savedVersion) || savedVersion < 0)
919
+ throw new TypeError('TinyLocalStorage: saved version in localStorage is not a valid number.');
920
+
921
+ if (version < savedVersion)
922
+ throw new Error(
923
+ `TinyLocalStorage: Cannot downgrade database version from ${savedVersion} to ${version}.`,
924
+ );
925
+
926
+ if (version > savedVersion) {
927
+ onUpgrade(savedVersion, version);
928
+ localStorage.setItem(this.#dbKey, String(version));
929
+ this.#version = version;
930
+ }
931
+ }
932
+
933
+ /**
934
+ * Gets the current database key used in localStorage.
935
+ *
936
+ * @returns {string|null} The database key, or null if not set.
937
+ */
938
+ getDbKey() {
939
+ return this.#dbKey;
940
+ }
941
+
942
+ /**
943
+ * Gets the current version of the database.
944
+ *
945
+ * @returns {number} The current version number.
946
+ */
947
+ getVersion() {
948
+ return this.#version;
949
+ }
950
+
872
951
  /**
873
952
  * Defines a custom storage interface (e.g. `sessionStorage`).
874
953
  *
@@ -876,7 +955,7 @@ class TinyLocalStorage {
876
955
  */
877
956
  setLocalStorage(localstorage) {
878
957
  if (!(localstorage instanceof Storage))
879
- throw new Error('Argument must be a valid instance of Storage.');
958
+ throw new TypeError('Argument must be a valid instance of Storage.');
880
959
  this.#localStorage = localstorage;
881
960
  }
882
961
 
@@ -898,7 +977,7 @@ class TinyLocalStorage {
898
977
  */
899
978
  #setJson(name, data) {
900
979
  if (typeof name !== 'string' || !name.length)
901
- throw new Error('Key must be a non-empty string.');
980
+ throw new TypeError('Key must be a non-empty string.');
902
981
  return TinyLocalStorage.encodeSpecialJson(data);
903
982
  }
904
983
 
@@ -911,13 +990,14 @@ class TinyLocalStorage {
911
990
  * @param {LocalStorageJsonValue} data - The data to be serialized and stored.
912
991
  */
913
992
  setJson(name, data) {
993
+ this.#isProtectedDbKey(name);
914
994
  if (
915
995
  !isJsonObject(data) &&
916
996
  !Array.isArray(data) &&
917
997
  !(data instanceof Map) &&
918
998
  !(data instanceof Set)
919
999
  ) {
920
- throw new Error('The storage value is not a valid JSON-compatible structure.');
1000
+ throw new TypeError('The storage value is not a valid JSON-compatible structure.');
921
1001
  }
922
1002
  const encoded = this.#setJson(name, data);
923
1003
  this.emit('setJson', name, data);
@@ -935,7 +1015,7 @@ class TinyLocalStorage {
935
1015
  */
936
1016
  #getJson(name, defaultData) {
937
1017
  if (typeof name !== 'string' || !name.length)
938
- throw new Error('Key must be a non-empty string.');
1018
+ throw new TypeError('Key must be a non-empty string.');
939
1019
 
940
1020
  const raw = this.#localStorage.getItem(name);
941
1021
  const fallbackTypes = {
@@ -989,7 +1069,8 @@ class TinyLocalStorage {
989
1069
  * @param {Date} data
990
1070
  */
991
1071
  setDate(name, data) {
992
- if (!(data instanceof Date)) throw new Error('Value must be a Date.');
1072
+ this.#isProtectedDbKey(name);
1073
+ if (!(data instanceof Date)) throw new TypeError('Value must be a Date.');
993
1074
  const encoded = this.#setJson(name, data);
994
1075
  this.emit('setDate', name, data);
995
1076
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -1011,7 +1092,8 @@ class TinyLocalStorage {
1011
1092
  * @param {RegExp} data
1012
1093
  */
1013
1094
  setRegExp(name, data) {
1014
- if (!(data instanceof RegExp)) throw new Error('Value must be a RegExp.');
1095
+ this.#isProtectedDbKey(name);
1096
+ if (!(data instanceof RegExp)) throw new TypeError('Value must be a RegExp.');
1015
1097
  const encoded = this.#setJson(name, data);
1016
1098
  this.emit('setRegExp', name, data);
1017
1099
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -1033,7 +1115,8 @@ class TinyLocalStorage {
1033
1115
  * @param {bigint} data
1034
1116
  */
1035
1117
  setBigInt(name, data) {
1036
- if (typeof data !== 'bigint') throw new Error('Value must be a BigInt.');
1118
+ this.#isProtectedDbKey(name);
1119
+ if (typeof data !== 'bigint') throw new TypeError('Value must be a BigInt.');
1037
1120
  const encoded = this.#setJson(name, data);
1038
1121
  this.emit('setBigInt', name, data);
1039
1122
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -1056,7 +1139,8 @@ class TinyLocalStorage {
1056
1139
  * @param {symbol} data
1057
1140
  */
1058
1141
  setSymbol(name, data) {
1059
- if (typeof data !== 'symbol') throw new Error('Value must be a Symbol.');
1142
+ this.#isProtectedDbKey(name);
1143
+ if (typeof data !== 'symbol') throw new TypeError('Value must be a Symbol.');
1060
1144
  const encoded = this.#setJson(name, data);
1061
1145
  this.emit('setSymbol', name, data);
1062
1146
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -1089,8 +1173,9 @@ class TinyLocalStorage {
1089
1173
  * @param {any} data - The data to store.
1090
1174
  */
1091
1175
  setItem(name, data) {
1176
+ this.#isProtectedDbKey(name);
1092
1177
  if (typeof name !== 'string' || !name.length)
1093
- throw new Error('Key must be a non-empty string.');
1178
+ throw new TypeError('Key must be a non-empty string.');
1094
1179
  this.emit('setItem', name, data);
1095
1180
  return this.#localStorage.setItem(name, data);
1096
1181
  }
@@ -1103,7 +1188,7 @@ class TinyLocalStorage {
1103
1188
  */
1104
1189
  getItem(name) {
1105
1190
  if (typeof name !== 'string' || !name.length)
1106
- throw new Error('Key must be a non-empty string.');
1191
+ throw new TypeError('Key must be a non-empty string.');
1107
1192
  return this.#localStorage.getItem(name);
1108
1193
  }
1109
1194
 
@@ -1114,9 +1199,10 @@ class TinyLocalStorage {
1114
1199
  * @param {string} data - The string to store.
1115
1200
  */
1116
1201
  setString(name, data) {
1202
+ this.#isProtectedDbKey(name);
1117
1203
  if (typeof name !== 'string' || !name.length)
1118
- throw new Error('Key must be a non-empty string.');
1119
- if (typeof data !== 'string') throw new Error('Value must be a string.');
1204
+ throw new TypeError('Key must be a non-empty string.');
1205
+ if (typeof data !== 'string') throw new TypeError('Value must be a string.');
1120
1206
 
1121
1207
  this.emit('setString', name, data);
1122
1208
  return this.#localStorage.setItem(
@@ -1136,7 +1222,7 @@ class TinyLocalStorage {
1136
1222
  */
1137
1223
  getString(name) {
1138
1224
  if (typeof name !== 'string' || !name.length)
1139
- throw new Error('Key must be a non-empty string.');
1225
+ throw new TypeError('Key must be a non-empty string.');
1140
1226
  let value = this.#localStorage.getItem(name);
1141
1227
  try {
1142
1228
  /** @type {{ value: string; __string__: boolean }} */
@@ -1158,9 +1244,10 @@ class TinyLocalStorage {
1158
1244
  * @param {number} data - The number to store.
1159
1245
  */
1160
1246
  setNumber(name, data) {
1247
+ this.#isProtectedDbKey(name);
1161
1248
  if (typeof name !== 'string' || !name.length)
1162
- throw new Error('Key must be a non-empty string.');
1163
- if (typeof data !== 'number') throw new Error('Value must be a number.');
1249
+ throw new TypeError('Key must be a non-empty string.');
1250
+ if (typeof data !== 'number') throw new TypeError('Value must be a number.');
1164
1251
  this.emit('setNumber', name, data);
1165
1252
  return this.#localStorage.setItem(name, String(data));
1166
1253
  }
@@ -1173,7 +1260,7 @@ class TinyLocalStorage {
1173
1260
  */
1174
1261
  getNumber(name) {
1175
1262
  if (typeof name !== 'string' || !name.length)
1176
- throw new Error('Key must be a non-empty string.');
1263
+ throw new TypeError('Key must be a non-empty string.');
1177
1264
 
1178
1265
  /** @type {number|string|null} */
1179
1266
  let number = this.#localStorage.getItem(name);
@@ -1192,9 +1279,10 @@ class TinyLocalStorage {
1192
1279
  * @param {boolean} data - The boolean value to store.
1193
1280
  */
1194
1281
  setBool(name, data) {
1282
+ this.#isProtectedDbKey(name);
1195
1283
  if (typeof name !== 'string' || !name.length)
1196
- throw new Error('Key must be a non-empty string.');
1197
- if (typeof data !== 'boolean') throw new Error('Value must be a boolean.');
1284
+ throw new TypeError('Key must be a non-empty string.');
1285
+ if (typeof data !== 'boolean') throw new TypeError('Value must be a boolean.');
1198
1286
  this.emit('setBool', name, data);
1199
1287
  return this.#localStorage.setItem(name, String(data));
1200
1288
  }
@@ -1207,7 +1295,7 @@ class TinyLocalStorage {
1207
1295
  */
1208
1296
  getBool(name) {
1209
1297
  if (typeof name !== 'string' || !name.length)
1210
- throw new Error('Key must be a non-empty string.');
1298
+ throw new TypeError('Key must be a non-empty string.');
1211
1299
 
1212
1300
  const value = this.#localStorage.getItem(name);
1213
1301
  if (typeof value === 'boolean') return value;
@@ -1226,7 +1314,7 @@ class TinyLocalStorage {
1226
1314
  */
1227
1315
  removeItem(name) {
1228
1316
  if (typeof name !== 'string' || !name.length)
1229
- throw new Error('Key must be a non-empty string.');
1317
+ throw new TypeError('Key must be a non-empty string.');
1230
1318
 
1231
1319
  this.emit('removeItem', name);
1232
1320
  return this.#localStorage.removeItem(name);
@@ -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:()=>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})();
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=null;#l=0;#c=e=>this.emit("storage",e);constructor(e){if(void 0!==e&&"string"!=typeof e)throw new TypeError("TinyLocalStorage: dbName must be a string if provided.");"string"==typeof e&&(this.#a=`LSDB::${e}`),window.addEventListener("storage",this.#c)}#h(e){if("string"==typeof e&&e.startsWith("LSDB::"))throw new Error(`TinyLocalStorage: Key "${e}" may conflict with reserved dbKeys.`)}updateStorageVersion(e,t){if("string"!=typeof this.#a)throw new Error("TinyLocalStorage: Database key is not initialized. Set a valid dbName in the constructor.");if("number"!=typeof e||Number.isNaN(e)||e<1)throw new TypeError("TinyLocalStorage: version must be a positive number.");if("function"!=typeof t)throw new TypeError("TinyLocalStorage: onUpgrade must be a function.");const r=parseInt(localStorage.getItem(this.#a),10)||0;if("number"!=typeof r||Number.isNaN(r)||r<0)throw new TypeError("TinyLocalStorage: saved version in localStorage is not a valid number.");if(e<r)throw new Error(`TinyLocalStorage: Cannot downgrade database version from ${r} to ${e}.`);e>r&&(t(r,e),localStorage.setItem(this.#a,String(e)),this.#l=e)}getDbKey(){return this.#a}getVersion(){return this.#l}setLocalStorage(e){if(!(e instanceof Storage))throw new TypeError("Argument must be a valid instance of Storage.");this.#i=e}localStorageExists(){return this.#i instanceof Storage}#y(e,t){if("string"!=typeof e||!e.length)throw new TypeError("Key must be a non-empty string.");return a.encodeSpecialJson(t)}setJson(e,t){if(this.#h(e),!(r(t)||Array.isArray(t)||t instanceof Map||t instanceof Set))throw new TypeError("The storage value is not a valid JSON-compatible structure.");const n=this.#y(e,t);return this.emit("setJson",e,t),this.#i.setItem(e,JSON.stringify(n))}#g(e,t){if("string"!=typeof e||!e.length)throw new TypeError("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.#g(e,t);return n instanceof Map||n instanceof Set||Array.isArray(n)||r(n)?n:s}setDate(e,t){if(this.#h(e),!(t instanceof Date))throw new TypeError("Value must be a Date.");const r=this.#y(e,t);return this.emit("setDate",e,t),this.#i.setItem(e,JSON.stringify(r))}getDate(e){const t=this.#g(e).decoded;return t instanceof Date?t:null}setRegExp(e,t){if(this.#h(e),!(t instanceof RegExp))throw new TypeError("Value must be a RegExp.");const r=this.#y(e,t);return this.emit("setRegExp",e,t),this.#i.setItem(e,JSON.stringify(r))}getRegExp(e){const t=this.#g(e).decoded;return t instanceof RegExp?t:null}setBigInt(e,t){if(this.#h(e),"bigint"!=typeof t)throw new TypeError("Value must be a BigInt.");const r=this.#y(e,t);return this.emit("setBigInt",e,t),this.#i.setItem(e,JSON.stringify(r))}getBigInt(e){const t=this.#g(e).decoded;return"bigint"==typeof t?t:null}setSymbol(e,t){if(this.#h(e),"symbol"!=typeof t)throw new TypeError("Value must be a Symbol.");const r=this.#y(e,t);return this.emit("setSymbol",e,t),this.#i.setItem(e,JSON.stringify(r))}getSymbol(e){const t=this.#g(e).decoded;return"symbol"==typeof t?t:null}getValue(e){return this.#g(e).decoded??null}setItem(e,t){if(this.#h(e),"string"!=typeof e||!e.length)throw new TypeError("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 TypeError("Key must be a non-empty string.");return this.#i.getItem(e)}setString(e,t){if(this.#h(e),"string"!=typeof e||!e.length)throw new TypeError("Key must be a non-empty string.");if("string"!=typeof t)throw new TypeError("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 TypeError("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(this.#h(e),"string"!=typeof e||!e.length)throw new TypeError("Key must be a non-empty string.");if("number"!=typeof t)throw new TypeError("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 TypeError("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(this.#h(e),"string"!=typeof e||!e.length)throw new TypeError("Key must be a non-empty string.");if("boolean"!=typeof t)throw new TypeError("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 TypeError("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 TypeError("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.#c),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})();
@@ -12610,6 +12610,12 @@ class TinyLocalStorage {
12610
12610
  /** @type {Storage} */
12611
12611
  #localStorage = window.localStorage;
12612
12612
 
12613
+ /** @type {string|null} */
12614
+ #dbKey = null;
12615
+
12616
+ /** @type {number} */
12617
+ #version = 0;
12618
+
12613
12619
  /** @type {(ev: StorageEvent) => any} */
12614
12620
  #storageEvent = (ev) => this.emit('storage', ev);
12615
12621
 
@@ -12617,11 +12623,84 @@ class TinyLocalStorage {
12617
12623
  * Initializes the TinyLocalStorage instance and sets up cross-tab sync.
12618
12624
  *
12619
12625
  * Adds listener for the native `storage` event to support tab synchronization.
12626
+ * @param {string} [dbName] - Unique database name.
12620
12627
  */
12621
- constructor() {
12628
+ constructor(dbName) {
12629
+ if (typeof dbName !== 'undefined' && typeof dbName !== 'string')
12630
+ throw new TypeError('TinyLocalStorage: dbName must be a string if provided.');
12631
+ if (typeof dbName === 'string') this.#dbKey = `LSDB::${dbName}`;
12622
12632
  window.addEventListener('storage', this.#storageEvent);
12623
12633
  }
12624
12634
 
12635
+ /**
12636
+ * Validates that a given key does not conflict with internal database keys.
12637
+ *
12638
+ * This method is used to prevent accidental overwriting of reserved `LSDB::` keys
12639
+ * in `localStorage`, which are used internally by TinyLocalStorage for versioning
12640
+ * and data management.
12641
+ *
12642
+ * @param {string} name - The key to validate before writing to localStorage.
12643
+ * @throws {Error} If the key starts with `LSDB::`.
12644
+ */
12645
+ #isProtectedDbKey(name) {
12646
+ if (typeof name === 'string' && name.startsWith('LSDB::'))
12647
+ throw new Error(`TinyLocalStorage: Key "${name}" may conflict with reserved dbKeys.`);
12648
+ }
12649
+
12650
+ /**
12651
+ * Updates the version of the storage and triggers migration if needed.
12652
+ *
12653
+ * @param {number} version - Desired version of the database.
12654
+ * @param {(oldVersion: number, newVersion: number) => void} onUpgrade - Callback to perform migration logic.
12655
+ * @throws {Error} If the database key has not been initialized.
12656
+ * @throws {TypeError} If `version` is not a valid positive number.
12657
+ * @throws {TypeError} If `onUpgrade` is not a function.
12658
+ */
12659
+ updateStorageVersion(version, onUpgrade) {
12660
+ if (typeof this.#dbKey !== 'string')
12661
+ throw new Error(
12662
+ 'TinyLocalStorage: Database key is not initialized. Set a valid dbName in the constructor.',
12663
+ );
12664
+ if (typeof version !== 'number' || Number.isNaN(version) || version < 1)
12665
+ throw new TypeError('TinyLocalStorage: version must be a positive number.');
12666
+ if (typeof onUpgrade !== 'function')
12667
+ throw new TypeError('TinyLocalStorage: onUpgrade must be a function.');
12668
+
12669
+ // @ts-ignore
12670
+ const savedVersion = parseInt(localStorage.getItem(this.#dbKey), 10) || 0;
12671
+ if (typeof savedVersion !== 'number' || Number.isNaN(savedVersion) || savedVersion < 0)
12672
+ throw new TypeError('TinyLocalStorage: saved version in localStorage is not a valid number.');
12673
+
12674
+ if (version < savedVersion)
12675
+ throw new Error(
12676
+ `TinyLocalStorage: Cannot downgrade database version from ${savedVersion} to ${version}.`,
12677
+ );
12678
+
12679
+ if (version > savedVersion) {
12680
+ onUpgrade(savedVersion, version);
12681
+ localStorage.setItem(this.#dbKey, String(version));
12682
+ this.#version = version;
12683
+ }
12684
+ }
12685
+
12686
+ /**
12687
+ * Gets the current database key used in localStorage.
12688
+ *
12689
+ * @returns {string|null} The database key, or null if not set.
12690
+ */
12691
+ getDbKey() {
12692
+ return this.#dbKey;
12693
+ }
12694
+
12695
+ /**
12696
+ * Gets the current version of the database.
12697
+ *
12698
+ * @returns {number} The current version number.
12699
+ */
12700
+ getVersion() {
12701
+ return this.#version;
12702
+ }
12703
+
12625
12704
  /**
12626
12705
  * Defines a custom storage interface (e.g. `sessionStorage`).
12627
12706
  *
@@ -12629,7 +12708,7 @@ class TinyLocalStorage {
12629
12708
  */
12630
12709
  setLocalStorage(localstorage) {
12631
12710
  if (!(localstorage instanceof Storage))
12632
- throw new Error('Argument must be a valid instance of Storage.');
12711
+ throw new TypeError('Argument must be a valid instance of Storage.');
12633
12712
  this.#localStorage = localstorage;
12634
12713
  }
12635
12714
 
@@ -12651,7 +12730,7 @@ class TinyLocalStorage {
12651
12730
  */
12652
12731
  #setJson(name, data) {
12653
12732
  if (typeof name !== 'string' || !name.length)
12654
- throw new Error('Key must be a non-empty string.');
12733
+ throw new TypeError('Key must be a non-empty string.');
12655
12734
  return TinyLocalStorage.encodeSpecialJson(data);
12656
12735
  }
12657
12736
 
@@ -12664,13 +12743,14 @@ class TinyLocalStorage {
12664
12743
  * @param {LocalStorageJsonValue} data - The data to be serialized and stored.
12665
12744
  */
12666
12745
  setJson(name, data) {
12746
+ this.#isProtectedDbKey(name);
12667
12747
  if (
12668
12748
  !isJsonObject(data) &&
12669
12749
  !Array.isArray(data) &&
12670
12750
  !(data instanceof Map) &&
12671
12751
  !(data instanceof Set)
12672
12752
  ) {
12673
- throw new Error('The storage value is not a valid JSON-compatible structure.');
12753
+ throw new TypeError('The storage value is not a valid JSON-compatible structure.');
12674
12754
  }
12675
12755
  const encoded = this.#setJson(name, data);
12676
12756
  this.emit('setJson', name, data);
@@ -12688,7 +12768,7 @@ class TinyLocalStorage {
12688
12768
  */
12689
12769
  #getJson(name, defaultData) {
12690
12770
  if (typeof name !== 'string' || !name.length)
12691
- throw new Error('Key must be a non-empty string.');
12771
+ throw new TypeError('Key must be a non-empty string.');
12692
12772
 
12693
12773
  const raw = this.#localStorage.getItem(name);
12694
12774
  const fallbackTypes = {
@@ -12742,7 +12822,8 @@ class TinyLocalStorage {
12742
12822
  * @param {Date} data
12743
12823
  */
12744
12824
  setDate(name, data) {
12745
- if (!(data instanceof Date)) throw new Error('Value must be a Date.');
12825
+ this.#isProtectedDbKey(name);
12826
+ if (!(data instanceof Date)) throw new TypeError('Value must be a Date.');
12746
12827
  const encoded = this.#setJson(name, data);
12747
12828
  this.emit('setDate', name, data);
12748
12829
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -12764,7 +12845,8 @@ class TinyLocalStorage {
12764
12845
  * @param {RegExp} data
12765
12846
  */
12766
12847
  setRegExp(name, data) {
12767
- if (!(data instanceof RegExp)) throw new Error('Value must be a RegExp.');
12848
+ this.#isProtectedDbKey(name);
12849
+ if (!(data instanceof RegExp)) throw new TypeError('Value must be a RegExp.');
12768
12850
  const encoded = this.#setJson(name, data);
12769
12851
  this.emit('setRegExp', name, data);
12770
12852
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -12786,7 +12868,8 @@ class TinyLocalStorage {
12786
12868
  * @param {bigint} data
12787
12869
  */
12788
12870
  setBigInt(name, data) {
12789
- if (typeof data !== 'bigint') throw new Error('Value must be a BigInt.');
12871
+ this.#isProtectedDbKey(name);
12872
+ if (typeof data !== 'bigint') throw new TypeError('Value must be a BigInt.');
12790
12873
  const encoded = this.#setJson(name, data);
12791
12874
  this.emit('setBigInt', name, data);
12792
12875
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -12809,7 +12892,8 @@ class TinyLocalStorage {
12809
12892
  * @param {symbol} data
12810
12893
  */
12811
12894
  setSymbol(name, data) {
12812
- if (typeof data !== 'symbol') throw new Error('Value must be a Symbol.');
12895
+ this.#isProtectedDbKey(name);
12896
+ if (typeof data !== 'symbol') throw new TypeError('Value must be a Symbol.');
12813
12897
  const encoded = this.#setJson(name, data);
12814
12898
  this.emit('setSymbol', name, data);
12815
12899
  return this.#localStorage.setItem(name, JSON.stringify(encoded));
@@ -12842,8 +12926,9 @@ class TinyLocalStorage {
12842
12926
  * @param {any} data - The data to store.
12843
12927
  */
12844
12928
  setItem(name, data) {
12929
+ this.#isProtectedDbKey(name);
12845
12930
  if (typeof name !== 'string' || !name.length)
12846
- throw new Error('Key must be a non-empty string.');
12931
+ throw new TypeError('Key must be a non-empty string.');
12847
12932
  this.emit('setItem', name, data);
12848
12933
  return this.#localStorage.setItem(name, data);
12849
12934
  }
@@ -12856,7 +12941,7 @@ class TinyLocalStorage {
12856
12941
  */
12857
12942
  getItem(name) {
12858
12943
  if (typeof name !== 'string' || !name.length)
12859
- throw new Error('Key must be a non-empty string.');
12944
+ throw new TypeError('Key must be a non-empty string.');
12860
12945
  return this.#localStorage.getItem(name);
12861
12946
  }
12862
12947
 
@@ -12867,9 +12952,10 @@ class TinyLocalStorage {
12867
12952
  * @param {string} data - The string to store.
12868
12953
  */
12869
12954
  setString(name, data) {
12955
+ this.#isProtectedDbKey(name);
12870
12956
  if (typeof name !== 'string' || !name.length)
12871
- throw new Error('Key must be a non-empty string.');
12872
- if (typeof data !== 'string') throw new Error('Value must be a string.');
12957
+ throw new TypeError('Key must be a non-empty string.');
12958
+ if (typeof data !== 'string') throw new TypeError('Value must be a string.');
12873
12959
 
12874
12960
  this.emit('setString', name, data);
12875
12961
  return this.#localStorage.setItem(
@@ -12889,7 +12975,7 @@ class TinyLocalStorage {
12889
12975
  */
12890
12976
  getString(name) {
12891
12977
  if (typeof name !== 'string' || !name.length)
12892
- throw new Error('Key must be a non-empty string.');
12978
+ throw new TypeError('Key must be a non-empty string.');
12893
12979
  let value = this.#localStorage.getItem(name);
12894
12980
  try {
12895
12981
  /** @type {{ value: string; __string__: boolean }} */
@@ -12911,9 +12997,10 @@ class TinyLocalStorage {
12911
12997
  * @param {number} data - The number to store.
12912
12998
  */
12913
12999
  setNumber(name, data) {
13000
+ this.#isProtectedDbKey(name);
12914
13001
  if (typeof name !== 'string' || !name.length)
12915
- throw new Error('Key must be a non-empty string.');
12916
- if (typeof data !== 'number') throw new Error('Value must be a number.');
13002
+ throw new TypeError('Key must be a non-empty string.');
13003
+ if (typeof data !== 'number') throw new TypeError('Value must be a number.');
12917
13004
  this.emit('setNumber', name, data);
12918
13005
  return this.#localStorage.setItem(name, String(data));
12919
13006
  }
@@ -12926,7 +13013,7 @@ class TinyLocalStorage {
12926
13013
  */
12927
13014
  getNumber(name) {
12928
13015
  if (typeof name !== 'string' || !name.length)
12929
- throw new Error('Key must be a non-empty string.');
13016
+ throw new TypeError('Key must be a non-empty string.');
12930
13017
 
12931
13018
  /** @type {number|string|null} */
12932
13019
  let number = this.#localStorage.getItem(name);
@@ -12945,9 +13032,10 @@ class TinyLocalStorage {
12945
13032
  * @param {boolean} data - The boolean value to store.
12946
13033
  */
12947
13034
  setBool(name, data) {
13035
+ this.#isProtectedDbKey(name);
12948
13036
  if (typeof name !== 'string' || !name.length)
12949
- throw new Error('Key must be a non-empty string.');
12950
- if (typeof data !== 'boolean') throw new Error('Value must be a boolean.');
13037
+ throw new TypeError('Key must be a non-empty string.');
13038
+ if (typeof data !== 'boolean') throw new TypeError('Value must be a boolean.');
12951
13039
  this.emit('setBool', name, data);
12952
13040
  return this.#localStorage.setItem(name, String(data));
12953
13041
  }
@@ -12960,7 +13048,7 @@ class TinyLocalStorage {
12960
13048
  */
12961
13049
  getBool(name) {
12962
13050
  if (typeof name !== 'string' || !name.length)
12963
- throw new Error('Key must be a non-empty string.');
13051
+ throw new TypeError('Key must be a non-empty string.');
12964
13052
 
12965
13053
  const value = this.#localStorage.getItem(name);
12966
13054
  if (typeof value === 'boolean') return value;
@@ -12979,7 +13067,7 @@ class TinyLocalStorage {
12979
13067
  */
12980
13068
  removeItem(name) {
12981
13069
  if (typeof name !== 'string' || !name.length)
12982
- throw new Error('Key must be a non-empty string.');
13070
+ throw new TypeError('Key must be a non-empty string.');
12983
13071
 
12984
13072
  this.emit('removeItem', name);
12985
13073
  return this.#localStorage.removeItem(name);