@choksheak/react-utils 0.3.1 → 0.3.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.
package/sharedState.mjs CHANGED
@@ -5,6 +5,7 @@ import { useSyncExternalStore } from "react";
5
5
  // src/storage.ts
6
6
  import { kvStore } from "@choksheak/ts-utils/kvStore";
7
7
  import { LocalStore } from "@choksheak/ts-utils/localStore";
8
+ var IS_NOT_BROWSER = typeof window === "undefined";
8
9
  function getStorageAdapter(options, defaultStoreExpiryMs) {
9
10
  const { store } = options ?? {};
10
11
  if (!store) {
@@ -12,6 +13,7 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
12
13
  }
13
14
  if ("persistTo" in store && typeof store.persistTo === "string" && "key" in store && typeof store.key === "string") {
14
15
  const expiryMs = store.expiryMs || defaultStoreExpiryMs;
16
+ const isValid = store.isValid;
15
17
  switch (store.persistTo) {
16
18
  case "localStorage": {
17
19
  const adapter = new LocalStore(store.key, {
@@ -19,40 +21,58 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
19
21
  });
20
22
  return {
21
23
  set: (key, value) => {
22
- if (typeof window === "undefined") return;
24
+ if (IS_NOT_BROWSER) return;
23
25
  adapter.set(key, value);
24
26
  },
25
27
  get: (key) => {
26
- if (typeof window === "undefined") return void 0;
27
- return adapter.get(key);
28
+ if (IS_NOT_BROWSER) return void 0;
29
+ const value = adapter.get(key);
30
+ if (isValid && !isValid(value)) {
31
+ console.warn(
32
+ `localStorage adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
33
+ );
34
+ adapter.delete(key);
35
+ return void 0;
36
+ }
37
+ return value;
28
38
  },
29
39
  delete: (key) => {
30
- if (typeof window === "undefined") return;
40
+ if (IS_NOT_BROWSER) return;
31
41
  adapter.delete(key);
32
42
  },
33
43
  clear: () => {
34
- if (typeof window === "undefined") return;
44
+ if (IS_NOT_BROWSER) return;
35
45
  adapter.clear();
36
46
  }
37
47
  };
38
48
  }
39
49
  case "indexedDb": {
40
50
  const keyPrefix = store.key + ":";
51
+ const isValid2 = store.isValid;
41
52
  return {
42
53
  set: async (key, value) => {
43
- if (typeof window === "undefined") return;
54
+ if (IS_NOT_BROWSER) return;
44
55
  await kvStore.set(keyPrefix + key, value, expiryMs);
45
56
  },
46
57
  get: async (key) => {
47
- if (typeof window === "undefined") return void 0;
48
- return await kvStore.get(keyPrefix + key);
58
+ if (IS_NOT_BROWSER) return void 0;
59
+ const fullKey = keyPrefix + key;
60
+ const value = await kvStore.get(fullKey);
61
+ if (isValid2 && !isValid2(value)) {
62
+ console.warn(
63
+ `indexedDb adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
64
+ );
65
+ await kvStore.delete(fullKey);
66
+ return void 0;
67
+ }
68
+ return value;
49
69
  },
50
70
  delete: async (key) => {
51
- if (typeof window === "undefined") return;
71
+ if (IS_NOT_BROWSER) return;
52
72
  await kvStore.delete(keyPrefix + key);
53
73
  },
54
74
  clear: async () => {
55
- if (typeof window === "undefined") return;
75
+ if (IS_NOT_BROWSER) return;
56
76
  await kvStore.clear();
57
77
  }
58
78
  };
@@ -153,39 +173,65 @@ var SharedState = class {
153
173
  setValueBounded;
154
174
  initStarted = false;
155
175
  initDone = false;
156
- // `subscribe` function required by useSyncExternalStore
176
+ /** `subscribe` function required by useSyncExternalStore. */
157
177
  subscribe(subscriber) {
158
178
  return pubSubStore.subscribe(this.pubSubKey, () => subscriber());
159
179
  }
160
- // Get the value of the shared state.
161
- // `getSnapshot` function — must return same ref if value unchanged
180
+ /** Returns true if there is a backing store. */
181
+ hasStore() {
182
+ return Boolean(this.storageAdapter);
183
+ }
184
+ /**
185
+ * Set a value directly into the store. Does nothing if there is no
186
+ * backing store.
187
+ *
188
+ * Warning: If you want to set the value properly, you should use
189
+ * `setValue()` instead which also updates the in-memory copy.
190
+ */
191
+ async setInStore(value) {
192
+ return await this.storageAdapter?.set(STORAGE_KEY, value);
193
+ }
194
+ /**
195
+ * Get a value directly from the store. Returns undefined if there is no
196
+ * backing store.
197
+ */
198
+ async getFromStore() {
199
+ return await this.storageAdapter?.get(STORAGE_KEY);
200
+ }
201
+ /**
202
+ * Get the value of the shared state.
203
+ * `getSnapshot` function — must return same ref if value unchanged.
204
+ * Note that this would return the default value if data init from store is
205
+ * not completed yet.
206
+ */
162
207
  getSnapshot() {
163
208
  return pubSubStore.get(this.pubSubKey);
164
209
  }
165
- // Set the value of the shared state.
210
+ /** Set the value of the shared state. */
166
211
  setValue(next) {
167
212
  if (typeof next === "function") {
168
213
  const prev = pubSubStore.get(this.pubSubKey);
169
214
  next = next(prev);
170
215
  }
171
216
  pubSubStore.set(this.pubSubKey, next);
172
- void this.storageAdapter?.set(STORAGE_KEY, next);
217
+ void this.setInStore(next);
173
218
  }
174
- // Remove the value of the shared state. Actually this just sets the value
175
- // back to the given default value.
219
+ /**
220
+ * Remove the value of the shared state. Actually this just sets the value
221
+ * back to the given default value.
222
+ */
176
223
  delete() {
177
224
  pubSubStore.set(this.pubSubKey, this.defaultValue);
178
225
  void this.storageAdapter?.set(STORAGE_KEY, this.defaultValue);
179
226
  }
180
- // Initial default handling
227
+ /** Initial default handling. */
181
228
  async initDefaultValueOnce() {
182
229
  if (this.initDone || this.initStarted) return;
183
230
  this.initStarted = true;
184
- const v = await this.storageAdapter?.get(STORAGE_KEY);
185
- const value = v !== void 0 ? v : this.defaultValue;
231
+ const stored = await this.getFromStore();
232
+ const value = stored !== void 0 ? stored : this.defaultValue;
186
233
  pubSubStore.set(this.pubSubKey, value);
187
234
  this.initDone = true;
188
- pubSubStore.set(this.pubSubKey, value);
189
235
  }
190
236
  };
191
237
  function sharedState(defaultValue, options) {
package/storage.cjs CHANGED
@@ -25,6 +25,7 @@ __export(storage_exports, {
25
25
  module.exports = __toCommonJS(storage_exports);
26
26
  var import_kvStore = require("@choksheak/ts-utils/kvStore");
27
27
  var import_localStore = require("@choksheak/ts-utils/localStore");
28
+ var IS_NOT_BROWSER = typeof window === "undefined";
28
29
  function getStorageAdapter(options, defaultStoreExpiryMs) {
29
30
  const { store } = options ?? {};
30
31
  if (!store) {
@@ -32,6 +33,7 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
32
33
  }
33
34
  if ("persistTo" in store && typeof store.persistTo === "string" && "key" in store && typeof store.key === "string") {
34
35
  const expiryMs = store.expiryMs || defaultStoreExpiryMs;
36
+ const isValid = store.isValid;
35
37
  switch (store.persistTo) {
36
38
  case "localStorage": {
37
39
  const adapter = new import_localStore.LocalStore(store.key, {
@@ -39,40 +41,58 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
39
41
  });
40
42
  return {
41
43
  set: (key, value) => {
42
- if (typeof window === "undefined") return;
44
+ if (IS_NOT_BROWSER) return;
43
45
  adapter.set(key, value);
44
46
  },
45
47
  get: (key) => {
46
- if (typeof window === "undefined") return void 0;
47
- return adapter.get(key);
48
+ if (IS_NOT_BROWSER) return void 0;
49
+ const value = adapter.get(key);
50
+ if (isValid && !isValid(value)) {
51
+ console.warn(
52
+ `localStorage adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
53
+ );
54
+ adapter.delete(key);
55
+ return void 0;
56
+ }
57
+ return value;
48
58
  },
49
59
  delete: (key) => {
50
- if (typeof window === "undefined") return;
60
+ if (IS_NOT_BROWSER) return;
51
61
  adapter.delete(key);
52
62
  },
53
63
  clear: () => {
54
- if (typeof window === "undefined") return;
64
+ if (IS_NOT_BROWSER) return;
55
65
  adapter.clear();
56
66
  }
57
67
  };
58
68
  }
59
69
  case "indexedDb": {
60
70
  const keyPrefix = store.key + ":";
71
+ const isValid2 = store.isValid;
61
72
  return {
62
73
  set: async (key, value) => {
63
- if (typeof window === "undefined") return;
74
+ if (IS_NOT_BROWSER) return;
64
75
  await import_kvStore.kvStore.set(keyPrefix + key, value, expiryMs);
65
76
  },
66
77
  get: async (key) => {
67
- if (typeof window === "undefined") return void 0;
68
- return await import_kvStore.kvStore.get(keyPrefix + key);
78
+ if (IS_NOT_BROWSER) return void 0;
79
+ const fullKey = keyPrefix + key;
80
+ const value = await import_kvStore.kvStore.get(fullKey);
81
+ if (isValid2 && !isValid2(value)) {
82
+ console.warn(
83
+ `indexedDb adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
84
+ );
85
+ await import_kvStore.kvStore.delete(fullKey);
86
+ return void 0;
87
+ }
88
+ return value;
69
89
  },
70
90
  delete: async (key) => {
71
- if (typeof window === "undefined") return;
91
+ if (IS_NOT_BROWSER) return;
72
92
  await import_kvStore.kvStore.delete(keyPrefix + key);
73
93
  },
74
94
  clear: async () => {
75
- if (typeof window === "undefined") return;
95
+ if (IS_NOT_BROWSER) return;
76
96
  await import_kvStore.kvStore.clear();
77
97
  }
78
98
  };
package/storage.d.mts CHANGED
@@ -2,11 +2,18 @@ import { StorageAdapter } from '@choksheak/ts-utils/storageAdapter';
2
2
 
3
3
  /** List of all available pre-built persistence options. */
4
4
  type PersistTo = "localStorage" | "indexedDb";
5
+ /** Options to configure a pre-built store adapter. */
5
6
  type StoreOptions = {
7
+ /** Choose which pre-built store adapter to use. */
6
8
  persistTo: PersistTo;
9
+ /** Storage key to act as a namespace to store the data. */
7
10
  key: string;
11
+ /** How long to wait before the value is considered expired. */
8
12
  expiryMs?: number;
13
+ /** Returns true if the given value supposed to be of type T is valid. */
14
+ isValid?: (u: unknown) => boolean;
9
15
  };
16
+ /** Either use a pre-built store, or a custom one you provide. */
10
17
  type StorageOptions<T> = {
11
18
  /**
12
19
  * You can either specify the storage adapter directly to use a custom
package/storage.d.ts CHANGED
@@ -1,11 +1,18 @@
1
1
  import { StorageAdapter } from "@choksheak/ts-utils/storageAdapter";
2
2
  /** List of all available pre-built persistence options. */
3
3
  export type PersistTo = "localStorage" | "indexedDb";
4
+ /** Options to configure a pre-built store adapter. */
4
5
  export type StoreOptions = {
6
+ /** Choose which pre-built store adapter to use. */
5
7
  persistTo: PersistTo;
8
+ /** Storage key to act as a namespace to store the data. */
6
9
  key: string;
10
+ /** How long to wait before the value is considered expired. */
7
11
  expiryMs?: number;
12
+ /** Returns true if the given value supposed to be of type T is valid. */
13
+ isValid?: (u: unknown) => boolean;
8
14
  };
15
+ /** Either use a pre-built store, or a custom one you provide. */
9
16
  export type StorageOptions<T> = {
10
17
  /**
11
18
  * You can either specify the storage adapter directly to use a custom
package/storage.min.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var a=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var w=(i,r)=>{for(var e in r)a(i,e,{get:r[e],enumerable:!0})},g=(i,r,e,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let t of u(r))!y.call(i,t)&&t!==e&&a(i,t,{get:()=>r[t],enumerable:!(o=p(r,t))||o.enumerable});return i};var c=i=>g(a({},"__esModule",{value:!0}),i);var T={};w(T,{getStorageAdapter:()=>l});module.exports=c(T);var s=require("@choksheak/ts-utils/kvStore"),f=require("@choksheak/ts-utils/localStore");function l(i,r){let{store:e}=i??{};if(!e)return null;if("persistTo"in e&&typeof e.persistTo=="string"&&"key"in e&&typeof e.key=="string"){let o=e.expiryMs||r;switch(e.persistTo){case"localStorage":{let t=new f.LocalStore(e.key,{defaultExpiryMs:o});return{set:(n,d)=>{typeof window>"u"||t.set(n,d)},get:n=>{if(!(typeof window>"u"))return t.get(n)},delete:n=>{typeof window>"u"||t.delete(n)},clear:()=>{typeof window>"u"||t.clear()}}}case"indexedDb":{let t=e.key+":";return{set:async(n,d)=>{typeof window>"u"||await s.kvStore.set(t+n,d,o)},get:async n=>{if(!(typeof window>"u"))return await s.kvStore.get(t+n)},delete:async n=>{typeof window>"u"||await s.kvStore.delete(t+n)},clear:async()=>{typeof window>"u"||await s.kvStore.clear()}}}default:throw e.persistTo,new Error(`Unknown store.adapter ${e.persistTo}`)}}return e}0&&(module.exports={getStorageAdapter});
1
+ "use strict";var p=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var S=(n,r)=>{for(var e in r)p(n,e,{get:r[e],enumerable:!0})},w=(n,r,e,u)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of T(r))!y.call(n,s)&&s!==e&&p(n,s,{get:()=>r[s],enumerable:!(u=g(r,s))||u.enumerable});return n};var v=n=>w(p({},"__esModule",{value:!0}),n);var x={};S(x,{getStorageAdapter:()=>m});module.exports=v(x);var d=require("@choksheak/ts-utils/kvStore"),c=require("@choksheak/ts-utils/localStore"),a=typeof window>"u";function m(n,r){let{store:e}=n??{};if(!e)return null;if("persistTo"in e&&typeof e.persistTo=="string"&&"key"in e&&typeof e.key=="string"){let u=e.expiryMs||r,s=e.isValid;switch(e.persistTo){case"localStorage":{let o=new c.LocalStore(e.key,{defaultExpiryMs:u});return{set:(i,t)=>{a||o.set(i,t)},get:i=>{if(a)return;let t=o.get(i);if(s&&!s(t)){console.warn(`localStorage adapter: Auto-discard invalid value for ${i}: ${JSON.stringify(t)}`),o.delete(i);return}return t},delete:i=>{a||o.delete(i)},clear:()=>{a||o.clear()}}}case"indexedDb":{let o=e.key+":",i=e.isValid;return{set:async(t,l)=>{a||await d.kvStore.set(o+t,l,u)},get:async t=>{if(a)return;let l=o+t,f=await d.kvStore.get(l);if(i&&!i(f)){console.warn(`indexedDb adapter: Auto-discard invalid value for ${t}: ${JSON.stringify(f)}`),await d.kvStore.delete(l);return}return f},delete:async t=>{a||await d.kvStore.delete(o+t)},clear:async()=>{a||await d.kvStore.clear()}}}default:throw e.persistTo,new Error(`Unknown store.adapter ${e.persistTo}`)}}return e}0&&(module.exports={getStorageAdapter});
2
2
  //# sourceMappingURL=storage.min.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storage.ts"],"sourcesContent":["import { kvStore } from \"@choksheak/ts-utils/kvStore\";\nimport { LocalStore } from \"@choksheak/ts-utils/localStore\";\nimport { StorageAdapter } from \"@choksheak/ts-utils/storageAdapter\";\n\n/** List of all available pre-built persistence options. */\nexport type PersistTo = \"localStorage\" | \"indexedDb\";\n\nexport type StoreOptions = {\n persistTo: PersistTo;\n\n // Storage key to act as a namespace to store the data.\n key: string;\n\n // How long to wait before the value is considered expired.\n expiryMs?: number;\n};\n\nexport type StorageOptions<T> = {\n /**\n * You can either specify the storage adapter directly to use a custom\n * store, or specify the properties to use the pre-built ones.\n */\n store?: StorageAdapter<T> | StoreOptions;\n};\n\nexport function getStorageAdapter<T>(\n options: StorageOptions<T> | undefined,\n defaultStoreExpiryMs: number,\n): StorageAdapter<T> | null {\n const { store } = options ?? {};\n\n if (!store) {\n return null;\n }\n\n if (\n \"persistTo\" in store &&\n typeof store.persistTo === \"string\" &&\n \"key\" in store &&\n typeof store.key === \"string\"\n ) {\n const expiryMs = store.expiryMs || defaultStoreExpiryMs;\n\n switch (store.persistTo) {\n case \"localStorage\": {\n const adapter = new LocalStore(store.key, {\n defaultExpiryMs: expiryMs,\n });\n\n // Handle SSR.\n return {\n set: (key: string, value: T): void => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n // Also apply the custom expiryMs on every item in this store.\n adapter.set(key, value);\n },\n get: (key: string): T | undefined => {\n if (typeof window === \"undefined\") return undefined; // handle SSR\n\n return adapter.get(key);\n },\n delete: (key: string): void => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n adapter.delete(key);\n },\n clear: (): void => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n adapter.clear();\n },\n };\n }\n\n case \"indexedDb\": {\n // Use the key prefix as a unique namespace for this store.\n const keyPrefix = store.key + \":\";\n\n // Prefix all keys using `keyPrefix` so that we can keep all data in\n // the same store in the same DB. If we keep the data in different\n // DB stores, then GC will not be able to clean up abandoned stores.\n return {\n set: async (key: string, value: T): Promise<void> => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n // Also apply the custom expiryMs on every item in this store.\n await kvStore.set(keyPrefix + key, value, expiryMs);\n },\n get: async (key: string): Promise<T | undefined> => {\n if (typeof window === \"undefined\") return undefined; // handle SSR\n\n return await kvStore.get(keyPrefix + key);\n },\n delete: async (key: string): Promise<void> => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n await kvStore.delete(keyPrefix + key);\n },\n clear: async (): Promise<void> => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n await kvStore.clear();\n },\n };\n }\n\n default:\n store.persistTo satisfies never;\n throw new Error(`Unknown store.adapter ${store.persistTo}`);\n }\n }\n\n return store as StorageAdapter<T>;\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,uBAAAE,IAAA,eAAAC,EAAAH,GAAA,IAAAI,EAAwB,uCACxBC,EAA2B,0CAwBpB,SAASH,EACdI,EACAC,EAC0B,CAC1B,GAAM,CAAE,MAAAC,CAAM,EAAIF,GAAW,CAAC,EAE9B,GAAI,CAACE,EACH,OAAO,KAGT,GACE,cAAeA,GACf,OAAOA,EAAM,WAAc,UAC3B,QAASA,GACT,OAAOA,EAAM,KAAQ,SACrB,CACA,IAAMC,EAAWD,EAAM,UAAYD,EAEnC,OAAQC,EAAM,UAAW,CACvB,IAAK,eAAgB,CACnB,IAAME,EAAU,IAAI,aAAWF,EAAM,IAAK,CACxC,gBAAiBC,CACnB,CAAC,EAGD,MAAO,CACL,IAAK,CAACE,EAAaC,IAAmB,CAChC,OAAO,OAAW,KAGtBF,EAAQ,IAAIC,EAAKC,CAAK,CACxB,EACA,IAAMD,GAA+B,CACnC,GAAI,SAAO,OAAW,KAEtB,OAAOD,EAAQ,IAAIC,CAAG,CACxB,EACA,OAASA,GAAsB,CACzB,OAAO,OAAW,KAEtBD,EAAQ,OAAOC,CAAG,CACpB,EACA,MAAO,IAAY,CACb,OAAO,OAAW,KAEtBD,EAAQ,MAAM,CAChB,CACF,CACF,CAEA,IAAK,YAAa,CAEhB,IAAMG,EAAYL,EAAM,IAAM,IAK9B,MAAO,CACL,IAAK,MAAOG,EAAaC,IAA4B,CAC/C,OAAO,OAAW,KAGtB,MAAM,UAAQ,IAAIC,EAAYF,EAAKC,EAAOH,CAAQ,CACpD,EACA,IAAK,MAAOE,GAAwC,CAClD,GAAI,SAAO,OAAW,KAEtB,OAAO,MAAM,UAAQ,IAAIE,EAAYF,CAAG,CAC1C,EACA,OAAQ,MAAOA,GAA+B,CACxC,OAAO,OAAW,KAEtB,MAAM,UAAQ,OAAOE,EAAYF,CAAG,CACtC,EACA,MAAO,SAA2B,CAC5B,OAAO,OAAW,KAEtB,MAAM,UAAQ,MAAM,CACtB,CACF,CACF,CAEA,QACE,MAAAH,EAAM,UACA,IAAI,MAAM,yBAAyBA,EAAM,SAAS,EAAE,CAC9D,CACF,CAEA,OAAOA,CACT","names":["storage_exports","__export","getStorageAdapter","__toCommonJS","import_kvStore","import_localStore","options","defaultStoreExpiryMs","store","expiryMs","adapter","key","value","keyPrefix"]}
1
+ {"version":3,"sources":["../src/storage.ts"],"sourcesContent":["import { kvStore } from \"@choksheak/ts-utils/kvStore\";\nimport { LocalStore } from \"@choksheak/ts-utils/localStore\";\nimport { StorageAdapter } from \"@choksheak/ts-utils/storageAdapter\";\n\n/** List of all available pre-built persistence options. */\nexport type PersistTo = \"localStorage\" | \"indexedDb\";\n\n/** Options to configure a pre-built store adapter. */\nexport type StoreOptions = {\n /** Choose which pre-built store adapter to use. */\n persistTo: PersistTo;\n\n /** Storage key to act as a namespace to store the data. */\n key: string;\n\n /** How long to wait before the value is considered expired. */\n expiryMs?: number;\n\n /** Returns true if the given value supposed to be of type T is valid. */\n isValid?: (u: unknown) => boolean;\n};\n\n/** Either use a pre-built store, or a custom one you provide. */\nexport type StorageOptions<T> = {\n /**\n * You can either specify the storage adapter directly to use a custom\n * store, or specify the properties to use the pre-built ones.\n */\n store?: StorageAdapter<T> | StoreOptions;\n};\n\n// In SSR (Server-Side Rendering) mode, we never want to use our storage\n// adapters for anything.\nconst IS_NOT_BROWSER = typeof window === \"undefined\";\n\nexport function getStorageAdapter<T>(\n options: StorageOptions<T> | undefined,\n defaultStoreExpiryMs: number,\n): StorageAdapter<T> | null {\n const { store } = options ?? {};\n\n if (!store) {\n return null;\n }\n\n if (\n \"persistTo\" in store &&\n typeof store.persistTo === \"string\" &&\n \"key\" in store &&\n typeof store.key === \"string\"\n ) {\n const expiryMs = store.expiryMs || defaultStoreExpiryMs;\n const isValid = store.isValid;\n\n switch (store.persistTo) {\n case \"localStorage\": {\n const adapter = new LocalStore(store.key, {\n defaultExpiryMs: expiryMs,\n });\n\n // Handle SSR.\n return {\n set: (key: string, value: T): void => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n // Also apply the custom expiryMs on every item in this store.\n adapter.set(key, value);\n },\n get: (key: string): T | undefined => {\n if (IS_NOT_BROWSER) return undefined; // handle SSR\n\n const value = adapter.get<T>(key);\n\n if (isValid && !isValid(value)) {\n console.warn(\n `localStorage adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`,\n );\n adapter.delete(key);\n return undefined;\n }\n\n return value;\n },\n delete: (key: string): void => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n adapter.delete(key);\n },\n clear: (): void => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n adapter.clear();\n },\n };\n }\n\n case \"indexedDb\": {\n // Use the key prefix as a unique namespace for this store.\n const keyPrefix = store.key + \":\";\n const isValid = store.isValid;\n\n // Prefix all keys using `keyPrefix` so that we can keep all data in\n // the same store in the same DB. If we keep the data in different\n // DB stores, then GC will not be able to clean up abandoned stores.\n return {\n set: async (key: string, value: T): Promise<void> => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n // Also apply the custom expiryMs on every item in this store.\n await kvStore.set(keyPrefix + key, value, expiryMs);\n },\n get: async (key: string): Promise<T | undefined> => {\n if (IS_NOT_BROWSER) return undefined; // handle SSR\n\n const fullKey = keyPrefix + key;\n const value = await kvStore.get<T>(fullKey);\n\n if (isValid && !isValid(value)) {\n console.warn(\n `indexedDb adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`,\n );\n await kvStore.delete(fullKey);\n return undefined;\n }\n\n return value;\n },\n delete: async (key: string): Promise<void> => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n await kvStore.delete(keyPrefix + key);\n },\n clear: async (): Promise<void> => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n await kvStore.clear();\n },\n };\n }\n\n default:\n store.persistTo satisfies never;\n throw new Error(`Unknown store.adapter ${store.persistTo}`);\n }\n }\n\n return store as StorageAdapter<T>;\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,uBAAAE,IAAA,eAAAC,EAAAH,GAAA,IAAAI,EAAwB,uCACxBC,EAA2B,0CAgCrBC,EAAiB,OAAO,OAAW,IAElC,SAASJ,EACdK,EACAC,EAC0B,CAC1B,GAAM,CAAE,MAAAC,CAAM,EAAIF,GAAW,CAAC,EAE9B,GAAI,CAACE,EACH,OAAO,KAGT,GACE,cAAeA,GACf,OAAOA,EAAM,WAAc,UAC3B,QAASA,GACT,OAAOA,EAAM,KAAQ,SACrB,CACA,IAAMC,EAAWD,EAAM,UAAYD,EAC7BG,EAAUF,EAAM,QAEtB,OAAQA,EAAM,UAAW,CACvB,IAAK,eAAgB,CACnB,IAAMG,EAAU,IAAI,aAAWH,EAAM,IAAK,CACxC,gBAAiBC,CACnB,CAAC,EAGD,MAAO,CACL,IAAK,CAACG,EAAaC,IAAmB,CAChCR,GAGJM,EAAQ,IAAIC,EAAKC,CAAK,CACxB,EACA,IAAMD,GAA+B,CACnC,GAAIP,EAAgB,OAEpB,IAAMQ,EAAQF,EAAQ,IAAOC,CAAG,EAEhC,GAAIF,GAAW,CAACA,EAAQG,CAAK,EAAG,CAC9B,QAAQ,KACN,wDAAwDD,CAAG,KAAK,KAAK,UAAUC,CAAK,CAAC,EACvF,EACAF,EAAQ,OAAOC,CAAG,EAClB,MACF,CAEA,OAAOC,CACT,EACA,OAASD,GAAsB,CACzBP,GAEJM,EAAQ,OAAOC,CAAG,CACpB,EACA,MAAO,IAAY,CACbP,GAEJM,EAAQ,MAAM,CAChB,CACF,CACF,CAEA,IAAK,YAAa,CAEhB,IAAMG,EAAYN,EAAM,IAAM,IACxBE,EAAUF,EAAM,QAKtB,MAAO,CACL,IAAK,MAAOI,EAAaC,IAA4B,CAC/CR,GAGJ,MAAM,UAAQ,IAAIS,EAAYF,EAAKC,EAAOJ,CAAQ,CACpD,EACA,IAAK,MAAOG,GAAwC,CAClD,GAAIP,EAAgB,OAEpB,IAAMU,EAAUD,EAAYF,EACtBC,EAAQ,MAAM,UAAQ,IAAOE,CAAO,EAE1C,GAAIL,GAAW,CAACA,EAAQG,CAAK,EAAG,CAC9B,QAAQ,KACN,qDAAqDD,CAAG,KAAK,KAAK,UAAUC,CAAK,CAAC,EACpF,EACA,MAAM,UAAQ,OAAOE,CAAO,EAC5B,MACF,CAEA,OAAOF,CACT,EACA,OAAQ,MAAOD,GAA+B,CACxCP,GAEJ,MAAM,UAAQ,OAAOS,EAAYF,CAAG,CACtC,EACA,MAAO,SAA2B,CAC5BP,GAEJ,MAAM,UAAQ,MAAM,CACtB,CACF,CACF,CAEA,QACE,MAAAG,EAAM,UACA,IAAI,MAAM,yBAAyBA,EAAM,SAAS,EAAE,CAC9D,CACF,CAEA,OAAOA,CACT","names":["storage_exports","__export","getStorageAdapter","__toCommonJS","import_kvStore","import_localStore","IS_NOT_BROWSER","options","defaultStoreExpiryMs","store","expiryMs","isValid","adapter","key","value","keyPrefix","fullKey"]}
package/storage.min.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{kvStore as n}from"@choksheak/ts-utils/kvStore";import{LocalStore as a}from"@choksheak/ts-utils/localStore";function u(s,d){let{store:e}=s??{};if(!e)return null;if("persistTo"in e&&typeof e.persistTo=="string"&&"key"in e&&typeof e.key=="string"){let o=e.expiryMs||d;switch(e.persistTo){case"localStorage":{let r=new a(e.key,{defaultExpiryMs:o});return{set:(t,i)=>{typeof window>"u"||r.set(t,i)},get:t=>{if(!(typeof window>"u"))return r.get(t)},delete:t=>{typeof window>"u"||r.delete(t)},clear:()=>{typeof window>"u"||r.clear()}}}case"indexedDb":{let r=e.key+":";return{set:async(t,i)=>{typeof window>"u"||await n.set(r+t,i,o)},get:async t=>{if(!(typeof window>"u"))return await n.get(r+t)},delete:async t=>{typeof window>"u"||await n.delete(r+t)},clear:async()=>{typeof window>"u"||await n.clear()}}}default:throw e.persistTo,new Error(`Unknown store.adapter ${e.persistTo}`)}}return e}export{u as getStorageAdapter};
1
+ import{kvStore as o}from"@choksheak/ts-utils/kvStore";import{LocalStore as p}from"@choksheak/ts-utils/localStore";var n=typeof window>"u";function T(l,f){let{store:e}=l??{};if(!e)return null;if("persistTo"in e&&typeof e.persistTo=="string"&&"key"in e&&typeof e.key=="string"){let d=e.expiryMs||f,u=e.isValid;switch(e.persistTo){case"localStorage":{let i=new p(e.key,{defaultExpiryMs:d});return{set:(r,t)=>{n||i.set(r,t)},get:r=>{if(n)return;let t=i.get(r);if(u&&!u(t)){console.warn(`localStorage adapter: Auto-discard invalid value for ${r}: ${JSON.stringify(t)}`),i.delete(r);return}return t},delete:r=>{n||i.delete(r)},clear:()=>{n||i.clear()}}}case"indexedDb":{let i=e.key+":",r=e.isValid;return{set:async(t,s)=>{n||await o.set(i+t,s,d)},get:async t=>{if(n)return;let s=i+t,a=await o.get(s);if(r&&!r(a)){console.warn(`indexedDb adapter: Auto-discard invalid value for ${t}: ${JSON.stringify(a)}`),await o.delete(s);return}return a},delete:async t=>{n||await o.delete(i+t)},clear:async()=>{n||await o.clear()}}}default:throw e.persistTo,new Error(`Unknown store.adapter ${e.persistTo}`)}}return e}export{T as getStorageAdapter};
2
2
  //# sourceMappingURL=storage.min.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storage.ts"],"sourcesContent":["import { kvStore } from \"@choksheak/ts-utils/kvStore\";\nimport { LocalStore } from \"@choksheak/ts-utils/localStore\";\nimport { StorageAdapter } from \"@choksheak/ts-utils/storageAdapter\";\n\n/** List of all available pre-built persistence options. */\nexport type PersistTo = \"localStorage\" | \"indexedDb\";\n\nexport type StoreOptions = {\n persistTo: PersistTo;\n\n // Storage key to act as a namespace to store the data.\n key: string;\n\n // How long to wait before the value is considered expired.\n expiryMs?: number;\n};\n\nexport type StorageOptions<T> = {\n /**\n * You can either specify the storage adapter directly to use a custom\n * store, or specify the properties to use the pre-built ones.\n */\n store?: StorageAdapter<T> | StoreOptions;\n};\n\nexport function getStorageAdapter<T>(\n options: StorageOptions<T> | undefined,\n defaultStoreExpiryMs: number,\n): StorageAdapter<T> | null {\n const { store } = options ?? {};\n\n if (!store) {\n return null;\n }\n\n if (\n \"persistTo\" in store &&\n typeof store.persistTo === \"string\" &&\n \"key\" in store &&\n typeof store.key === \"string\"\n ) {\n const expiryMs = store.expiryMs || defaultStoreExpiryMs;\n\n switch (store.persistTo) {\n case \"localStorage\": {\n const adapter = new LocalStore(store.key, {\n defaultExpiryMs: expiryMs,\n });\n\n // Handle SSR.\n return {\n set: (key: string, value: T): void => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n // Also apply the custom expiryMs on every item in this store.\n adapter.set(key, value);\n },\n get: (key: string): T | undefined => {\n if (typeof window === \"undefined\") return undefined; // handle SSR\n\n return adapter.get(key);\n },\n delete: (key: string): void => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n adapter.delete(key);\n },\n clear: (): void => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n adapter.clear();\n },\n };\n }\n\n case \"indexedDb\": {\n // Use the key prefix as a unique namespace for this store.\n const keyPrefix = store.key + \":\";\n\n // Prefix all keys using `keyPrefix` so that we can keep all data in\n // the same store in the same DB. If we keep the data in different\n // DB stores, then GC will not be able to clean up abandoned stores.\n return {\n set: async (key: string, value: T): Promise<void> => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n // Also apply the custom expiryMs on every item in this store.\n await kvStore.set(keyPrefix + key, value, expiryMs);\n },\n get: async (key: string): Promise<T | undefined> => {\n if (typeof window === \"undefined\") return undefined; // handle SSR\n\n return await kvStore.get(keyPrefix + key);\n },\n delete: async (key: string): Promise<void> => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n await kvStore.delete(keyPrefix + key);\n },\n clear: async (): Promise<void> => {\n if (typeof window === \"undefined\") return; // handle SSR\n\n await kvStore.clear();\n },\n };\n }\n\n default:\n store.persistTo satisfies never;\n throw new Error(`Unknown store.adapter ${store.persistTo}`);\n }\n }\n\n return store as StorageAdapter<T>;\n}\n"],"mappings":"AAAA,OAAS,WAAAA,MAAe,8BACxB,OAAS,cAAAC,MAAkB,iCAwBpB,SAASC,EACdC,EACAC,EAC0B,CAC1B,GAAM,CAAE,MAAAC,CAAM,EAAIF,GAAW,CAAC,EAE9B,GAAI,CAACE,EACH,OAAO,KAGT,GACE,cAAeA,GACf,OAAOA,EAAM,WAAc,UAC3B,QAASA,GACT,OAAOA,EAAM,KAAQ,SACrB,CACA,IAAMC,EAAWD,EAAM,UAAYD,EAEnC,OAAQC,EAAM,UAAW,CACvB,IAAK,eAAgB,CACnB,IAAME,EAAU,IAAIN,EAAWI,EAAM,IAAK,CACxC,gBAAiBC,CACnB,CAAC,EAGD,MAAO,CACL,IAAK,CAACE,EAAaC,IAAmB,CAChC,OAAO,OAAW,KAGtBF,EAAQ,IAAIC,EAAKC,CAAK,CACxB,EACA,IAAMD,GAA+B,CACnC,GAAI,SAAO,OAAW,KAEtB,OAAOD,EAAQ,IAAIC,CAAG,CACxB,EACA,OAASA,GAAsB,CACzB,OAAO,OAAW,KAEtBD,EAAQ,OAAOC,CAAG,CACpB,EACA,MAAO,IAAY,CACb,OAAO,OAAW,KAEtBD,EAAQ,MAAM,CAChB,CACF,CACF,CAEA,IAAK,YAAa,CAEhB,IAAMG,EAAYL,EAAM,IAAM,IAK9B,MAAO,CACL,IAAK,MAAOG,EAAaC,IAA4B,CAC/C,OAAO,OAAW,KAGtB,MAAMT,EAAQ,IAAIU,EAAYF,EAAKC,EAAOH,CAAQ,CACpD,EACA,IAAK,MAAOE,GAAwC,CAClD,GAAI,SAAO,OAAW,KAEtB,OAAO,MAAMR,EAAQ,IAAIU,EAAYF,CAAG,CAC1C,EACA,OAAQ,MAAOA,GAA+B,CACxC,OAAO,OAAW,KAEtB,MAAMR,EAAQ,OAAOU,EAAYF,CAAG,CACtC,EACA,MAAO,SAA2B,CAC5B,OAAO,OAAW,KAEtB,MAAMR,EAAQ,MAAM,CACtB,CACF,CACF,CAEA,QACE,MAAAK,EAAM,UACA,IAAI,MAAM,yBAAyBA,EAAM,SAAS,EAAE,CAC9D,CACF,CAEA,OAAOA,CACT","names":["kvStore","LocalStore","getStorageAdapter","options","defaultStoreExpiryMs","store","expiryMs","adapter","key","value","keyPrefix"]}
1
+ {"version":3,"sources":["../src/storage.ts"],"sourcesContent":["import { kvStore } from \"@choksheak/ts-utils/kvStore\";\nimport { LocalStore } from \"@choksheak/ts-utils/localStore\";\nimport { StorageAdapter } from \"@choksheak/ts-utils/storageAdapter\";\n\n/** List of all available pre-built persistence options. */\nexport type PersistTo = \"localStorage\" | \"indexedDb\";\n\n/** Options to configure a pre-built store adapter. */\nexport type StoreOptions = {\n /** Choose which pre-built store adapter to use. */\n persistTo: PersistTo;\n\n /** Storage key to act as a namespace to store the data. */\n key: string;\n\n /** How long to wait before the value is considered expired. */\n expiryMs?: number;\n\n /** Returns true if the given value supposed to be of type T is valid. */\n isValid?: (u: unknown) => boolean;\n};\n\n/** Either use a pre-built store, or a custom one you provide. */\nexport type StorageOptions<T> = {\n /**\n * You can either specify the storage adapter directly to use a custom\n * store, or specify the properties to use the pre-built ones.\n */\n store?: StorageAdapter<T> | StoreOptions;\n};\n\n// In SSR (Server-Side Rendering) mode, we never want to use our storage\n// adapters for anything.\nconst IS_NOT_BROWSER = typeof window === \"undefined\";\n\nexport function getStorageAdapter<T>(\n options: StorageOptions<T> | undefined,\n defaultStoreExpiryMs: number,\n): StorageAdapter<T> | null {\n const { store } = options ?? {};\n\n if (!store) {\n return null;\n }\n\n if (\n \"persistTo\" in store &&\n typeof store.persistTo === \"string\" &&\n \"key\" in store &&\n typeof store.key === \"string\"\n ) {\n const expiryMs = store.expiryMs || defaultStoreExpiryMs;\n const isValid = store.isValid;\n\n switch (store.persistTo) {\n case \"localStorage\": {\n const adapter = new LocalStore(store.key, {\n defaultExpiryMs: expiryMs,\n });\n\n // Handle SSR.\n return {\n set: (key: string, value: T): void => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n // Also apply the custom expiryMs on every item in this store.\n adapter.set(key, value);\n },\n get: (key: string): T | undefined => {\n if (IS_NOT_BROWSER) return undefined; // handle SSR\n\n const value = adapter.get<T>(key);\n\n if (isValid && !isValid(value)) {\n console.warn(\n `localStorage adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`,\n );\n adapter.delete(key);\n return undefined;\n }\n\n return value;\n },\n delete: (key: string): void => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n adapter.delete(key);\n },\n clear: (): void => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n adapter.clear();\n },\n };\n }\n\n case \"indexedDb\": {\n // Use the key prefix as a unique namespace for this store.\n const keyPrefix = store.key + \":\";\n const isValid = store.isValid;\n\n // Prefix all keys using `keyPrefix` so that we can keep all data in\n // the same store in the same DB. If we keep the data in different\n // DB stores, then GC will not be able to clean up abandoned stores.\n return {\n set: async (key: string, value: T): Promise<void> => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n // Also apply the custom expiryMs on every item in this store.\n await kvStore.set(keyPrefix + key, value, expiryMs);\n },\n get: async (key: string): Promise<T | undefined> => {\n if (IS_NOT_BROWSER) return undefined; // handle SSR\n\n const fullKey = keyPrefix + key;\n const value = await kvStore.get<T>(fullKey);\n\n if (isValid && !isValid(value)) {\n console.warn(\n `indexedDb adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`,\n );\n await kvStore.delete(fullKey);\n return undefined;\n }\n\n return value;\n },\n delete: async (key: string): Promise<void> => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n await kvStore.delete(keyPrefix + key);\n },\n clear: async (): Promise<void> => {\n if (IS_NOT_BROWSER) return; // handle SSR\n\n await kvStore.clear();\n },\n };\n }\n\n default:\n store.persistTo satisfies never;\n throw new Error(`Unknown store.adapter ${store.persistTo}`);\n }\n }\n\n return store as StorageAdapter<T>;\n}\n"],"mappings":"AAAA,OAAS,WAAAA,MAAe,8BACxB,OAAS,cAAAC,MAAkB,iCAgC3B,IAAMC,EAAiB,OAAO,OAAW,IAElC,SAASC,EACdC,EACAC,EAC0B,CAC1B,GAAM,CAAE,MAAAC,CAAM,EAAIF,GAAW,CAAC,EAE9B,GAAI,CAACE,EACH,OAAO,KAGT,GACE,cAAeA,GACf,OAAOA,EAAM,WAAc,UAC3B,QAASA,GACT,OAAOA,EAAM,KAAQ,SACrB,CACA,IAAMC,EAAWD,EAAM,UAAYD,EAC7BG,EAAUF,EAAM,QAEtB,OAAQA,EAAM,UAAW,CACvB,IAAK,eAAgB,CACnB,IAAMG,EAAU,IAAIR,EAAWK,EAAM,IAAK,CACxC,gBAAiBC,CACnB,CAAC,EAGD,MAAO,CACL,IAAK,CAACG,EAAaC,IAAmB,CAChCT,GAGJO,EAAQ,IAAIC,EAAKC,CAAK,CACxB,EACA,IAAMD,GAA+B,CACnC,GAAIR,EAAgB,OAEpB,IAAMS,EAAQF,EAAQ,IAAOC,CAAG,EAEhC,GAAIF,GAAW,CAACA,EAAQG,CAAK,EAAG,CAC9B,QAAQ,KACN,wDAAwDD,CAAG,KAAK,KAAK,UAAUC,CAAK,CAAC,EACvF,EACAF,EAAQ,OAAOC,CAAG,EAClB,MACF,CAEA,OAAOC,CACT,EACA,OAASD,GAAsB,CACzBR,GAEJO,EAAQ,OAAOC,CAAG,CACpB,EACA,MAAO,IAAY,CACbR,GAEJO,EAAQ,MAAM,CAChB,CACF,CACF,CAEA,IAAK,YAAa,CAEhB,IAAMG,EAAYN,EAAM,IAAM,IACxBE,EAAUF,EAAM,QAKtB,MAAO,CACL,IAAK,MAAOI,EAAaC,IAA4B,CAC/CT,GAGJ,MAAMF,EAAQ,IAAIY,EAAYF,EAAKC,EAAOJ,CAAQ,CACpD,EACA,IAAK,MAAOG,GAAwC,CAClD,GAAIR,EAAgB,OAEpB,IAAMW,EAAUD,EAAYF,EACtBC,EAAQ,MAAMX,EAAQ,IAAOa,CAAO,EAE1C,GAAIL,GAAW,CAACA,EAAQG,CAAK,EAAG,CAC9B,QAAQ,KACN,qDAAqDD,CAAG,KAAK,KAAK,UAAUC,CAAK,CAAC,EACpF,EACA,MAAMX,EAAQ,OAAOa,CAAO,EAC5B,MACF,CAEA,OAAOF,CACT,EACA,OAAQ,MAAOD,GAA+B,CACxCR,GAEJ,MAAMF,EAAQ,OAAOY,EAAYF,CAAG,CACtC,EACA,MAAO,SAA2B,CAC5BR,GAEJ,MAAMF,EAAQ,MAAM,CACtB,CACF,CACF,CAEA,QACE,MAAAM,EAAM,UACA,IAAI,MAAM,yBAAyBA,EAAM,SAAS,EAAE,CAC9D,CACF,CAEA,OAAOA,CACT","names":["kvStore","LocalStore","IS_NOT_BROWSER","getStorageAdapter","options","defaultStoreExpiryMs","store","expiryMs","isValid","adapter","key","value","keyPrefix","fullKey"]}
package/storage.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/storage.ts
2
2
  import { kvStore } from "@choksheak/ts-utils/kvStore";
3
3
  import { LocalStore } from "@choksheak/ts-utils/localStore";
4
+ var IS_NOT_BROWSER = typeof window === "undefined";
4
5
  function getStorageAdapter(options, defaultStoreExpiryMs) {
5
6
  const { store } = options ?? {};
6
7
  if (!store) {
@@ -8,6 +9,7 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
8
9
  }
9
10
  if ("persistTo" in store && typeof store.persistTo === "string" && "key" in store && typeof store.key === "string") {
10
11
  const expiryMs = store.expiryMs || defaultStoreExpiryMs;
12
+ const isValid = store.isValid;
11
13
  switch (store.persistTo) {
12
14
  case "localStorage": {
13
15
  const adapter = new LocalStore(store.key, {
@@ -15,40 +17,58 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
15
17
  });
16
18
  return {
17
19
  set: (key, value) => {
18
- if (typeof window === "undefined") return;
20
+ if (IS_NOT_BROWSER) return;
19
21
  adapter.set(key, value);
20
22
  },
21
23
  get: (key) => {
22
- if (typeof window === "undefined") return void 0;
23
- return adapter.get(key);
24
+ if (IS_NOT_BROWSER) return void 0;
25
+ const value = adapter.get(key);
26
+ if (isValid && !isValid(value)) {
27
+ console.warn(
28
+ `localStorage adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
29
+ );
30
+ adapter.delete(key);
31
+ return void 0;
32
+ }
33
+ return value;
24
34
  },
25
35
  delete: (key) => {
26
- if (typeof window === "undefined") return;
36
+ if (IS_NOT_BROWSER) return;
27
37
  adapter.delete(key);
28
38
  },
29
39
  clear: () => {
30
- if (typeof window === "undefined") return;
40
+ if (IS_NOT_BROWSER) return;
31
41
  adapter.clear();
32
42
  }
33
43
  };
34
44
  }
35
45
  case "indexedDb": {
36
46
  const keyPrefix = store.key + ":";
47
+ const isValid2 = store.isValid;
37
48
  return {
38
49
  set: async (key, value) => {
39
- if (typeof window === "undefined") return;
50
+ if (IS_NOT_BROWSER) return;
40
51
  await kvStore.set(keyPrefix + key, value, expiryMs);
41
52
  },
42
53
  get: async (key) => {
43
- if (typeof window === "undefined") return void 0;
44
- return await kvStore.get(keyPrefix + key);
54
+ if (IS_NOT_BROWSER) return void 0;
55
+ const fullKey = keyPrefix + key;
56
+ const value = await kvStore.get(fullKey);
57
+ if (isValid2 && !isValid2(value)) {
58
+ console.warn(
59
+ `indexedDb adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
60
+ );
61
+ await kvStore.delete(fullKey);
62
+ return void 0;
63
+ }
64
+ return value;
45
65
  },
46
66
  delete: async (key) => {
47
- if (typeof window === "undefined") return;
67
+ if (IS_NOT_BROWSER) return;
48
68
  await kvStore.delete(keyPrefix + key);
49
69
  },
50
70
  clear: async () => {
51
- if (typeof window === "undefined") return;
71
+ if (IS_NOT_BROWSER) return;
52
72
  await kvStore.clear();
53
73
  }
54
74
  };