Skip to content

Commit bb83178

Browse files
matthewjumpsoffbuildingsOrbisK43081j
authored
feat(useIDBKeyval): add options.serializer (#4781)
Co-authored-by: Robin <robin.kehl@singular-it.de> Co-authored-by: James Garbutt <43081j@users.noreply.github.com>
1 parent e17c567 commit bb83178

File tree

2 files changed

+87
-23
lines changed

2 files changed

+87
-23
lines changed

packages/integrations/useIDBKeyval/index.test.ts

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1+
import type { Ref } from 'vue'
12
import { get, set } from 'idb-keyval'
2-
import { beforeEach, describe, expect, it, vi } from 'vitest'
3+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
34
import { nextTick } from 'vue'
45
import { useIDBKeyval } from './index'
56

6-
const cache = {} as any
7+
const cache = new Map<string, unknown>()
78

89
vi.mock('idb-keyval', () => ({
9-
get: (key: string) => Promise.resolve(cache[key]),
10+
get: (key: string) => Promise.resolve(cache.get(key)),
1011
set: vi.fn((key: string, value: any) => new Promise((resolve, reject) => {
1112
if (value === 'error') {
1213
reject(new Error('set error'))
1314
return
1415
}
1516

16-
cache[key] = value
17+
cache.set(key, value)
1718

1819
resolve(undefined)
1920
})),
@@ -24,30 +25,39 @@ vi.mock('idb-keyval', () => ({
2425
return
2526
}
2627

27-
cache[key] = value
28+
cache.set(key, value)
2829

2930
resolve(undefined)
3031
}),
3132
del: (key: string) => {
32-
delete cache[key]
33+
cache.delete(key)
3334
},
3435
}))
3536

3637
const KEY1 = 'vue-use-idb-keyval-1'
3738
const KEY2 = 'vue-use-idb-keyval-2'
3839
const KEY3 = 'vue-use-idb-keyval-3'
3940
const KEY4 = 'vue-use-idb-keyval-4'
41+
4042
describe('useIDBKeyval', () => {
41-
beforeEach(() => {
42-
vi.clearAllMocks()
43+
let data1: Ref<{ count: number } | null>
44+
let data2: Ref<string[] | null>
45+
let data3: Ref<string | null>
46+
47+
beforeEach(async () => {
4348
console.error = vi.fn()
44-
})
4549

46-
set(KEY3, 'hello')
50+
await set(KEY3, 'hello');
4751

48-
const { data: data1 } = useIDBKeyval(KEY1, { count: 0 })
49-
const { data: data2 } = useIDBKeyval(KEY2, ['foo', 'bar'])
50-
const { data: data3 } = useIDBKeyval(KEY3, 'world', { shallow: true })
52+
({ data: data1 } = useIDBKeyval(KEY1, { count: 0 }));
53+
({ data: data2 } = useIDBKeyval(KEY2, ['foo', 'bar']));
54+
({ data: data3 } = useIDBKeyval(KEY3, 'world', { shallow: true }))
55+
})
56+
57+
afterEach(() => {
58+
vi.clearAllMocks()
59+
cache.clear()
60+
})
5161

5262
it('get/set', async () => {
5363
expect(data1.value).toEqual({ count: 0 })
@@ -60,8 +70,8 @@ describe('useIDBKeyval', () => {
6070
})
6171

6272
it('update', async () => {
63-
data1.value.count++
64-
data2.value.push('woo')
73+
data1.value!.count++
74+
data2.value!.push('woo')
6575
data3.value = 'world'
6676

6777
expect(await get(KEY1)).toEqual(data1.value)
@@ -109,6 +119,43 @@ describe('useIDBKeyval', () => {
109119
it('writeDefaults false', async () => {
110120
useIDBKeyval(KEY4, 'test', { writeDefaults: false })
111121

112-
expect(set).toHaveBeenCalledTimes(0)
122+
// the initial 3 IDB calls minus this one
123+
expect(set).toHaveBeenCalledTimes(3)
124+
})
125+
it('get/set with serializer', async () => {
126+
const serializer = {
127+
read(raw: unknown): string {
128+
return raw === 1 ? 'foo' : 'bar'
129+
},
130+
write(value: string): unknown {
131+
return value === 'foo' ? 1 : 0
132+
},
133+
}
134+
135+
const { data } = useIDBKeyval(KEY4, 'foo', { serializer })
136+
137+
await nextTick()
138+
139+
expect(data.value).toEqual('foo')
140+
expect(await get(KEY4)).toEqual(1)
141+
})
142+
it('get/set with serializer and existing value', async () => {
143+
const serializer = {
144+
read(raw: unknown): string {
145+
return raw === 1 ? 'foo' : 'bar'
146+
},
147+
write(value: string): unknown {
148+
return value === 'foo' ? 1 : 0
149+
},
150+
}
151+
152+
await set(KEY4, 0)
153+
154+
const { data } = useIDBKeyval(KEY4, 'foo', { serializer })
155+
156+
await nextTick()
157+
158+
expect(data.value).toEqual('bar')
159+
expect(await get(KEY4)).toEqual(0)
113160
})
114161
})

packages/integrations/useIDBKeyval/index.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import { watchPausable } from '@vueuse/core'
44
import { del, get, set, update } from 'idb-keyval'
55
import { ref as deepRef, shallowRef, toRaw, toValue } from 'vue'
66

7-
export interface UseIDBOptions extends ConfigurableFlush {
7+
interface Serializer<T> {
8+
read: (raw: unknown) => T
9+
write: (value: T) => unknown
10+
}
11+
12+
export interface UseIDBOptions<T> extends ConfigurableFlush {
813
/**
914
* Watch for deep changes
1015
*
@@ -31,6 +36,11 @@ export interface UseIDBOptions extends ConfigurableFlush {
3136
* @default true
3237
*/
3338
writeDefaults?: boolean
39+
40+
/**
41+
* Custom data serialization
42+
*/
43+
serializer?: Serializer<T>
3444
}
3545

3646
export interface UseIDBKeyvalReturn<T> {
@@ -48,7 +58,7 @@ export interface UseIDBKeyvalReturn<T> {
4858
export function useIDBKeyval<T>(
4959
key: IDBValidKey,
5060
initialValue: MaybeRefOrGetter<T>,
51-
options: UseIDBOptions = {},
61+
options: UseIDBOptions<T> = {},
5262
): UseIDBKeyvalReturn<T> {
5363
const {
5464
flush = 'pre',
@@ -58,6 +68,10 @@ export function useIDBKeyval<T>(
5868
console.error(e)
5969
},
6070
writeDefaults = true,
71+
serializer = {
72+
read: (raw: unknown) => raw as T,
73+
write: (value: T) => value,
74+
},
6175
} = options
6276

6377
const isFinished = shallowRef(false)
@@ -69,11 +83,13 @@ export function useIDBKeyval<T>(
6983
try {
7084
const rawValue = await get<T>(key)
7185
if (rawValue === undefined) {
72-
if (rawInit !== undefined && rawInit !== null && writeDefaults)
73-
await set(key, rawInit)
86+
if (rawInit !== undefined && rawInit !== null && writeDefaults) {
87+
const initValue = serializer.write(rawInit)
88+
await set(key, initValue)
89+
}
7490
}
7591
else {
76-
data.value = rawValue
92+
data.value = serializer.read(rawValue)
7793
}
7894
}
7995
catch (e) {
@@ -90,8 +106,9 @@ export function useIDBKeyval<T>(
90106
await del(key)
91107
}
92108
else {
93-
// IndexedDB does not support saving proxies, convert from proxy before saving
94-
await update(key, () => toRaw(data.value))
109+
const rawValue = toRaw(data.value)
110+
const serializedValue = serializer.write(rawValue)
111+
await update(key, () => serializedValue)
95112
}
96113
}
97114
catch (e) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy