Skip to content

Commit

Permalink
feat: custom serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
wobsoriano committed Dec 4, 2023
1 parent 9440f6b commit 2f52a8b
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 18 deletions.
6 changes: 0 additions & 6 deletions examples/vite/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { share } from 'pinia-shared-state'
import { useCounterStore } from './store'
const counterStore = useCounterStore()
onMounted(() => {
share('foo', counterStore, { initialize: true })
})
</script>

<template>
Expand Down
6 changes: 3 additions & 3 deletions examples/vite/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const useCounterStore = defineStore('counter', {
},
},

// share: {
// enable: true,
// },
share: {
enable: true,
},
})
21 changes: 17 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { toRaw } from 'vue-demi'
import type { MethodType } from 'broadcast-channel'
import { BroadcastChannel as BroadcastChannelImpl } from 'broadcast-channel'
import type { PiniaPluginContext } from 'pinia'
import { serialize, type Serializer } from './utils'

function stateHasKey(key: string, $state: PiniaPluginContext['store']['$state']) {
return Object.keys($state).includes(key)
}

interface Options {
initialize?: boolean
enable?: boolean
type?: MethodType
serializer?: Serializer
}

/**
* Adds a `share` option to your store to share state across browser tabs.
*
Expand All @@ -23,8 +30,14 @@ function stateHasKey(key: string, $state: PiniaPluginContext['store']['$state'])
* @param options.enable - Enable/disable sharing of state for all stores.
* @param options.initialize - Immediately recover the shared state from another tab.
* @param options.type - 'native', 'idb', 'localstorage', 'node'.
* @param options.serializer - Custom serializer to serialize state before broadcasting.
*/
export function PiniaSharedState({ enable = true, initialize = true, type }: { initialize?: boolean, enable?: boolean, type?: MethodType }) {
export function PiniaSharedState({
enable = true,
initialize = true,
type,
serializer,
}: Options) {
return ({ store, options }: PiniaPluginContext) => {
const isEnabled = options?.share?.enable ?? enable
const omittedKeys = options?.share?.omit ?? []
Expand All @@ -44,7 +57,7 @@ export function PiniaSharedState({ enable = true, initialize = true, type }: { i
if (newState === undefined) {
channel.postMessage({
timestamp,
state: toRaw(store.$state),
state: serialize(store.$state, serializer),
})
return
}
Expand All @@ -71,7 +84,7 @@ export function PiniaSharedState({ enable = true, initialize = true, type }: { i
timestamp = Date.now()
channel.postMessage({
timestamp,
state: toRaw(state),
state: serialize(state, serializer),
})
}
externalUpdate = false
Expand Down
11 changes: 11 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface Serializer {
serialize: (value: any) => string
deserialize: (value: string) => any
}

export function serialize(
obj: Record<string, unknown>,
serializer: Serializer = { serialize: JSON.stringify, deserialize: JSON.parse }
) {
return serializer.deserialize(serializer.serialize(obj))
}
10 changes: 5 additions & 5 deletions src/vanilla.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { toRaw } from 'vue-demi'
import type { MethodType } from 'broadcast-channel'
import { BroadcastChannel as BroadcastChannelImpl } from 'broadcast-channel'
import type { Store } from 'pinia'
import { Serializer, serialize } from './utils'

/**
* Share state across browser tabs.
Expand All @@ -21,11 +21,12 @@ import type { Store } from 'pinia'
* @param options - Share state options.
* @param options.initialize - Immediately recover the shared state from another tab.
* @param options.type - 'native', 'idb', 'localstorage', 'node'.
* @param options.serializer - Custom serializer to serialize state before broadcasting.
*/
export function share<T extends Store, K extends keyof T['$state']>(
key: K,
store: T,
{ initialize, type }: { initialize: boolean, type?: MethodType },
{ initialize, serializer, type }: { initialize: boolean, serializer?: Serializer, type?: MethodType },
): { sync: () => void, unshare: () => void } {
const channelName = `${store.$id}-${key.toString()}`

Expand All @@ -40,8 +41,7 @@ export function share<T extends Store, K extends keyof T['$state']>(
timestamp = Date.now()
channel.postMessage({
timestamp,
// @ts-expect-error: TODO
newValue: toRaw(state)[key],
newValue: serialize(state, serializer)[key],
})
}
externalUpdate = false
Expand All @@ -52,7 +52,7 @@ export function share<T extends Store, K extends keyof T['$state']>(
channel.postMessage({
timestamp,
// @ts-expect-error: TODO
newValue: toRaw(store.$state)[key],
newValue: serialize(store.$state, serializer)[key],
})
return
}
Expand Down

0 comments on commit 2f52a8b

Please sign in to comment.