pinia-react 2.1.1 → 2.1.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/dist/index.cjs +508 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +106 -0
- package/dist/index.d.ts +44 -8
- package/dist/index.js +169 -94
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Draft, Patch } from 'immer';
|
|
2
|
+
|
|
3
|
+
type StateTree = Record<string, any>;
|
|
4
|
+
type StatePath = string[];
|
|
5
|
+
type MutationType = 'action' | 'patch' | 'reset' | 'restore';
|
|
6
|
+
/** Metadata describing why a state transition was committed. */
|
|
7
|
+
interface MutationMeta {
|
|
8
|
+
type: MutationType;
|
|
9
|
+
origin?: string;
|
|
10
|
+
action?: string;
|
|
11
|
+
args?: unknown[];
|
|
12
|
+
}
|
|
13
|
+
/** A committed state transition, intended for Pinia plugins. */
|
|
14
|
+
interface MutationEvent<S extends StateTree = StateTree> {
|
|
15
|
+
storeId: string;
|
|
16
|
+
store: StoreGeneric;
|
|
17
|
+
state: S;
|
|
18
|
+
prevState: S;
|
|
19
|
+
patches: Patch[];
|
|
20
|
+
meta: MutationMeta;
|
|
21
|
+
}
|
|
22
|
+
type MutationListener = (event: MutationEvent) => void;
|
|
23
|
+
interface RestoreStateOptions {
|
|
24
|
+
type?: Extract<MutationType, 'restore' | 'reset'>;
|
|
25
|
+
origin?: string;
|
|
26
|
+
}
|
|
27
|
+
type TransformGetters<G> = {
|
|
28
|
+
[K in keyof G]: G[K] extends (...args: any[]) => infer R ? R : never;
|
|
29
|
+
};
|
|
30
|
+
type TransformActions<A> = A;
|
|
31
|
+
type SubscriptionCallback<S> = (state: S, prevState: S) => void;
|
|
32
|
+
type DeepReadonly<T> = T extends (...args: any[]) => any ? T : T extends readonly (infer U)[] ? ReadonlyArray<DeepReadonly<U>> : T extends object ? {
|
|
33
|
+
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
34
|
+
} : T;
|
|
35
|
+
interface PiniaCustomProperties<Id extends string = string, S extends StateTree = StateTree, G extends Record<string, any> = Record<string, any>, A extends Record<string, any> = Record<string, any>> {
|
|
36
|
+
}
|
|
37
|
+
interface StorePublicApi<Id extends string = string, S = StateTree> {
|
|
38
|
+
readonly $id: Id;
|
|
39
|
+
$patch: (updater: (draft: Draft<S>) => void) => void;
|
|
40
|
+
$reset: () => void;
|
|
41
|
+
$subscribe: (callback: SubscriptionCallback<S>) => () => void;
|
|
42
|
+
$state: DeepReadonly<S>;
|
|
43
|
+
}
|
|
44
|
+
type GetterContext<S, G> = Readonly<S> & TransformGetters<G>;
|
|
45
|
+
type ActionContext<S, G, A> = S & TransformGetters<G> & TransformActions<A> & StorePublicApi<string, S>;
|
|
46
|
+
type Store<Id extends string, S extends StateTree, G extends Record<string, any>, A extends Record<string, any>> = S & TransformGetters<G> & TransformActions<A> & StorePublicApi<Id, S> & PiniaCustomProperties<Id, S, G, A>;
|
|
47
|
+
type StoreGeneric = Store<string, StateTree, Record<string, any>, Record<string, any>>;
|
|
48
|
+
type GettersImplementation<S> = {
|
|
49
|
+
[K in string]: (state: S) => any;
|
|
50
|
+
};
|
|
51
|
+
/** Extension point for store-option plugins. */
|
|
52
|
+
interface DefineStoreOptionsBase<S extends StateTree, Store> {
|
|
53
|
+
}
|
|
54
|
+
interface DefineStoreOptions<S extends StateTree, G extends Record<string, any>, A extends Record<string, any>> extends DefineStoreOptionsBase<S, Store<string, S, G, A>> {
|
|
55
|
+
state: () => S;
|
|
56
|
+
getters?: G & ThisType<GetterContext<S, G>> & GettersImplementation<S>;
|
|
57
|
+
actions?: A & ThisType<ActionContext<S, G, A>>;
|
|
58
|
+
}
|
|
59
|
+
type StoreScope = {
|
|
60
|
+
currentState: StateTree;
|
|
61
|
+
listeners: Set<(state: any, prev: any, patches: Patch[]) => void>;
|
|
62
|
+
getterResultCache: Map<string, any>;
|
|
63
|
+
getterDependencies: Map<string, Set<StatePath>>;
|
|
64
|
+
subscribers: Map<string, Set<string>>;
|
|
65
|
+
createStoreProxy: (onAccess?: (path: StatePath) => void) => StoreGeneric;
|
|
66
|
+
};
|
|
67
|
+
interface Pinia {
|
|
68
|
+
state: Record<string, StateTree>;
|
|
69
|
+
use(plugin: PiniaPlugin): Pinia;
|
|
70
|
+
/** Subscribe to committed mutations from every store in this Pinia instance. */
|
|
71
|
+
onMutation(listener: MutationListener): () => void;
|
|
72
|
+
_p: PiniaPlugin[];
|
|
73
|
+
_s: Map<string, StoreGeneric>;
|
|
74
|
+
_scopes: Map<string, StoreScope>;
|
|
75
|
+
_m: Set<MutationListener>;
|
|
76
|
+
}
|
|
77
|
+
interface PiniaPlugin {
|
|
78
|
+
(context: PiniaPluginContext): Partial<PiniaCustomProperties> | void;
|
|
79
|
+
}
|
|
80
|
+
type PiniaPluginContext<Id extends string = string, S extends StateTree = StateTree, G extends Record<string, any> = Record<string, any>, A extends Record<string, any> = Record<string, any>> = {
|
|
81
|
+
id: Id;
|
|
82
|
+
store: Store<Id, S, G, A>;
|
|
83
|
+
options: DefineStoreOptions<S, G, A>;
|
|
84
|
+
pinia: Pinia;
|
|
85
|
+
/** A plugin-only, fully controlled state replacement operation. */
|
|
86
|
+
restoreState: (state: S, options?: RestoreStateOptions) => void;
|
|
87
|
+
};
|
|
88
|
+
interface StoreDefinition<Id extends string, S extends StateTree, G extends Record<string, any>, A extends Record<string, any>> {
|
|
89
|
+
useStore: () => Store<Id, S, G, A>;
|
|
90
|
+
getStore: () => Store<Id, S, G, A>;
|
|
91
|
+
}
|
|
92
|
+
type NamedStoreDefinition<Id extends string, S extends StateTree, G extends Record<string, any>, A extends Record<string, any>> = string extends Id ? {} : {
|
|
93
|
+
[K in `use${Capitalize<Id>}Store`]: () => Store<Id, S, G, A>;
|
|
94
|
+
} & {
|
|
95
|
+
[K in `get${Capitalize<Id>}Store`]: () => Store<Id, S, G, A>;
|
|
96
|
+
};
|
|
97
|
+
type StoreDefinitionWithNames<Id extends string, S extends StateTree, G extends Record<string, any>, A extends Record<string, any>> = StoreDefinition<Id, S, G, A> & NamedStoreDefinition<Id, S, G, A>;
|
|
98
|
+
|
|
99
|
+
declare function createPinia(): Pinia;
|
|
100
|
+
|
|
101
|
+
declare function setActivePinia(pinia: Pinia): void;
|
|
102
|
+
declare function getActivePinia(): Pinia;
|
|
103
|
+
|
|
104
|
+
declare function defineStore<Id extends string, S extends StateTree, G extends Record<string, any> = {}, A extends Record<string, any> = {}>(id: Id, options: DefineStoreOptions<S, G, A>): StoreDefinitionWithNames<Id, S, G, A>;
|
|
105
|
+
|
|
106
|
+
export { type DeepReadonly, type DefineStoreOptions, type DefineStoreOptionsBase, type MutationEvent, type MutationListener, type MutationMeta, type MutationType, type Pinia, type PiniaCustomProperties, type PiniaPlugin, type PiniaPluginContext, type RestoreStateOptions, type StatePath, type StateTree, type Store, type StoreDefinition, type StoreGeneric, type SubscriptionCallback, createPinia, defineStore, getActivePinia, setActivePinia };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,27 +1,57 @@
|
|
|
1
1
|
import { Draft, Patch } from 'immer';
|
|
2
2
|
|
|
3
3
|
type StateTree = Record<string, any>;
|
|
4
|
+
type StatePath = string[];
|
|
5
|
+
type MutationType = 'action' | 'patch' | 'reset' | 'restore';
|
|
6
|
+
/** Metadata describing why a state transition was committed. */
|
|
7
|
+
interface MutationMeta {
|
|
8
|
+
type: MutationType;
|
|
9
|
+
origin?: string;
|
|
10
|
+
action?: string;
|
|
11
|
+
args?: unknown[];
|
|
12
|
+
}
|
|
13
|
+
/** A committed state transition, intended for Pinia plugins. */
|
|
14
|
+
interface MutationEvent<S extends StateTree = StateTree> {
|
|
15
|
+
storeId: string;
|
|
16
|
+
store: StoreGeneric;
|
|
17
|
+
state: S;
|
|
18
|
+
prevState: S;
|
|
19
|
+
patches: Patch[];
|
|
20
|
+
meta: MutationMeta;
|
|
21
|
+
}
|
|
22
|
+
type MutationListener = (event: MutationEvent) => void;
|
|
23
|
+
interface RestoreStateOptions {
|
|
24
|
+
type?: Extract<MutationType, 'restore' | 'reset'>;
|
|
25
|
+
origin?: string;
|
|
26
|
+
}
|
|
4
27
|
type TransformGetters<G> = {
|
|
5
28
|
[K in keyof G]: G[K] extends (...args: any[]) => infer R ? R : never;
|
|
6
29
|
};
|
|
7
30
|
type TransformActions<A> = A;
|
|
8
31
|
type SubscriptionCallback<S> = (state: S, prevState: S) => void;
|
|
32
|
+
type DeepReadonly<T> = T extends (...args: any[]) => any ? T : T extends readonly (infer U)[] ? ReadonlyArray<DeepReadonly<U>> : T extends object ? {
|
|
33
|
+
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
34
|
+
} : T;
|
|
9
35
|
interface PiniaCustomProperties<Id extends string = string, S extends StateTree = StateTree, G extends Record<string, any> = Record<string, any>, A extends Record<string, any> = Record<string, any>> {
|
|
10
36
|
}
|
|
11
|
-
interface StorePublicApi<S> {
|
|
37
|
+
interface StorePublicApi<Id extends string = string, S = StateTree> {
|
|
38
|
+
readonly $id: Id;
|
|
12
39
|
$patch: (updater: (draft: Draft<S>) => void) => void;
|
|
13
40
|
$reset: () => void;
|
|
14
41
|
$subscribe: (callback: SubscriptionCallback<S>) => () => void;
|
|
15
|
-
$state: S
|
|
42
|
+
$state: DeepReadonly<S>;
|
|
16
43
|
}
|
|
17
44
|
type GetterContext<S, G> = Readonly<S> & TransformGetters<G>;
|
|
18
|
-
type ActionContext<S, G, A> = S & TransformGetters<G> & TransformActions<A> & StorePublicApi<S>;
|
|
19
|
-
type Store<Id extends string, S extends StateTree, G extends Record<string, any>, A extends Record<string, any>> = S & TransformGetters<G> & TransformActions<A> & StorePublicApi<S> & PiniaCustomProperties<Id, S, G, A>;
|
|
45
|
+
type ActionContext<S, G, A> = S & TransformGetters<G> & TransformActions<A> & StorePublicApi<string, S>;
|
|
46
|
+
type Store<Id extends string, S extends StateTree, G extends Record<string, any>, A extends Record<string, any>> = S & TransformGetters<G> & TransformActions<A> & StorePublicApi<Id, S> & PiniaCustomProperties<Id, S, G, A>;
|
|
20
47
|
type StoreGeneric = Store<string, StateTree, Record<string, any>, Record<string, any>>;
|
|
21
48
|
type GettersImplementation<S> = {
|
|
22
49
|
[K in string]: (state: S) => any;
|
|
23
50
|
};
|
|
24
|
-
|
|
51
|
+
/** Extension point for store-option plugins. */
|
|
52
|
+
interface DefineStoreOptionsBase<S extends StateTree, Store> {
|
|
53
|
+
}
|
|
54
|
+
interface DefineStoreOptions<S extends StateTree, G extends Record<string, any>, A extends Record<string, any>> extends DefineStoreOptionsBase<S, Store<string, S, G, A>> {
|
|
25
55
|
state: () => S;
|
|
26
56
|
getters?: G & ThisType<GetterContext<S, G>> & GettersImplementation<S>;
|
|
27
57
|
actions?: A & ThisType<ActionContext<S, G, A>>;
|
|
@@ -30,16 +60,19 @@ type StoreScope = {
|
|
|
30
60
|
currentState: StateTree;
|
|
31
61
|
listeners: Set<(state: any, prev: any, patches: Patch[]) => void>;
|
|
32
62
|
getterResultCache: Map<string, any>;
|
|
33
|
-
getterDependencies: Map<string, Set<
|
|
63
|
+
getterDependencies: Map<string, Set<StatePath>>;
|
|
34
64
|
subscribers: Map<string, Set<string>>;
|
|
35
|
-
createStoreProxy: (onAccess?: (path:
|
|
65
|
+
createStoreProxy: (onAccess?: (path: StatePath) => void) => StoreGeneric;
|
|
36
66
|
};
|
|
37
67
|
interface Pinia {
|
|
38
68
|
state: Record<string, StateTree>;
|
|
39
69
|
use(plugin: PiniaPlugin): Pinia;
|
|
70
|
+
/** Subscribe to committed mutations from every store in this Pinia instance. */
|
|
71
|
+
onMutation(listener: MutationListener): () => void;
|
|
40
72
|
_p: PiniaPlugin[];
|
|
41
73
|
_s: Map<string, StoreGeneric>;
|
|
42
74
|
_scopes: Map<string, StoreScope>;
|
|
75
|
+
_m: Set<MutationListener>;
|
|
43
76
|
}
|
|
44
77
|
interface PiniaPlugin {
|
|
45
78
|
(context: PiniaPluginContext): Partial<PiniaCustomProperties> | void;
|
|
@@ -48,6 +81,9 @@ type PiniaPluginContext<Id extends string = string, S extends StateTree = StateT
|
|
|
48
81
|
id: Id;
|
|
49
82
|
store: Store<Id, S, G, A>;
|
|
50
83
|
options: DefineStoreOptions<S, G, A>;
|
|
84
|
+
pinia: Pinia;
|
|
85
|
+
/** A plugin-only, fully controlled state replacement operation. */
|
|
86
|
+
restoreState: (state: S, options?: RestoreStateOptions) => void;
|
|
51
87
|
};
|
|
52
88
|
interface StoreDefinition<Id extends string, S extends StateTree, G extends Record<string, any>, A extends Record<string, any>> {
|
|
53
89
|
useStore: () => Store<Id, S, G, A>;
|
|
@@ -67,4 +103,4 @@ declare function getActivePinia(): Pinia;
|
|
|
67
103
|
|
|
68
104
|
declare function defineStore<Id extends string, S extends StateTree, G extends Record<string, any> = {}, A extends Record<string, any> = {}>(id: Id, options: DefineStoreOptions<S, G, A>): StoreDefinitionWithNames<Id, S, G, A>;
|
|
69
105
|
|
|
70
|
-
export { type DefineStoreOptions, type Pinia, type PiniaCustomProperties, type PiniaPlugin, type PiniaPluginContext, type StateTree, type Store, type StoreDefinition, type StoreGeneric, type SubscriptionCallback, createPinia, defineStore, getActivePinia, setActivePinia };
|
|
106
|
+
export { type DeepReadonly, type DefineStoreOptions, type DefineStoreOptionsBase, type MutationEvent, type MutationListener, type MutationMeta, type MutationType, type Pinia, type PiniaCustomProperties, type PiniaPlugin, type PiniaPluginContext, type RestoreStateOptions, type StatePath, type StateTree, type Store, type StoreDefinition, type StoreGeneric, type SubscriptionCallback, createPinia, defineStore, getActivePinia, setActivePinia };
|
package/dist/index.js
CHANGED
|
@@ -18,14 +18,25 @@ function createPinia() {
|
|
|
18
18
|
const _p = [];
|
|
19
19
|
const _s = /* @__PURE__ */ new Map();
|
|
20
20
|
const _scopes = /* @__PURE__ */ new Map();
|
|
21
|
+
const _m = /* @__PURE__ */ new Set();
|
|
21
22
|
const pinia = {
|
|
22
23
|
use(plugin) {
|
|
24
|
+
if (_s.size > 0) {
|
|
25
|
+
console.warn(
|
|
26
|
+
"[pinia-react] A plugin was registered after stores were created. The new plugin will not be applied to the stores that already exist."
|
|
27
|
+
);
|
|
28
|
+
}
|
|
23
29
|
_p.push(plugin);
|
|
24
30
|
return this;
|
|
25
31
|
},
|
|
32
|
+
onMutation(listener) {
|
|
33
|
+
_m.add(listener);
|
|
34
|
+
return () => _m.delete(listener);
|
|
35
|
+
},
|
|
26
36
|
_p,
|
|
27
37
|
_s,
|
|
28
38
|
_scopes,
|
|
39
|
+
_m,
|
|
29
40
|
state
|
|
30
41
|
};
|
|
31
42
|
setActivePinia(pinia);
|
|
@@ -33,15 +44,16 @@ function createPinia() {
|
|
|
33
44
|
}
|
|
34
45
|
|
|
35
46
|
// src/store.ts
|
|
36
|
-
import { enablePatches,
|
|
47
|
+
import { enablePatches, Immer } from "immer";
|
|
37
48
|
import { useCallback, useRef, useSyncExternalStore } from "react";
|
|
38
49
|
enablePatches();
|
|
39
|
-
|
|
50
|
+
var immer = new Immer({ autoFreeze: false });
|
|
40
51
|
var activeListenerId = null;
|
|
41
52
|
var activeGetterKey = null;
|
|
53
|
+
var storeDefinitionByPinia = /* @__PURE__ */ new WeakMap();
|
|
42
54
|
function isAffected(patches, trackedPaths) {
|
|
43
55
|
if (trackedPaths.size === 0) return false;
|
|
44
|
-
const tracked = Array.from(trackedPaths)
|
|
56
|
+
const tracked = Array.from(trackedPaths);
|
|
45
57
|
for (const patch of patches) {
|
|
46
58
|
const patchPath = patch.path.map(String);
|
|
47
59
|
for (const trackedPath of tracked) {
|
|
@@ -58,8 +70,23 @@ function isAffected(patches, trackedPaths) {
|
|
|
58
70
|
}
|
|
59
71
|
return false;
|
|
60
72
|
}
|
|
73
|
+
function isPlainObjectOrArray(value) {
|
|
74
|
+
if (Array.isArray(value)) return true;
|
|
75
|
+
if (value === null || typeof value !== "object") return false;
|
|
76
|
+
const proto = Object.getPrototypeOf(value);
|
|
77
|
+
return proto === Object.prototype || proto === null;
|
|
78
|
+
}
|
|
61
79
|
function defineStore(id, options) {
|
|
62
80
|
const getters = options.getters || {};
|
|
81
|
+
function ensureStoreInstance(pinia) {
|
|
82
|
+
if (pinia._s.has(id)) {
|
|
83
|
+
if (storeDefinitionByPinia.get(pinia)?.get(id) === options) return;
|
|
84
|
+
console.warn(
|
|
85
|
+
`[pinia-react] Duplicate store id "${id}" detected. The new definition replaces the previous store with the same id.`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
createStoreInstance();
|
|
89
|
+
}
|
|
63
90
|
function resolveGetterDependencies(getterName, getterDepsMap, visited = /* @__PURE__ */ new Set()) {
|
|
64
91
|
if (visited.has(getterName)) {
|
|
65
92
|
console.warn(`[pinia-react] Circular dependency in getters detected involving: ${getterName}`);
|
|
@@ -70,8 +97,9 @@ function defineStore(id, options) {
|
|
|
70
97
|
const directDeps = getterDepsMap.get(getterName);
|
|
71
98
|
if (!directDeps) return finalDeps;
|
|
72
99
|
for (const dep of directDeps) {
|
|
73
|
-
|
|
74
|
-
|
|
100
|
+
const getterKey = dep.length === 1 ? dep[0] : void 0;
|
|
101
|
+
if (getterKey && getterKey in getters) {
|
|
102
|
+
const nestedDeps = resolveGetterDependencies(getterKey, getterDepsMap, visited);
|
|
75
103
|
nestedDeps.forEach((d) => finalDeps.add(d));
|
|
76
104
|
} else {
|
|
77
105
|
finalDeps.add(dep);
|
|
@@ -83,8 +111,6 @@ function defineStore(id, options) {
|
|
|
83
111
|
const pinia = getActivePinia();
|
|
84
112
|
const initialState = options.state();
|
|
85
113
|
let storePublicApi;
|
|
86
|
-
let devTools;
|
|
87
|
-
let isTimeTraveling = false;
|
|
88
114
|
const localScope = {
|
|
89
115
|
currentState: initialState,
|
|
90
116
|
listeners: /* @__PURE__ */ new Set(),
|
|
@@ -94,6 +120,12 @@ function defineStore(id, options) {
|
|
|
94
120
|
createStoreProxy: (_onAccess) => storePublicApi
|
|
95
121
|
};
|
|
96
122
|
pinia._scopes.set(id, localScope);
|
|
123
|
+
let definitionsById = storeDefinitionByPinia.get(pinia);
|
|
124
|
+
if (!definitionsById) {
|
|
125
|
+
definitionsById = /* @__PURE__ */ new Map();
|
|
126
|
+
storeDefinitionByPinia.set(pinia, definitionsById);
|
|
127
|
+
}
|
|
128
|
+
definitionsById.set(id, options);
|
|
97
129
|
const isGetterComputing = /* @__PURE__ */ new Set();
|
|
98
130
|
const emit = (nextState, oldState, patches) => {
|
|
99
131
|
localScope.listeners.forEach((fn) => fn(nextState, oldState, patches));
|
|
@@ -116,28 +148,42 @@ function defineStore(id, options) {
|
|
|
116
148
|
}
|
|
117
149
|
});
|
|
118
150
|
};
|
|
119
|
-
const internalPatch = (updater,
|
|
120
|
-
if (isTimeTraveling) return;
|
|
151
|
+
const internalPatch = (updater, meta) => {
|
|
121
152
|
const oldState = localScope.currentState;
|
|
122
153
|
let patches = [];
|
|
123
|
-
const nextState = produce(oldState, updater, (p) => {
|
|
154
|
+
const nextState = immer.produce(oldState, updater, (p) => {
|
|
124
155
|
patches = p;
|
|
125
156
|
});
|
|
126
|
-
if (patches.length > 0 ||
|
|
157
|
+
if (patches.length > 0 || meta.type === "reset") {
|
|
127
158
|
localScope.currentState = nextState;
|
|
128
159
|
pinia.state[id] = nextState;
|
|
129
|
-
if (devTools) {
|
|
130
|
-
devTools.send({ type: actionName, payload: patches }, nextState);
|
|
131
|
-
}
|
|
132
160
|
emit(nextState, oldState, patches);
|
|
161
|
+
const event = {
|
|
162
|
+
storeId: id,
|
|
163
|
+
store: storePublicApi,
|
|
164
|
+
state: nextState,
|
|
165
|
+
prevState: oldState,
|
|
166
|
+
patches,
|
|
167
|
+
meta
|
|
168
|
+
};
|
|
169
|
+
pinia._m.forEach((listener) => listener(event));
|
|
133
170
|
}
|
|
134
171
|
};
|
|
135
172
|
const $patch = (updater) => {
|
|
136
|
-
internalPatch(
|
|
137
|
-
|
|
138
|
-
|
|
173
|
+
internalPatch(
|
|
174
|
+
(draft) => {
|
|
175
|
+
updater(draft);
|
|
176
|
+
},
|
|
177
|
+
{ type: "patch" }
|
|
178
|
+
);
|
|
179
|
+
};
|
|
180
|
+
const $reset = () => internalPatch(() => options.state(), { type: "reset" });
|
|
181
|
+
const restoreState = (state, options2 = {}) => {
|
|
182
|
+
internalPatch(() => state, {
|
|
183
|
+
type: options2.type ?? "restore",
|
|
184
|
+
origin: options2.origin
|
|
185
|
+
});
|
|
139
186
|
};
|
|
140
|
-
const $reset = () => internalPatch(() => options.state(), "@reset", true);
|
|
141
187
|
const $subscribe = (callback) => {
|
|
142
188
|
const listener = (state, prev) => callback(state, prev);
|
|
143
189
|
localScope.listeners.add(listener);
|
|
@@ -155,37 +201,92 @@ function defineStore(id, options) {
|
|
|
155
201
|
const originalActions = options.actions || {};
|
|
156
202
|
const wrappedActions = {};
|
|
157
203
|
const proxyTarget = {};
|
|
204
|
+
const readonlyStateProxyCache = /* @__PURE__ */ new WeakMap();
|
|
205
|
+
const readonlyWarning = () => {
|
|
206
|
+
console.warn(`[${id}] Store is read-only. Use actions for mutations.`);
|
|
207
|
+
throw new TypeError(`[${id}] Store is read-only. Use actions for mutations.`);
|
|
208
|
+
};
|
|
209
|
+
const createReadonlyStateProxy = (stateTarget) => {
|
|
210
|
+
const cached = readonlyStateProxyCache.get(stateTarget);
|
|
211
|
+
if (cached) return cached;
|
|
212
|
+
const proxy = new Proxy(stateTarget, {
|
|
213
|
+
get(obj, key) {
|
|
214
|
+
const value = Reflect.get(obj, key);
|
|
215
|
+
if (isPlainObjectOrArray(value)) return createReadonlyStateProxy(value);
|
|
216
|
+
return value;
|
|
217
|
+
},
|
|
218
|
+
set: readonlyWarning,
|
|
219
|
+
deleteProperty: readonlyWarning
|
|
220
|
+
});
|
|
221
|
+
readonlyStateProxyCache.set(stateTarget, proxy);
|
|
222
|
+
return proxy;
|
|
223
|
+
};
|
|
224
|
+
const getAtPath = (path) => {
|
|
225
|
+
let value = localScope.currentState;
|
|
226
|
+
for (const key of path) value = value[key];
|
|
227
|
+
return value;
|
|
228
|
+
};
|
|
229
|
+
const setAtPath = (draft, path, value) => {
|
|
230
|
+
let target = draft;
|
|
231
|
+
for (let i = 0; i < path.length - 1; i++) target = target[path[i]];
|
|
232
|
+
target[path[path.length - 1]] = value;
|
|
233
|
+
};
|
|
234
|
+
const deleteAtPath = (draft, path) => {
|
|
235
|
+
let target = draft;
|
|
236
|
+
for (let i = 0; i < path.length - 1; i++) target = target[path[i]];
|
|
237
|
+
delete target[path[path.length - 1]];
|
|
238
|
+
};
|
|
239
|
+
const createActionStateProxy = (path, meta) => {
|
|
240
|
+
return new Proxy(Array.isArray(getAtPath(path)) ? [] : {}, {
|
|
241
|
+
get(_target, key, receiver) {
|
|
242
|
+
const current = getAtPath(path);
|
|
243
|
+
const value = Reflect.get(current, key, receiver);
|
|
244
|
+
if (typeof key === "symbol" || !isPlainObjectOrArray(value)) return value;
|
|
245
|
+
return createActionStateProxy([...path, String(key)], meta);
|
|
246
|
+
},
|
|
247
|
+
set(_target, key, value) {
|
|
248
|
+
internalPatch((draft) => setAtPath(draft, [...path, String(key)], value), meta);
|
|
249
|
+
return true;
|
|
250
|
+
},
|
|
251
|
+
deleteProperty(_target, key) {
|
|
252
|
+
internalPatch((draft) => deleteAtPath(draft, [...path, String(key)]), meta);
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
};
|
|
158
257
|
function createStoreProxy(onAccess) {
|
|
159
|
-
const
|
|
160
|
-
console.warn(`[${id}] Store is read-only. Use actions for mutations.`);
|
|
161
|
-
return false;
|
|
162
|
-
};
|
|
163
|
-
const createStateProxy = (stateTarget, path, onDeepAccess) => {
|
|
258
|
+
const createStateProxy = (stateTarget, path, onDeepAccess, trackObjectAccess = false) => {
|
|
164
259
|
return new Proxy(stateTarget, {
|
|
165
260
|
get(obj, key) {
|
|
166
261
|
if (typeof key === "symbol") return Reflect.get(obj, key);
|
|
167
262
|
const currentPath = [...path, String(key)];
|
|
168
263
|
const value = Reflect.get(obj, key);
|
|
169
|
-
if (
|
|
170
|
-
|
|
264
|
+
if (isPlainObjectOrArray(value)) {
|
|
265
|
+
if (trackObjectAccess) onDeepAccess?.(currentPath);
|
|
266
|
+
return createStateProxy(value, currentPath, onDeepAccess, trackObjectAccess);
|
|
171
267
|
}
|
|
172
268
|
onDeepAccess?.(currentPath);
|
|
173
269
|
return value;
|
|
174
270
|
},
|
|
175
|
-
set: readonlyWarning
|
|
271
|
+
set: readonlyWarning,
|
|
272
|
+
deleteProperty: readonlyWarning
|
|
176
273
|
});
|
|
177
274
|
};
|
|
178
275
|
return new Proxy(proxyTarget, {
|
|
179
276
|
get(_target, key, receiver) {
|
|
180
277
|
const strKey = String(key);
|
|
181
|
-
if (strKey === "$state")
|
|
278
|
+
if (strKey === "$state") {
|
|
279
|
+
onAccess?.(["$state"]);
|
|
280
|
+
return createReadonlyStateProxy(localScope.currentState);
|
|
281
|
+
}
|
|
282
|
+
if (strKey === "$id") return id;
|
|
182
283
|
if (strKey === "$patch") return $patch;
|
|
183
284
|
if (strKey === "$reset") return $reset;
|
|
184
285
|
if (strKey === "$subscribe") return $subscribe;
|
|
185
286
|
const state = localScope.currentState;
|
|
186
287
|
if (strKey in state) {
|
|
187
288
|
const value = state[strKey];
|
|
188
|
-
if (
|
|
289
|
+
if (isPlainObjectOrArray(value)) {
|
|
189
290
|
return createStateProxy(value, [strKey], onAccess);
|
|
190
291
|
}
|
|
191
292
|
onAccess?.([strKey]);
|
|
@@ -206,10 +307,10 @@ function defineStore(id, options) {
|
|
|
206
307
|
activeGetterKey = strKey;
|
|
207
308
|
try {
|
|
208
309
|
const onGetterAccess = (path) => {
|
|
209
|
-
dependencies.add(path
|
|
310
|
+
dependencies.add(path);
|
|
210
311
|
};
|
|
211
312
|
const trackingProxyForThis = createStoreProxy(onGetterAccess);
|
|
212
|
-
const trackingStateProxy = createStateProxy(state, [], onGetterAccess);
|
|
313
|
+
const trackingStateProxy = createStateProxy(state, [], onGetterAccess, true);
|
|
213
314
|
const result = getters[strKey].call(trackingProxyForThis, trackingStateProxy);
|
|
214
315
|
localScope.getterDependencies.set(strKey, dependencies);
|
|
215
316
|
localScope.getterResultCache.set(strKey, result);
|
|
@@ -231,6 +332,7 @@ function defineStore(id, options) {
|
|
|
231
332
|
console.warn(`[${id}] Do not replace "$state" directly. Use "$patch()" to replace the whole state.`);
|
|
232
333
|
return false;
|
|
233
334
|
}
|
|
335
|
+
if (strKey === "$id") return readonlyWarning();
|
|
234
336
|
if (strKey in localScope.currentState || strKey in getters || strKey in wrappedActions) {
|
|
235
337
|
return readonlyWarning();
|
|
236
338
|
}
|
|
@@ -243,92 +345,63 @@ function defineStore(id, options) {
|
|
|
243
345
|
const originalAction = originalActions[actionName];
|
|
244
346
|
wrappedActions[actionName] = (...args) => {
|
|
245
347
|
let returnValue;
|
|
348
|
+
let draftActive = true;
|
|
349
|
+
const meta = { type: "action", action: actionName, args };
|
|
246
350
|
const recipe = (draft) => {
|
|
247
351
|
const actionContextProxy = new Proxy({}, {
|
|
248
352
|
get(_, key) {
|
|
249
353
|
const strKey = String(key);
|
|
250
|
-
if (Reflect.has(draft, strKey)) return draft[strKey];
|
|
354
|
+
if (draftActive && Reflect.has(draft, strKey)) return draft[strKey];
|
|
355
|
+
if (!draftActive && Reflect.has(localScope.currentState, strKey)) {
|
|
356
|
+
const value = localScope.currentState[strKey];
|
|
357
|
+
if (value !== null && typeof value === "object") return createActionStateProxy([strKey], meta);
|
|
358
|
+
return value;
|
|
359
|
+
}
|
|
251
360
|
if (strKey in getters) {
|
|
252
|
-
return getters[strKey].call(actionContextProxy, draft);
|
|
361
|
+
if (draftActive) return getters[strKey].call(actionContextProxy, draft);
|
|
362
|
+
return Reflect.get(storePublicApi, key, storePublicApi);
|
|
253
363
|
}
|
|
254
364
|
return Reflect.get(storePublicApi, key, storePublicApi);
|
|
255
365
|
},
|
|
256
366
|
set(_, key, value) {
|
|
257
|
-
;
|
|
258
|
-
draft[
|
|
367
|
+
const path = [String(key)];
|
|
368
|
+
if (draftActive) draft[path[0]] = value;
|
|
369
|
+
else internalPatch((currentDraft) => setAtPath(currentDraft, path, value), meta);
|
|
370
|
+
return true;
|
|
371
|
+
},
|
|
372
|
+
deleteProperty(_, key) {
|
|
373
|
+
const path = [String(key)];
|
|
374
|
+
if (draftActive) delete draft[path[0]];
|
|
375
|
+
else internalPatch((currentDraft) => deleteAtPath(currentDraft, path), meta);
|
|
259
376
|
return true;
|
|
260
377
|
}
|
|
261
378
|
});
|
|
262
379
|
returnValue = originalAction.apply(actionContextProxy, args);
|
|
263
380
|
};
|
|
264
|
-
internalPatch(recipe,
|
|
381
|
+
internalPatch(recipe, meta);
|
|
382
|
+
draftActive = false;
|
|
265
383
|
return returnValue;
|
|
266
384
|
};
|
|
267
385
|
});
|
|
268
386
|
localScope.createStoreProxy = createStoreProxy;
|
|
269
387
|
pinia._p.forEach((plugin) => {
|
|
270
|
-
const pluginResult = plugin({
|
|
388
|
+
const pluginResult = plugin({
|
|
389
|
+
id,
|
|
390
|
+
store: storePublicApi,
|
|
391
|
+
options,
|
|
392
|
+
pinia,
|
|
393
|
+
restoreState
|
|
394
|
+
});
|
|
271
395
|
if (pluginResult) {
|
|
272
396
|
Object.defineProperties(proxyTarget, Object.getOwnPropertyDescriptors(pluginResult));
|
|
273
397
|
}
|
|
274
398
|
});
|
|
275
399
|
pinia._s.set(id, storePublicApi);
|
|
276
|
-
if (typeof window !== "undefined" && window.__REDUX_DEVTOOLS_EXTENSION__) {
|
|
277
|
-
devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect({ name: id });
|
|
278
|
-
devTools.init(localScope.currentState);
|
|
279
|
-
devTools.subscribe((message) => {
|
|
280
|
-
if (message.type === "DISPATCH") {
|
|
281
|
-
const payloadType = message.payload?.type;
|
|
282
|
-
switch (payloadType) {
|
|
283
|
-
case "JUMP_TO_STATE":
|
|
284
|
-
case "JUMP_TO_ACTION":
|
|
285
|
-
case "IMPORT_STATE": {
|
|
286
|
-
const newState = typeof message.state === "string" ? JSON.parse(message.state) : message.state;
|
|
287
|
-
if (!newState || typeof newState !== "object") return;
|
|
288
|
-
isTimeTraveling = true;
|
|
289
|
-
const oldState = localScope.currentState;
|
|
290
|
-
localScope.currentState = newState;
|
|
291
|
-
pinia.state[id] = newState;
|
|
292
|
-
localScope.getterResultCache.clear();
|
|
293
|
-
emit(newState, oldState, []);
|
|
294
|
-
isTimeTraveling = false;
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
case "COMMIT": {
|
|
298
|
-
devTools.init(localScope.currentState);
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
|
-
case "ROLLBACK": {
|
|
302
|
-
const newState = typeof message.state === "string" ? JSON.parse(message.state) : message.state;
|
|
303
|
-
if (!newState || typeof newState !== "object") return;
|
|
304
|
-
isTimeTraveling = true;
|
|
305
|
-
const oldState = localScope.currentState;
|
|
306
|
-
localScope.currentState = newState;
|
|
307
|
-
pinia.state[id] = newState;
|
|
308
|
-
localScope.getterResultCache.clear();
|
|
309
|
-
emit(newState, oldState, []);
|
|
310
|
-
isTimeTraveling = false;
|
|
311
|
-
break;
|
|
312
|
-
}
|
|
313
|
-
case "RESET": {
|
|
314
|
-
const originalState = options.state();
|
|
315
|
-
devTools.init(originalState);
|
|
316
|
-
internalPatch(() => originalState, "@reset", true);
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
default:
|
|
320
|
-
break;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
400
|
return storePublicApi;
|
|
326
401
|
}
|
|
327
402
|
function getStore() {
|
|
328
403
|
const pinia = getActivePinia();
|
|
329
|
-
|
|
330
|
-
createStoreInstance();
|
|
331
|
-
}
|
|
404
|
+
ensureStoreInstance(pinia);
|
|
332
405
|
if (activeListenerId && activeGetterKey && activeListenerId !== id) {
|
|
333
406
|
const accessedStoreScope = pinia._scopes.get(id);
|
|
334
407
|
if (accessedStoreScope) {
|
|
@@ -344,9 +417,7 @@ function defineStore(id, options) {
|
|
|
344
417
|
}
|
|
345
418
|
function useStore() {
|
|
346
419
|
const pinia = getActivePinia();
|
|
347
|
-
|
|
348
|
-
createStoreInstance();
|
|
349
|
-
}
|
|
420
|
+
ensureStoreInstance(pinia);
|
|
350
421
|
const currentScope = pinia._scopes.get(id);
|
|
351
422
|
const trackedPaths = useRef(/* @__PURE__ */ new Set());
|
|
352
423
|
trackedPaths.current.clear();
|
|
@@ -355,8 +426,12 @@ function defineStore(id, options) {
|
|
|
355
426
|
const listener = (_state, _prevState, patches) => {
|
|
356
427
|
let shouldUpdate = false;
|
|
357
428
|
for (const path of trackedPaths.current) {
|
|
358
|
-
|
|
359
|
-
|
|
429
|
+
if (path.length === 1 && path[0] === "$state") {
|
|
430
|
+
shouldUpdate = patches.length > 0;
|
|
431
|
+
if (shouldUpdate) break;
|
|
432
|
+
}
|
|
433
|
+
const topKey = path[0];
|
|
434
|
+
if (path.length === 1 && topKey in getters) {
|
|
360
435
|
if (!currentScope.getterResultCache.has(topKey)) {
|
|
361
436
|
shouldUpdate = true;
|
|
362
437
|
break;
|
|
@@ -380,7 +455,7 @@ function defineStore(id, options) {
|
|
|
380
455
|
const getSnapshot = useCallback(() => currentScope.currentState, [currentScope]);
|
|
381
456
|
useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
382
457
|
const trackingProxy = currentScope.createStoreProxy((path) => {
|
|
383
|
-
trackedPaths.current.add(path
|
|
458
|
+
trackedPaths.current.add(path);
|
|
384
459
|
});
|
|
385
460
|
return trackingProxy;
|
|
386
461
|
}
|