// use getters in other getters
doubleCountPlusOne() {
return this.doubleCount * 2
- }
+ },
},
// optional actions
actions: {
})
```
-Note: the SSR implementation is yet to be decided on Pinia, but if you intend having SSR on your application, you should avoid using `useStore` functions at the root level of a file to make sure the correct store is retrieved for your request. Here is an example:
+Note: the SSR implementation on Pinia might change, but if you intend having SSR on your application, you should avoid using `useStore` functions at the root level of a file to make sure the correct store is retrieved for your currently running application instance. Here is an example:
-**Avoid doing this\***:
+**Avoid doing this**:
```ts
import { createRouter } from 'vue-router'
})
// In a different file...
+const pinia = createPinia()
+app.use(pinia)
router.beforeEach((to) => {
- // ✅ This will work (requires an extra param for SSR, see below)
- const main = useMainStore()
+ // ✅ This will work (requires pinia param when outside of setup on both
+ // Client and Server. See the SSR section below for more information)
+ const main = useMainStore(pinia)
if (to.meta.requiresAuth && !main.isLoggedIn) return '/login'
})
-import { defineStore, setActiveReq } from '../src'
+import { createPinia, defineStore, setActivePinia } from '../src'
describe('Actions', () => {
const useStore = () => {
// create a new store
- setActiveReq({})
+ setActivePinia(createPinia())
return defineStore({
id: 'main',
state: () => ({
expect(store.$state.nested.foo).toBe('bar')
})
- it('supports being called between requests', () => {
- const req1 = {}
- const req2 = {}
- setActiveReq(req1)
+ it('supports being called between piniauests', () => {
+ const pinia1 = createPinia()
+ const pinia2 = createPinia()
+ setActivePinia(pinia1)
const aStore = useA()
- // simulate a different request
- setActiveReq(req2)
+ // simulate a different piniauest
+ setActivePinia(pinia2)
const bStore = useB()
bStore.$state.b = 'c'
expect(bStore.$state.b).toBe('c')
})
- it('can force the req', () => {
- const req1 = {}
- const req2 = {}
- const aStore = useA(req1)
+ it('can force the pinia', () => {
+ const pinia1 = createPinia()
+ const pinia2 = createPinia()
+ const aStore = useA(pinia1)
- let bStore = useB(req2)
+ let bStore = useB(pinia2)
bStore.$state.b = 'c'
aStore.swap()
expect(aStore.$state.a).toBe('b')
// a different instance of b store was used
expect(bStore.$state.b).toBe('c')
- bStore = useB(req1)
+ bStore = useB(pinia1)
expect(bStore.$state.b).toBe('a')
})
})
-import { defineStore, setActiveReq } from '../src'
+import { createPinia, defineStore, setActivePinia } from '../src'
describe('Getters', () => {
const useStore = () => {
// create a new store
- setActiveReq({})
+ setActivePinia(createPinia())
return defineStore({
id: 'main',
state: () => ({
expect(store.upperCaseName).toBe('ED')
})
- it('supports changing between requests', () => {
- const req1 = {}
- const req2 = {}
- setActiveReq(req1)
+ it('supports changing between piniauests', () => {
+ const pinia1 = createPinia()
+ const pinia2 = createPinia()
+ setActivePinia(pinia1)
const aStore = useA()
- // simulate a different request
- setActiveReq(req2)
+ // simulate a different piniauest
+ setActivePinia(pinia2)
const bStore = useB()
bStore.b = 'c'
--- /dev/null
+import { createPinia, defineStore, setActivePinia } from '../src'
+import { mount } from '@vue/test-utils'
+import { watch, nextTick, ref } from 'vue'
+
+describe('Store Lifespan', () => {
+ function defineMyStore() {
+ return defineStore({
+ id: 'main',
+ state: () => ({
+ a: true,
+ n: 0,
+ nested: {
+ foo: 'foo',
+ a: { b: 'string' },
+ },
+ }),
+ getters: {
+ double() {
+ return this.n * 2
+ },
+ notA() {
+ return !this.a
+ },
+ },
+ })
+ }
+
+ const pinia = createPinia()
+ // let pinia: object
+
+ // const useStore = () => {
+ // // create a new store
+ // pinia = {}
+ // setActivePinia(pinia)
+ // return defineMyStore()()
+ // }
+
+ it('bug report', async () => {
+ const inComponentWatch = jest.fn()
+
+ const n = ref(0)
+
+ const wrapper = mount(
+ {
+ render: () => null,
+ setup() {
+ watch(() => n.value, inComponentWatch)
+ n.value++
+ },
+ },
+ {
+ global: {
+ plugins: [pinia],
+ },
+ }
+ )
+
+ await wrapper.unmount()
+
+ expect(inComponentWatch).toHaveBeenCalledTimes(1)
+
+ // store!.n++
+ n.value++
+ await nextTick()
+ expect(inComponentWatch).toHaveBeenCalledTimes(1)
+ })
+
+ it('state reactivity outlives component life', async () => {
+ const useStore = defineMyStore()
+ setActivePinia(createPinia())
+
+ const inComponentWatch = jest.fn()
+
+ let store: ReturnType<typeof useStore>
+
+ const n = ref(0)
+
+ const wrapper = mount(
+ {
+ render: () => null,
+ setup() {
+ store = useStore()
+ // watch(() => store.n, inComponentWatch)
+ watch(() => n.value, inComponentWatch)
+ store.n++
+ n.value++
+ },
+ },
+ {
+ global: {
+ plugins: [pinia],
+ },
+ }
+ )
+
+ await wrapper.unmount()
+
+ expect(inComponentWatch).toHaveBeenCalledTimes(1)
+
+ // store!.n++
+ n.value++
+ await nextTick()
+ expect(inComponentWatch).toHaveBeenCalledTimes(1)
+ })
+})
-import { defineStore, getRootState } from '../src'
+import { createPinia, defineStore, getRootState } from '../src'
describe('Root State', () => {
const useA = defineStore({
})
it('works with no stores', () => {
- expect(getRootState({})).toEqual({})
+ expect(getRootState(createPinia())).toEqual({})
})
it('retrieves the root state of one store', () => {
- const req = {}
- useA(req)
- expect(getRootState(req)).toEqual({
+ const pinia = createPinia()
+ useA(pinia)
+ expect(getRootState(pinia)).toEqual({
a: { a: 'a' },
})
})
- it('does not mix up different requests', () => {
- const req1 = {}
- const req2 = {}
- useA(req1)
- useB(req2)
- expect(getRootState(req1)).toEqual({
+ it('does not mix up different piniauests', () => {
+ const pinia1 = createPinia()
+ const pinia2 = createPinia()
+ useA(pinia1)
+ useB(pinia2)
+ expect(getRootState(pinia1)).toEqual({
a: { a: 'a' },
})
- expect(getRootState(req2)).toEqual({
+ expect(getRootState(pinia2)).toEqual({
b: { b: 'b' },
})
})
it('can hold multiple stores', () => {
- const req1 = {}
- useA(req1)
- useB(req1)
- expect(getRootState(req1)).toEqual({
+ const pinia1 = createPinia()
+ useA(pinia1)
+ useB(pinia1)
+ expect(getRootState(pinia1)).toEqual({
a: { a: 'a' },
b: { b: 'b' },
})
-import { defineStore, setActiveReq } from '../src'
-import { computed } from 'vue'
+import { createPinia, defineStore, setActivePinia } from '../src'
+import { computed, nextTick, watch } from 'vue'
describe('State', () => {
const useStore = () => {
// create a new store
- setActiveReq({})
+ setActivePinia(createPinia())
return defineStore({
id: 'main',
state: () => ({
store.name = 'Ed'
expect(upperCased.value).toBe('ED')
})
+
+ // it('watch', () => {
+ // setActivePinia(createPinia())
+ // defineStore({
+ // id: 'main',
+ // state: () => ({
+ // name: 'Eduardo',
+ // counter: 0,
+ // }),
+ // })()
+ // })
+
+ it('state can be watched', async () => {
+ const store = useStore()
+ const spy = jest.fn()
+ watch(() => store.name, spy)
+ expect(spy).not.toHaveBeenCalled()
+ store.name = 'Ed'
+ await nextTick()
+ expect(spy).toHaveBeenCalledTimes(1)
+ })
})
-import { defineStore, setActiveReq } from '../src'
+import { createPinia, defineStore, setActivePinia } from '../src'
describe('store.$patch', () => {
const useStore = () => {
// create a new store
- setActiveReq({})
+ setActivePinia(createPinia())
return defineStore({
id: 'main',
state: () => ({
import {
createPinia,
defineStore,
- setActiveReq,
+ setActivePinia,
setStateProvider,
+ Pinia,
} from '../src'
import { mount } from '@vue/test-utils'
-import { getCurrentInstance } from 'vue'
+import { getCurrentInstance, nextTick, watch } from 'vue'
describe('Store', () => {
- let req: object
+ let pinia: Pinia
const useStore = () => {
// create a new store
- req = {}
- setActiveReq(req)
+ pinia = createPinia()
+ setActivePinia(pinia)
return defineStore({
id: 'main',
state: () => ({
})
it('can hydrate the state', () => {
- setActiveReq({})
+ setActivePinia(createPinia())
const useStore = defineStore({
id: 'main',
state: () => ({
)
})
- it('should outlive components', () => {
- let store: ReturnType<typeof useStore> | undefined
+ it('should outlive components', async () => {
+ const pinia = createPinia()
+ const useStore = defineStore({
+ id: 'main',
+ state: () => ({ n: 0 }),
+ })
const wrapper = mount(
{
setup() {
- store = useStore()
+ const store = useStore()
return { store }
},
- template: `a: {{ store.a }}`,
+ template: `n: {{ store.n }}`,
},
{
global: {
- plugins: [createPinia()],
+ plugins: [pinia],
},
}
)
- expect(wrapper.html()).toBe('a: true')
+ expect(wrapper.html()).toBe('n: 0')
- if (!store) throw new Error('no store')
+ const store = useStore(pinia)
const spy = jest.fn()
- store.$subscribe(spy)
+ watch(() => store.n, spy)
expect(spy).toHaveBeenCalledTimes(0)
- store.a = !store.a
+ store.n++
+ await nextTick()
expect(spy).toHaveBeenCalledTimes(1)
+ expect(wrapper.html()).toBe('n: 1')
- wrapper.unmount()
- store.a = !store.a
+ await wrapper.unmount()
+ store.n++
+ await nextTick()
expect(spy).toHaveBeenCalledTimes(2)
})
- it.skip('should not break getCurrentInstance', () => {
+ it('should not break getCurrentInstance', () => {
let store: ReturnType<typeof useStore> | undefined
let i1: any = {}
-import { defineStore, setActiveReq } from '../src'
+import { createPinia, defineStore, setActivePinia } from '../src'
describe('Subscriptions', () => {
const useStore = () => {
// create a new store
- setActiveReq({})
+ setActivePinia(createPinia())
return defineStore({
id: 'main',
state: () => ({
import App from './App'
import { useStore } from './store'
import { setActiveReq } from '../../../src'
+import { createPinia } from '../../src'
// Done in setup.ts
// Vue.use(VueCompositionApi)
export function createApp() {
// create router and store instances
- setActiveReq({})
+ setActiveReq(createPinia())
const store = useStore()
store.state.counter++
// create the app instance, injecting both the router and the store
const app = new Vue({
- render: h => h(App),
+ render: (h) => h(App),
})
// expose the app, the router and the store.
} from '@vue/devtools-api'
import { App } from 'vue'
import { getRegisteredStores, registerStore } from './rootStore'
-import { GenericStore, NonNullObject } from './types'
+import { GenericStore } from './types'
function formatDisplay(display: string) {
return {
let isAlreadyInstalled: boolean | undefined
-export function addDevtools(app: App, store: GenericStore, req: NonNullObject) {
+export function addDevtools(app: App, store: GenericStore) {
registerStore(store)
setupDevtoolsPlugin(
{
export {
- setActiveReq,
+ setActivePinia,
setStateProvider,
getRootState,
createPinia,
+ Pinia,
} from './rootStore'
export { defineStore } from './store'
export { createStore } from './deprecated'
-import { App, InjectionKey, Plugin } from 'vue'
+import { App, InjectionKey, Plugin, Ref, ref, warn } from 'vue'
import { IS_CLIENT } from './env'
-import { NonNullObject, StateTree, GenericStore } from './types'
+import {
+ StateTree,
+ GenericStore,
+ StoreWithState,
+ StateDescriptor,
+} from './types'
/**
- * setActiveReq must be called to handle SSR at the top of functions like `fetch`, `setup`, `serverPrefetch` and others
+ * setActivePinia must be called to handle SSR at the top of functions like
+ * `fetch`, `setup`, `serverPrefetch` and others
*/
-export let activeReq: NonNullObject = {}
-export const setActiveReq = (req: NonNullObject | undefined) =>
- req && (activeReq = req)
+export let activePinia: Pinia | undefined
+export const setActivePinia = (pinia: Pinia | undefined) =>
+ (activePinia = pinia)
+
+export const getActivePinia = () => {
+ if (__DEV__ && !activePinia) {
+ warn(
+ `[🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?\n\n` +
+ `const pinia = createPinia()\n` +
+ `app.use(pinia)\n\n` +
+ `This will fail in production.`
+ )
+ }
-export const getActiveReq = () => activeReq
+ return activePinia!
+}
/**
* The api needs more work we must be able to use the store easily in any
* function by calling `useStore` to get the store Instance and we also need to
- * be able to reset the store instance between requests on the server
+ * be able to reset the store instance between piniauests on the server
*/
-export const storesMap = new WeakMap<NonNullObject, Map<string, GenericStore>>()
+export const storesMap = new WeakMap<
+ Pinia,
+ Map<string, [StoreWithState<string, StateTree>, StateDescriptor<StateTree>]>
+>()
/**
* A state provider allows to set how states are stored for hydration. e.g. setting a property on a context, getting a property from window
/**
* Map of initial states used for hydration
*/
-export const stateProviders = new WeakMap<NonNullObject, StateProvider>()
+export const stateProviders = new WeakMap<Pinia, StateProvider>()
export function setStateProvider(stateProvider: StateProvider) {
- stateProviders.set(getActiveReq(), stateProvider)
+ stateProviders.set(getActivePinia(), stateProvider)
}
export function getInitialState(id: string): StateTree | undefined {
- const provider = stateProviders.get(getActiveReq())
+ const provider = stateProviders.get(getActivePinia())
return provider && provider()[id]
}
/**
* Gets the root state of all active stores. This is useful when reporting an application crash by
* retrieving the problematic state and send it to your error tracking service.
- * @param req - request key
+ * @param pinia - piniauest key
*/
-export function getRootState(req: NonNullObject): Record<string, StateTree> {
- const stores = storesMap.get(req)
- if (!stores) return {}
- const rootState = {} as Record<string, StateTree>
-
- // forEach is the only one that also works on IE11
- stores.forEach((store) => {
- rootState[store.$id] = store.$state
- })
-
- return rootState
+export function getRootState(pinia: Pinia): Record<string, StateTree> {
+ return pinia.state.value
}
/**
export interface Pinia {
install: Exclude<Plugin['install'], undefined>
- store<F extends (...args: any[]) => any>(useStore: F): ReturnType<F>
+
+ /**
+ * root state
+ */
+ state: Ref<any>
}
declare module '@vue/runtime-core' {
export interface ComponentCustomProperties {
/**
- * Instantiate a store anywhere
+ * Access to the application's Pinia
*/
- $pinia: Pinia['store']
+ $pinia: Pinia
}
}
: Symbol()) as InjectionKey<Pinia>
export function createPinia(): Pinia {
+ const state = ref({})
+
const pinia: Pinia = {
install(app: App) {
app.provide(piniaSymbol, pinia)
- app.config.globalProperties.$pinia = pinia.store
+ app.config.globalProperties.$pinia = pinia
// TODO: write test
// only set the app on client
if (__BROWSER__ && IS_CLIENT) {
setClientApp(app)
}
},
- store<F extends (req?: NonNullObject) => GenericStore>(
- useStore: F
- ): ReturnType<F> {
- return useStore(pinia) as ReturnType<F>
- },
+
+ state,
}
return pinia
-import {
- ref,
- watch,
- computed,
- Ref,
- reactive,
- inject,
- getCurrentInstance,
-} from 'vue'
+import { watch, computed, Ref, inject, getCurrentInstance, reactive } from 'vue'
import {
StateTree,
StoreWithState,
StoreWithGetters,
Store,
StoreWithActions,
+ StateDescriptor,
Method,
} from './types'
import {
- getActiveReq,
- setActiveReq,
+ getActivePinia,
+ setActivePinia,
storesMap,
getInitialState,
getClientApp,
piniaSymbol,
+ Pinia,
} from './rootStore'
import { addDevtools } from './devtools'
import { IS_CLIENT } from './env'
-import { withScope } from './withScope'
function innerPatch<T extends StateTree>(
target: T,
return target
}
-function toComputed<T>(refObject: Ref<T>) {
+/**
+ * Create an object of computed properties referring to
+ *
+ * @param rootStateRef - pinia.state
+ * @param id - unique name
+ */
+function computedFromState<T, Id extends string>(
+ rootStateRef: Ref<Record<Id, T>>,
+ id: Id
+) {
// let asComputed = computed<T>()
const reactiveObject = {} as {
[k in keyof T]: Ref<T[k]>
}
- for (const key in refObject.value) {
+ const state = rootStateRef.value[id]
+ for (const key in state) {
// @ts-ignore: the key matches
reactiveObject[key] = computed({
- get: () => refObject.value[key as keyof T],
- set: (value) => (refObject.value[key as keyof T] = value),
+ get: () => rootStateRef.value[id][key as keyof T],
+ set: (value) => (rootStateRef.value[id][key as keyof T] = value),
})
}
}
/**
- * Creates a store instance
+ * Creates a store with its state object. This is meant to be augmented with getters and actions
+ *
* @param id - unique identifier of the store, like a name. eg: main, cart, user
+ * @param buildState - function to build the initial state
* @param initialState - initial state applied to the store, Must be correctly typed to infer typings
*/
-export function buildStore<
- Id extends string,
- S extends StateTree,
- G extends Record<string, Method>,
- A extends Record<string, Method>
->(
+function initStore<Id extends string, S extends StateTree>(
$id: Id,
buildState: () => S = () => ({} as S),
- getters: G = {} as G,
- actions: A = {} as A,
initialState?: S | undefined
-): Store<Id, S, G, A> {
- const state: Ref<S> = ref(initialState || buildState())
- // TODO: remove req part?
- const _r = getActiveReq()
+): [StoreWithState<Id, S>, { get: () => S; set: (newValue: S) => void }] {
+ const _p = getActivePinia()
+ _p.state.value[$id] = initialState || buildState()
+ // const state: Ref<S> = toRef(_p.state.value, $id)
let isListening = true
let subscriptions: SubscriptionCallback<S>[] = []
- watch(
- () => state.value,
- (state) => {
- if (isListening) {
- subscriptions.forEach((callback) => {
- callback({ storeName: $id, type: '🧩 in place', payload: {} }, state)
- })
- }
- },
- {
- deep: true,
- flush: 'sync',
- }
- )
-
function $patch(partialState: DeepPartial<S>): void {
isListening = false
- innerPatch(state.value, partialState)
+ innerPatch(_p.state.value[$id], partialState)
isListening = true
// because we paused the watcher, we need to manually call the subscriptions
subscriptions.forEach((callback) => {
callback(
{ storeName: $id, type: '⤵️ patch', payload: partialState },
- state.value
+ _p.state.value[$id]
)
})
}
function $subscribe(callback: SubscriptionCallback<S>) {
subscriptions.push(callback)
+
+ // watch here to link the subscription to the current active instance
+ // e.g. inside the setup of a component
+ const stopWatcher = watch(
+ () => _p.state.value[$id],
+ (state) => {
+ if (isListening) {
+ subscriptions.forEach((callback) => {
+ callback(
+ { storeName: $id, type: '🧩 in place', payload: {} },
+ state
+ )
+ })
+ }
+ },
+ {
+ deep: true,
+ flush: 'sync',
+ }
+ )
+
return () => {
const idx = subscriptions.indexOf(callback)
if (idx > -1) {
subscriptions.splice(idx, 1)
+ stopWatcher()
}
}
}
function $reset() {
subscriptions = []
- state.value = buildState()
+ _p.state.value[$id] = buildState()
}
const storeWithState: StoreWithState<Id, S> = {
$id,
- _r,
- // @ts-ignore, `reactive` unwraps this making it of type S
- $state: computed<S>({
- get: () => state.value,
- set: (newState) => {
- isListening = false
- state.value = newState
- isListening = true
- },
- }),
+ _p,
+
+ // $state is added underneath
$patch,
$subscribe,
$reset,
- }
+ } as StoreWithState<Id, S>
+
+ return [
+ storeWithState,
+ {
+ get: () => _p.state.value[$id] as S,
+ set: (newState: S) => {
+ isListening = false
+ _p.state.value[$id] = newState
+ isListening = true
+ },
+ },
+ ]
+}
+
+/**
+ * Creates a store bound to the lifespan of where the function is called. This
+ * means creating the store inside of a component's setup will bound it to the
+ * lifespan of that component while creating it outside of a component will
+ * create an ever living store
+ *
+ * @param partialStore - store with state returned by initStore
+ * @param descriptor - descriptor to setup $state property
+ * @param $id - unique name of the store
+ * @param getters - getters of the store
+ * @param actions - actions of the store
+ */
+function buildStoreToUse<
+ Id extends string,
+ S extends StateTree,
+ G extends Record<string, Method>,
+ A extends Record<string, Method>
+>(
+ partialStore: StoreWithState<Id, S>,
+ descriptor: StateDescriptor<S>,
+ $id: Id,
+ getters: G = {} as G,
+ actions: A = {} as A
+) {
+ const _p = getActivePinia()
const computedGetters: StoreWithGetters<G> = {} as StoreWithGetters<G>
for (const getterName in getters) {
computedGetters[getterName] = computed(() => {
- setActiveReq(_r)
+ setActivePinia(_p)
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return getters[getterName].call(store, store)
}) as StoreWithGetters<G>[typeof getterName]
}
- // const reactiveGetters = reactive(computedGetters)
-
const wrappedActions: StoreWithActions<A> = {} as StoreWithActions<A>
for (const actionName in actions) {
wrappedActions[actionName] = function () {
- setActiveReq(_r)
+ setActivePinia(_p)
// eslint-disable-next-line
return actions[actionName].apply(store, (arguments as unknown) as any[])
} as StoreWithActions<A>[typeof actionName]
}
const store: Store<Id, S, G, A> = reactive({
- ...storeWithState,
+ ...partialStore,
// using this means no new properties can be added as state
- ...toComputed(state),
+ ...computedFromState(_p.state, $id),
...computedGetters,
...wrappedActions,
}) as Store<Id, S, G, A>
+ // use this instead of a computed with setter to be able to create it anywhere
+ // without linking the computed lifespan to wherever the store is first
+ // created.
+ Object.defineProperty(store, '$state', descriptor)
+
return store
}
}) {
const { id, state, getters, actions } = options
- return function useStore(reqKey?: object | null): Store<Id, S, G, A> {
+ return function useStore(pinia?: Pinia | null): Store<Id, S, G, A> {
// avoid injecting if `useStore` when not possible
- reqKey = reqKey || (getCurrentInstance() && inject(piniaSymbol))
- if (reqKey) setActiveReq(reqKey)
- // TODO: worth warning on server if no reqKey as it can leak data
- const req = getActiveReq()
- let stores = storesMap.get(req)
- if (!stores) storesMap.set(req, (stores = new Map()))
-
- let store = stores.get(id) as Store<Id, S, G, A>
- if (!store) {
- stores.set(
+ pinia = pinia || (getCurrentInstance() && inject(piniaSymbol))
+ if (pinia) setActivePinia(pinia)
+ // TODO: worth warning on server if no piniaKey as it can leak data
+ pinia = getActivePinia()
+ let stores = storesMap.get(pinia)
+ if (!stores) storesMap.set(pinia, (stores = new Map()))
+
+ let storeAndDescriptor = stores.get(id) as
+ | [StoreWithState<Id, S>, StateDescriptor<S>]
+ | undefined
+ if (!storeAndDescriptor) {
+ storeAndDescriptor = initStore(id, state, getInitialState(id))
+
+ stores.set(id, storeAndDescriptor)
+
+ const store = buildStoreToUse(
+ storeAndDescriptor[0],
+ storeAndDescriptor[1],
id,
- (store = withScope(
- () =>
- buildStore(
- id,
- state,
- getters as Record<string, Method> | undefined,
- actions as Record<string, Method> | undefined,
- getInitialState(id)
- ) as Store<Id, S, G, A>
- ))
+ getters as Record<string, Method> | undefined,
+ actions as Record<string, Method> | undefined
)
if (
) {
const app = getClientApp()
if (app) {
- addDevtools(app, store, req)
+ addDevtools(app, store)
} else if (!isDevWarned && !__TEST__) {
isDevWarned = true
console.warn(
)
}
}
+
+ return store
}
- return store
+ return buildStoreToUse(
+ storeAndDescriptor[0],
+ storeAndDescriptor[1],
+ id,
+ getters as Record<string, Method> | undefined,
+ actions as Record<string, Method> | undefined
+ )
}
}
import { Ref } from 'vue'
+import { Pinia } from './rootStore'
export type StateTree = Record<string | number | symbol, any>
+/**
+ * Object descriptor for Object.defineProperty
+ */
+export interface StateDescriptor<S extends StateTree> {
+ get(): S
+ set(newValue: S): void
+}
+
export function isPlainObject(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
o: any
)
}
-export type NonNullObject = Record<any, any>
-
export interface StoreGetter<S extends StateTree, T = any> {
(state: S, getters: Record<string, Ref<any>>): T
}
$state: S
/**
- * Private property defining the request key for this store
+ * Private property defining the pinia the store is attached to.
*
* @internal
*/
- _r: NonNullObject
+ _p: Pinia
/**
* Applies a state patch to current state. Allows passing nested values
+++ /dev/null
-import { createApp } from 'vue'
-import { IS_CLIENT } from './env'
-
-export function withScope<T>(factory: () => T): T {
- if (__BROWSER__ && IS_CLIENT) {
- let store: T
- createApp({
- setup() {
- store = factory()
- return () => null
- },
- }).mount(document.createElement('div'))
- // TODO: collect apps to be unmounted when the main app is unmounted
- return store!
- } else {
- // no need to wrap with an app on SSR
- return factory()
- }
-}