@choksheak/react-utils 0.3.0 → 0.3.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.
- package/package.json +1 -1
- package/sharedQuery.cjs +71 -21
- package/sharedQuery.min.cjs +1 -1
- package/sharedQuery.min.cjs.map +1 -1
- package/sharedQuery.min.mjs +1 -1
- package/sharedQuery.min.mjs.map +1 -1
- package/sharedQuery.mjs +71 -21
- package/sharedState.cjs +71 -21
- package/sharedState.d.mts +29 -0
- package/sharedState.d.ts +29 -0
- package/sharedState.min.cjs +1 -1
- package/sharedState.min.cjs.map +1 -1
- package/sharedState.min.mjs +1 -1
- package/sharedState.min.mjs.map +1 -1
- package/sharedState.mjs +71 -21
- package/storage.cjs +30 -10
- package/storage.d.mts +7 -0
- package/storage.d.ts +7 -0
- package/storage.min.cjs +1 -1
- package/storage.min.cjs.map +1 -1
- package/storage.min.mjs +1 -1
- package/storage.min.mjs.map +1 -1
- package/storage.mjs +30 -10
package/storage.min.cjs.map
CHANGED
|
@@ -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
|
|
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
|
|
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
|
package/storage.min.mjs.map
CHANGED
|
@@ -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
|
|
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 (
|
|
20
|
+
if (IS_NOT_BROWSER) return;
|
|
19
21
|
adapter.set(key, value);
|
|
20
22
|
},
|
|
21
23
|
get: (key) => {
|
|
22
|
-
if (
|
|
23
|
-
|
|
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 (
|
|
36
|
+
if (IS_NOT_BROWSER) return;
|
|
27
37
|
adapter.delete(key);
|
|
28
38
|
},
|
|
29
39
|
clear: () => {
|
|
30
|
-
if (
|
|
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 (
|
|
50
|
+
if (IS_NOT_BROWSER) return;
|
|
40
51
|
await kvStore.set(keyPrefix + key, value, expiryMs);
|
|
41
52
|
},
|
|
42
53
|
get: async (key) => {
|
|
43
|
-
if (
|
|
44
|
-
|
|
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 (
|
|
67
|
+
if (IS_NOT_BROWSER) return;
|
|
48
68
|
await kvStore.delete(keyPrefix + key);
|
|
49
69
|
},
|
|
50
70
|
clear: async () => {
|
|
51
|
-
if (
|
|
71
|
+
if (IS_NOT_BROWSER) return;
|
|
52
72
|
await kvStore.clear();
|
|
53
73
|
}
|
|
54
74
|
};
|