@composy/store-vue 0.0.1 → 0.0.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/CHANGELOG.md +59 -0
- package/README.md +169 -154
- package/dist/index.js +151 -53
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +242 -170
- package/dist/index.min.js.map +1 -1
- package/es/.dts-output-manifest.json +1 -0
- package/es/.dts.tsbuildinfo +1 -0
- package/es/composables/defineSetupStore.d.ts +0 -1
- package/es/composables/defineSetupStore.d.ts.map +1 -1
- package/es/composables/defineSetupStore.js +1 -1
- package/es/composables/defineSetupStore.js.map +1 -1
- package/es/composables/index.d.ts +8 -9
- package/es/composables/index.d.ts.map +1 -1
- package/es/composables/index.js +1 -1
- package/es/composables/useFormStore.d.ts +0 -1
- package/es/composables/useFormStore.d.ts.map +1 -1
- package/es/composables/useFormStore.js +1 -1
- package/es/composables/useFormStore.js.map +1 -1
- package/es/composables/useOptimistic.d.ts +1 -52
- package/es/composables/useOptimistic.d.ts.map +1 -1
- package/es/composables/useOptimistic.js +35 -20
- package/es/composables/useOptimistic.js.map +1 -1
- package/es/composables/usePersist.d.ts +0 -1
- package/es/composables/usePersist.d.ts.map +1 -1
- package/es/composables/usePersist.js +16 -7
- package/es/composables/usePersist.js.map +1 -1
- package/es/composables/usePersistedState.d.ts +0 -1
- package/es/composables/usePersistedState.d.ts.map +1 -1
- package/es/composables/usePersistedState.js +16 -3
- package/es/composables/usePersistedState.js.map +1 -1
- package/es/composables/useSharedState.d.ts +0 -1
- package/es/composables/useSharedState.d.ts.map +1 -1
- package/es/composables/useSharedState.js +25 -8
- package/es/composables/useSharedState.js.map +1 -1
- package/es/composables/useStore.d.ts +0 -1
- package/es/composables/useStore.js +1 -1
- package/es/composables/useStore.js.map +1 -1
- package/es/composables/useStoreDevtools.d.ts +0 -1
- package/es/composables/useStoreDevtools.d.ts.map +1 -1
- package/es/composables/useStoreDevtools.js +1 -1
- package/es/composables/useStoreDevtools.js.map +1 -1
- package/es/composables/useStoreSnapshot.d.ts +0 -1
- package/es/composables/useStoreSnapshot.d.ts.map +1 -1
- package/es/composables/useStoreSnapshot.js +18 -8
- package/es/composables/useStoreSnapshot.js.map +1 -1
- package/es/composables/useSubscribe.d.ts +0 -1
- package/es/composables/useSubscribe.d.ts.map +1 -1
- package/es/composables/useSubscribe.js +1 -1
- package/es/composables/useSubscribe.js.map +1 -1
- package/es/context/injection-keys.d.ts +0 -1
- package/es/context/injection-keys.d.ts.map +1 -1
- package/es/context/injection-keys.js +1 -1
- package/es/context/injection-keys.js.map +1 -1
- package/es/index.d.ts +1 -2
- package/es/index.js +2 -2
- package/es/index.js.map +1 -1
- package/es/plugins/engine-plugin.d.ts +1 -2
- package/es/plugins/engine-plugin.d.ts.map +1 -1
- package/es/plugins/engine-plugin.js +2 -2
- package/es/plugins/engine-plugin.js.map +1 -1
- package/es/plugins/enhanced-persist-plugin.d.ts +0 -1
- package/es/plugins/enhanced-persist-plugin.js +1 -1
- package/es/plugins/index.d.ts +0 -1
- package/es/plugins/index.js +1 -1
- package/es/plugins/pinia-plugin.d.ts +0 -1
- package/es/plugins/pinia-plugin.d.ts.map +1 -1
- package/es/plugins/pinia-plugin.js +24 -6
- package/es/plugins/pinia-plugin.js.map +1 -1
- package/es/plugins/registry-plugin.d.ts +0 -1
- package/es/plugins/registry-plugin.js +1 -1
- package/es/plugins/vue-plugin.d.ts +0 -1
- package/es/plugins/vue-plugin.d.ts.map +1 -1
- package/es/plugins/vue-plugin.js +1 -1
- package/es/plugins/vue-plugin.js.map +1 -1
- package/es/runtime/install-store-app.d.ts +0 -1
- package/es/runtime/install-store-app.js +1 -1
- package/es/runtime/install-store-app.js.map +1 -1
- package/es/runtime/store-plugin-options.d.ts +0 -1
- package/es/runtime/store-plugin-options.js +1 -1
- package/es/runtime/store-plugin-options.js.map +1 -1
- package/es/runtime/store-registry.d.ts +0 -1
- package/es/runtime/store-registry.d.ts.map +1 -1
- package/es/runtime/store-registry.js +14 -4
- package/es/runtime/store-registry.js.map +1 -1
- package/es/runtime/store-runtime.d.ts +1 -2
- package/es/runtime/store-runtime.d.ts.map +1 -1
- package/es/runtime/store-runtime.js +8 -2
- package/es/runtime/store-runtime.js.map +1 -1
- package/es/types.d.ts +2 -1
- package/es/types.d.ts.map +1 -1
- package/es/types.js +1 -1
- package/esm/composables/defineSetupStore.d.ts.map +1 -1
- package/esm/composables/defineSetupStore.js +1 -1
- package/esm/composables/defineSetupStore.js.map +1 -1
- package/esm/composables/index.d.ts +8 -8
- package/esm/composables/index.d.ts.map +1 -1
- package/esm/composables/index.js +1 -1
- package/esm/composables/useFormStore.d.ts.map +1 -1
- package/esm/composables/useFormStore.js +1 -1
- package/esm/composables/useFormStore.js.map +1 -1
- package/esm/composables/useOptimistic.d.ts +1 -51
- package/esm/composables/useOptimistic.d.ts.map +1 -1
- package/esm/composables/useOptimistic.js +35 -20
- package/esm/composables/useOptimistic.js.map +1 -1
- package/esm/composables/usePersist.d.ts.map +1 -1
- package/esm/composables/usePersist.js +16 -7
- package/esm/composables/usePersist.js.map +1 -1
- package/esm/composables/usePersistedState.d.ts.map +1 -1
- package/esm/composables/usePersistedState.js +16 -3
- package/esm/composables/usePersistedState.js.map +1 -1
- package/esm/composables/useSharedState.d.ts.map +1 -1
- package/esm/composables/useSharedState.js +25 -8
- package/esm/composables/useSharedState.js.map +1 -1
- package/esm/composables/useStore.js +1 -1
- package/esm/composables/useStore.js.map +1 -1
- package/esm/composables/useStoreDevtools.d.ts.map +1 -1
- package/esm/composables/useStoreDevtools.js +1 -1
- package/esm/composables/useStoreDevtools.js.map +1 -1
- package/esm/composables/useStoreSnapshot.d.ts.map +1 -1
- package/esm/composables/useStoreSnapshot.js +18 -8
- package/esm/composables/useStoreSnapshot.js.map +1 -1
- package/esm/composables/useSubscribe.d.ts.map +1 -1
- package/esm/composables/useSubscribe.js +1 -1
- package/esm/composables/useSubscribe.js.map +1 -1
- package/esm/context/injection-keys.d.ts.map +1 -1
- package/esm/context/injection-keys.js +1 -1
- package/esm/context/injection-keys.js.map +1 -1
- package/esm/index.d.ts +1 -1
- package/esm/index.js +2 -2
- package/esm/index.js.map +1 -1
- package/esm/plugins/engine-plugin.d.ts +1 -1
- package/esm/plugins/engine-plugin.d.ts.map +1 -1
- package/esm/plugins/engine-plugin.js +2 -2
- package/esm/plugins/engine-plugin.js.map +1 -1
- package/esm/plugins/enhanced-persist-plugin.js +1 -1
- package/esm/plugins/index.js +1 -1
- package/esm/plugins/pinia-plugin.d.ts.map +1 -1
- package/esm/plugins/pinia-plugin.js +24 -6
- package/esm/plugins/pinia-plugin.js.map +1 -1
- package/esm/plugins/registry-plugin.js +1 -1
- package/esm/plugins/vue-plugin.d.ts.map +1 -1
- package/esm/plugins/vue-plugin.js +1 -1
- package/esm/plugins/vue-plugin.js.map +1 -1
- package/esm/runtime/install-store-app.js +1 -1
- package/esm/runtime/install-store-app.js.map +1 -1
- package/esm/runtime/store-plugin-options.js +1 -1
- package/esm/runtime/store-plugin-options.js.map +1 -1
- package/esm/runtime/store-registry.d.ts.map +1 -1
- package/esm/runtime/store-registry.js +14 -4
- package/esm/runtime/store-registry.js.map +1 -1
- package/esm/runtime/store-runtime.d.ts +1 -1
- package/esm/runtime/store-runtime.d.ts.map +1 -1
- package/esm/runtime/store-runtime.js +8 -2
- package/esm/runtime/store-runtime.js.map +1 -1
- package/esm/types.d.ts +2 -0
- package/esm/types.d.ts.map +1 -1
- package/esm/types.js +1 -1
- package/lib/composables/defineSetupStore.cjs +1 -1
- package/lib/composables/defineSetupStore.cjs.map +1 -1
- package/lib/composables/{defineSetupStore.d.ts → defineSetupStore.d.cts} +0 -1
- package/lib/composables/defineSetupStore.d.cts.map +1 -0
- package/lib/composables/index.cjs +1 -1
- package/lib/composables/{index.d.ts → index.d.cts} +8 -9
- package/lib/composables/index.d.cts.map +1 -0
- package/lib/composables/useFormStore.cjs +1 -1
- package/lib/composables/useFormStore.cjs.map +1 -1
- package/lib/composables/{useFormStore.d.ts → useFormStore.d.cts} +0 -1
- package/lib/composables/useFormStore.d.cts.map +1 -0
- package/lib/composables/useOptimistic.cjs +35 -20
- package/lib/composables/useOptimistic.cjs.map +1 -1
- package/lib/composables/useOptimistic.d.cts +16 -0
- package/lib/composables/useOptimistic.d.cts.map +1 -0
- package/lib/composables/usePersist.cjs +16 -7
- package/lib/composables/usePersist.cjs.map +1 -1
- package/lib/composables/{usePersist.d.ts → usePersist.d.cts} +0 -1
- package/lib/composables/usePersist.d.cts.map +1 -0
- package/lib/composables/usePersistedState.cjs +16 -3
- package/lib/composables/usePersistedState.cjs.map +1 -1
- package/lib/composables/{usePersistedState.d.ts → usePersistedState.d.cts} +0 -1
- package/lib/composables/usePersistedState.d.cts.map +1 -0
- package/lib/composables/useSharedState.cjs +24 -7
- package/lib/composables/useSharedState.cjs.map +1 -1
- package/lib/composables/{useSharedState.d.ts → useSharedState.d.cts} +0 -1
- package/lib/composables/useSharedState.d.cts.map +1 -0
- package/lib/composables/useStore.cjs +1 -1
- package/lib/composables/useStore.cjs.map +1 -1
- package/lib/composables/{useStore.d.ts → useStore.d.cts} +0 -1
- package/lib/composables/useStore.d.cts.map +1 -0
- package/lib/composables/useStoreDevtools.cjs +1 -1
- package/lib/composables/useStoreDevtools.cjs.map +1 -1
- package/lib/composables/{useStoreDevtools.d.ts → useStoreDevtools.d.cts} +0 -1
- package/lib/composables/useStoreDevtools.d.cts.map +1 -0
- package/lib/composables/useStoreSnapshot.cjs +18 -8
- package/lib/composables/useStoreSnapshot.cjs.map +1 -1
- package/lib/composables/{useStoreSnapshot.d.ts → useStoreSnapshot.d.cts} +0 -1
- package/lib/composables/useStoreSnapshot.d.cts.map +1 -0
- package/lib/composables/useSubscribe.cjs +1 -1
- package/lib/composables/useSubscribe.cjs.map +1 -1
- package/lib/composables/{useSubscribe.d.ts → useSubscribe.d.cts} +0 -1
- package/lib/composables/useSubscribe.d.cts.map +1 -0
- package/lib/context/injection-keys.cjs +1 -1
- package/lib/context/injection-keys.cjs.map +1 -1
- package/lib/context/{injection-keys.d.ts → injection-keys.d.cts} +0 -1
- package/lib/context/injection-keys.d.cts.map +1 -0
- package/lib/index.cjs +2 -2
- package/lib/index.cjs.map +1 -1
- package/lib/{index.d.ts → index.d.cts} +1 -2
- package/lib/index.d.cts.map +1 -0
- package/lib/plugins/engine-plugin.cjs +2 -2
- package/lib/plugins/engine-plugin.cjs.map +1 -1
- package/lib/plugins/{engine-plugin.d.ts → engine-plugin.d.cts} +1 -2
- package/lib/plugins/engine-plugin.d.cts.map +1 -0
- package/lib/plugins/enhanced-persist-plugin.cjs +1 -1
- package/lib/plugins/{enhanced-persist-plugin.d.ts → enhanced-persist-plugin.d.cts} +0 -1
- package/lib/plugins/enhanced-persist-plugin.d.cts.map +1 -0
- package/lib/plugins/index.cjs +1 -1
- package/lib/plugins/{index.d.ts → index.d.cts} +0 -1
- package/lib/plugins/index.d.cts.map +1 -0
- package/lib/plugins/pinia-plugin.cjs +24 -6
- package/lib/plugins/pinia-plugin.cjs.map +1 -1
- package/lib/plugins/{pinia-plugin.d.ts → pinia-plugin.d.cts} +0 -1
- package/lib/plugins/pinia-plugin.d.cts.map +1 -0
- package/lib/plugins/registry-plugin.cjs +1 -1
- package/lib/plugins/{registry-plugin.d.ts → registry-plugin.d.cts} +0 -1
- package/lib/plugins/registry-plugin.d.cts.map +1 -0
- package/lib/plugins/vue-plugin.cjs +1 -1
- package/lib/plugins/vue-plugin.cjs.map +1 -1
- package/lib/plugins/{vue-plugin.d.ts → vue-plugin.d.cts} +0 -1
- package/lib/plugins/vue-plugin.d.cts.map +1 -0
- package/lib/runtime/install-store-app.cjs +1 -1
- package/lib/runtime/install-store-app.cjs.map +1 -1
- package/lib/runtime/{install-store-app.d.ts → install-store-app.d.cts} +0 -1
- package/lib/runtime/install-store-app.d.cts.map +1 -0
- package/lib/runtime/store-plugin-options.cjs +1 -1
- package/lib/runtime/store-plugin-options.cjs.map +1 -1
- package/lib/runtime/{store-plugin-options.d.ts → store-plugin-options.d.cts} +0 -1
- package/lib/runtime/store-plugin-options.d.cts.map +1 -0
- package/lib/runtime/store-registry.cjs +14 -4
- package/lib/runtime/store-registry.cjs.map +1 -1
- package/lib/runtime/{store-registry.d.ts → store-registry.d.cts} +0 -1
- package/lib/runtime/store-registry.d.cts.map +1 -0
- package/lib/runtime/store-runtime.cjs +8 -2
- package/lib/runtime/store-runtime.cjs.map +1 -1
- package/lib/runtime/{store-runtime.d.ts → store-runtime.d.cts} +1 -2
- package/lib/runtime/store-runtime.d.cts.map +1 -0
- package/lib/types.cjs +1 -1
- package/lib/{types.d.ts → types.d.cts} +2 -1
- package/lib/types.d.cts.map +1 -0
- package/package.json +19 -16
- package/lib/composables/useOptimistic.d.ts +0 -67
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ***********************************
|
|
3
3
|
* @composy/store-vue v0.0.1 *
|
|
4
4
|
* Built with rollup *
|
|
5
|
-
* Build time: 2024-
|
|
5
|
+
* Build time: 2024-10-12 17:40:04 *
|
|
6
6
|
* Build mode: production *
|
|
7
7
|
* Minified: No *
|
|
8
8
|
* ***********************************
|
|
@@ -76,6 +76,7 @@ function useStoreSnapshot(state, setState, options = {}) {
|
|
|
76
76
|
const currentIndex = ref(-1);
|
|
77
77
|
let debounceTimer;
|
|
78
78
|
let isRestoring = false;
|
|
79
|
+
let previousAutoRecordState;
|
|
79
80
|
const getState = () => {
|
|
80
81
|
return typeof state === "function" ? state() : state.value;
|
|
81
82
|
};
|
|
@@ -106,6 +107,7 @@ function useStoreSnapshot(state, setState, options = {}) {
|
|
|
106
107
|
try {
|
|
107
108
|
setState(deepClone(snapshots.value[index].state));
|
|
108
109
|
currentIndex.value = index;
|
|
110
|
+
previousAutoRecordState = deepClone(getState());
|
|
109
111
|
return true;
|
|
110
112
|
} finally {
|
|
111
113
|
isRestoring = false;
|
|
@@ -113,10 +115,12 @@ function useStoreSnapshot(state, setState, options = {}) {
|
|
|
113
115
|
};
|
|
114
116
|
const undo = () => {
|
|
115
117
|
if (!canUndo.value) return false;
|
|
116
|
-
currentIndex.value
|
|
118
|
+
const targetIndex = currentIndex.value - 1;
|
|
117
119
|
isRestoring = true;
|
|
118
120
|
try {
|
|
119
|
-
setState(deepClone(snapshots.value[
|
|
121
|
+
setState(deepClone(snapshots.value[targetIndex].state));
|
|
122
|
+
currentIndex.value = targetIndex;
|
|
123
|
+
previousAutoRecordState = deepClone(getState());
|
|
120
124
|
return true;
|
|
121
125
|
} finally {
|
|
122
126
|
isRestoring = false;
|
|
@@ -124,10 +128,12 @@ function useStoreSnapshot(state, setState, options = {}) {
|
|
|
124
128
|
};
|
|
125
129
|
const redo = () => {
|
|
126
130
|
if (!canRedo.value) return false;
|
|
127
|
-
currentIndex.value
|
|
131
|
+
const targetIndex = currentIndex.value + 1;
|
|
128
132
|
isRestoring = true;
|
|
129
133
|
try {
|
|
130
|
-
setState(deepClone(snapshots.value[
|
|
134
|
+
setState(deepClone(snapshots.value[targetIndex].state));
|
|
135
|
+
currentIndex.value = targetIndex;
|
|
136
|
+
previousAutoRecordState = deepClone(getState());
|
|
131
137
|
return true;
|
|
132
138
|
} finally {
|
|
133
139
|
isRestoring = false;
|
|
@@ -135,10 +141,11 @@ function useStoreSnapshot(state, setState, options = {}) {
|
|
|
135
141
|
};
|
|
136
142
|
const goto = (index) => {
|
|
137
143
|
if (index < 0 || index >= snapshots.value.length) return false;
|
|
138
|
-
currentIndex.value = index;
|
|
139
144
|
isRestoring = true;
|
|
140
145
|
try {
|
|
141
146
|
setState(deepClone(snapshots.value[index].state));
|
|
147
|
+
currentIndex.value = index;
|
|
148
|
+
previousAutoRecordState = deepClone(getState());
|
|
142
149
|
return true;
|
|
143
150
|
} finally {
|
|
144
151
|
isRestoring = false;
|
|
@@ -173,9 +180,12 @@ function useStoreSnapshot(state, setState, options = {}) {
|
|
|
173
180
|
}
|
|
174
181
|
};
|
|
175
182
|
if (autoRecord) {
|
|
176
|
-
const
|
|
183
|
+
const stateRef = typeof state === "function" ? computed(state) : state;
|
|
184
|
+
previousAutoRecordState = deepClone(getState());
|
|
185
|
+
const stopWatch = watch(stateRef, (newState, oldState) => {
|
|
177
186
|
if (isRestoring) return;
|
|
178
|
-
if (deepEqual(newState, oldState)) return;
|
|
187
|
+
if (deepEqual(newState, previousAutoRecordState ?? oldState)) return;
|
|
188
|
+
previousAutoRecordState = deepClone(newState);
|
|
179
189
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
180
190
|
debounceTimer = setTimeout(() => {
|
|
181
191
|
takeSnapshot();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStoreSnapshot.js","names":[],"sources":["../../src/composables/useStoreSnapshot.ts"],"sourcesContent":["/**\n * useStoreSnapshot - Store 状态快照工具\n *\n * 提供状态快照、历史记录和调试功能\n * @module composables/useStoreSnapshot\n */\n\nimport type {ComputedRef, Ref} from 'vue';\nimport { deepClone, deepEqual } from '@composy/store-core'\nimport { computed, onUnmounted, ref, watch } from 'vue'\n\n/**\n * 状态快照\n */\nexport interface StateSnapshot<S = unknown> {\n /** 快照 ID */\n id: string\n /** 状态数据 */\n state: S\n /** 时间戳 */\n timestamp: number\n /** 快照标签 */\n label?: string\n /** 元数据 */\n meta?: Record<string, unknown>\n}\n\n/**\n * 状态差异\n */\nexport interface StateDiff {\n /** 差异路径 */\n path: string\n /** 旧值 */\n oldValue: unknown\n /** 新值 */\n newValue: unknown\n /** 差异类型 */\n type: 'add' | 'remove' | 'change'\n}\n\n/**\n * useStoreSnapshot 选项\n */\nexport interface UseStoreSnapshotOptions {\n /** 最大快照数量 */\n maxSnapshots?: number\n /** 是否自动记录 */\n autoRecord?: boolean\n /** 自动记录防抖延迟(毫秒) */\n debounceMs?: number\n /** 是否深度监听 */\n deep?: boolean\n}\n\n/**\n * useStoreSnapshot 返回值\n */\nexport interface UseStoreSnapshotReturn<S> {\n /** 快照列表 */\n snapshots: Ref<Array<StateSnapshot<S>>>\n /** 当前快照索引 */\n currentIndex: Ref<number>\n /** 是否可以撤销 */\n canUndo: ComputedRef<boolean>\n /** 是否可以重做 */\n canRedo: ComputedRef<boolean>\n /** 创建快照 */\n takeSnapshot: (label?: string, meta?: Record<string, unknown>) => StateSnapshot<S>\n /** 恢复快照 */\n restoreSnapshot: (id: string) => boolean\n /** 撤销 */\n undo: () => boolean\n /** 重做 */\n redo: () => boolean\n /** 跳转到指定快照 */\n goto: (index: number) => boolean\n /** 清空快照 */\n clearSnapshots: () => void\n /** 获取状态差异 */\n getDiff: (snapshotId1: string, snapshotId2: string) => StateDiff[]\n /** 导出快照 */\n exportSnapshots: () => string\n /** 导入快照 */\n importSnapshots: (data: string) => boolean\n}\n\n/**\n * 生成唯一 ID\n */\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`\n}\n\n/**\n * 计算两个对象的差异\n */\nfunction computeDiff(\n oldObj: unknown,\n newObj: unknown,\n path = '',\n): StateDiff[] {\n const diffs: StateDiff[] = []\n\n // 类型不同\n if (typeof oldObj !== typeof newObj) {\n diffs.push({\n path: path || '.',\n oldValue: oldObj,\n newValue: newObj,\n type: 'change',\n })\n return diffs\n }\n\n // 基础类型比较\n if (typeof oldObj !== 'object' || oldObj === null || newObj === null) {\n if (!Object.is(oldObj, newObj)) {\n diffs.push({\n path: path || '.',\n oldValue: oldObj,\n newValue: newObj,\n type: 'change',\n })\n }\n return diffs\n }\n\n // 数组比较\n if (Array.isArray(oldObj) && Array.isArray(newObj)) {\n const maxLen = Math.max(oldObj.length, newObj.length)\n for (let i = 0; i < maxLen; i++) {\n const itemPath = path ? `${path}[${i}]` : `[${i}]`\n if (i >= oldObj.length) {\n diffs.push({\n path: itemPath,\n oldValue: undefined,\n newValue: newObj[i],\n type: 'add',\n })\n }\n else if (i >= newObj.length) {\n diffs.push({\n path: itemPath,\n oldValue: oldObj[i],\n newValue: undefined,\n type: 'remove',\n })\n }\n else {\n diffs.push(...computeDiff(oldObj[i], newObj[i], itemPath))\n }\n }\n return diffs\n }\n\n // 对象比较\n const oldKeys = new Set(Object.keys(oldObj as Record<string, unknown>))\n const newKeys = new Set(Object.keys(newObj as Record<string, unknown>))\n\n // 检查新增的键\n for (const key of newKeys) {\n if (!oldKeys.has(key)) {\n diffs.push({\n path: path ? `${path}.${key}` : key,\n oldValue: undefined,\n newValue: (newObj as Record<string, unknown>)[key],\n type: 'add',\n })\n }\n }\n\n // 检查删除的键\n for (const key of oldKeys) {\n if (!newKeys.has(key)) {\n diffs.push({\n path: path ? `${path}.${key}` : key,\n oldValue: (oldObj as Record<string, unknown>)[key],\n newValue: undefined,\n type: 'remove',\n })\n }\n }\n\n // 检查修改的键\n for (const key of oldKeys) {\n if (newKeys.has(key)) {\n diffs.push(\n ...computeDiff(\n (oldObj as Record<string, unknown>)[key],\n (newObj as Record<string, unknown>)[key],\n path ? `${path}.${key}` : key,\n ),\n )\n }\n }\n\n return diffs\n}\n\n/**\n * Store 状态快照 Hook\n *\n * 提供状态快照、撤销/重做和调试功能\n *\n * @template S - 状态类型\n * @param state - 响应式状态\n * @param setState - 状态设置函数\n * @param options - 配置选项\n * @returns 快照操作对象\n *\n * @example\n * ```typescript\n * const store = useCounterStore()\n *\n * const {\n * snapshots,\n * canUndo,\n * canRedo,\n * takeSnapshot,\n * undo,\n * redo,\n * getDiff\n * } = useStoreSnapshot(\n * () => store.$state,\n * (state) => store.$patch(state),\n * { maxSnapshots: 50, autoRecord: true }\n * )\n *\n * // 手动创建快照\n * takeSnapshot('保存点 1')\n *\n * // 撤销操作\n * if (canUndo.value) {\n * undo()\n * }\n *\n * // 获取状态差异\n * const diff = getDiff(snapshots.value[0].id, snapshots.value[1].id)\n * ```\n */\nexport function useStoreSnapshot<S>(\n state: Ref<S> | (() => S),\n setState: (state: S) => void,\n options: UseStoreSnapshotOptions = {},\n): UseStoreSnapshotReturn<S> {\n const {\n maxSnapshots = 100,\n autoRecord = false,\n debounceMs = 300,\n deep = true,\n } = options\n\n const snapshots = ref<Array<StateSnapshot<S>>>([]) as Ref<Array<StateSnapshot<S>>>\n const currentIndex = ref(-1)\n let debounceTimer: ReturnType<typeof setTimeout> | undefined\n let isRestoring = false\n\n // 获取当前状态\n const getState = (): S => {\n return typeof state === 'function' ? state() : state.value\n }\n\n // 是否可以撤销\n const canUndo = computed(() => currentIndex.value > 0)\n\n // 是否可以重做\n const canRedo = computed(() => currentIndex.value < snapshots.value.length - 1)\n\n /**\n * 创建快照\n */\n const takeSnapshot = (label?: string, meta?: Record<string, unknown>): StateSnapshot<S> => {\n const currentState = getState()\n const snapshot: StateSnapshot<S> = {\n id: generateId(),\n state: deepClone(currentState),\n timestamp: Date.now(),\n label,\n meta,\n }\n\n // 如果当前不在最新位置,删除后面的快照\n if (currentIndex.value < snapshots.value.length - 1) {\n snapshots.value = snapshots.value.slice(0, currentIndex.value + 1)\n }\n\n // 添加新快照\n snapshots.value.push(snapshot)\n currentIndex.value = snapshots.value.length - 1\n\n // 限制快照数量\n if (snapshots.value.length > maxSnapshots) {\n snapshots.value.shift()\n currentIndex.value--\n }\n\n return snapshot\n }\n\n /**\n * 恢复快照\n */\n const restoreSnapshot = (id: string): boolean => {\n const index = snapshots.value.findIndex(s => s.id === id)\n if (index === -1) return false\n\n isRestoring = true\n try {\n setState(deepClone(snapshots.value[index].state))\n currentIndex.value = index\n return true\n }\n finally {\n isRestoring = false\n }\n }\n\n /**\n * 撤销\n */\n const undo = (): boolean => {\n if (!canUndo.value) return false\n\n currentIndex.value--\n isRestoring = true\n try {\n setState(deepClone(snapshots.value[currentIndex.value].state))\n return true\n }\n finally {\n isRestoring = false\n }\n }\n\n /**\n * 重做\n */\n const redo = (): boolean => {\n if (!canRedo.value) return false\n\n currentIndex.value++\n isRestoring = true\n try {\n setState(deepClone(snapshots.value[currentIndex.value].state))\n return true\n }\n finally {\n isRestoring = false\n }\n }\n\n /**\n * 跳转到指定快照\n */\n const goto = (index: number): boolean => {\n if (index < 0 || index >= snapshots.value.length) return false\n\n currentIndex.value = index\n isRestoring = true\n try {\n setState(deepClone(snapshots.value[index].state))\n return true\n }\n finally {\n isRestoring = false\n }\n }\n\n /**\n * 清空快照\n */\n const clearSnapshots = (): void => {\n snapshots.value = []\n currentIndex.value = -1\n }\n\n /**\n * 获取状态差异\n */\n const getDiff = (snapshotId1: string, snapshotId2: string): StateDiff[] => {\n const snapshot1 = snapshots.value.find(s => s.id === snapshotId1)\n const snapshot2 = snapshots.value.find(s => s.id === snapshotId2)\n\n if (!snapshot1 || !snapshot2) return []\n\n return computeDiff(snapshot1.state, snapshot2.state)\n }\n\n /**\n * 导出快照\n */\n const exportSnapshots = (): string => {\n return JSON.stringify({\n snapshots: snapshots.value,\n currentIndex: currentIndex.value,\n exportedAt: Date.now(),\n })\n }\n\n /**\n * 导入快照\n */\n const importSnapshots = (data: string): boolean => {\n try {\n const parsed = JSON.parse(data) as {\n snapshots: Array<StateSnapshot<S>>\n currentIndex: number\n }\n\n if (!Array.isArray(parsed.snapshots)) return false\n\n snapshots.value = parsed.snapshots\n currentIndex.value = Math.min(\n parsed.currentIndex ?? snapshots.value.length - 1,\n snapshots.value.length - 1,\n )\n\n return true\n }\n catch {\n return false\n }\n }\n\n // 自动记录\n if (autoRecord) {\n const stateRef = typeof state === 'function' ? computed(state) : state\n\n const stopWatch = watch(\n stateRef,\n (newState, oldState) => {\n // 如果是恢复操作,不记录\n if (isRestoring) return\n\n // 如果状态未变化,不记录\n if (deepEqual(newState, oldState)) return\n\n // 防抖记录\n if (debounceTimer) {\n clearTimeout(debounceTimer)\n }\n\n debounceTimer = setTimeout(() => {\n takeSnapshot()\n }, debounceMs)\n },\n { deep },\n )\n\n // 初始快照\n takeSnapshot('初始状态')\n\n onUnmounted(() => {\n stopWatch()\n if (debounceTimer) {\n clearTimeout(debounceTimer)\n }\n })\n }\n\n return {\n snapshots,\n currentIndex,\n canUndo,\n canRedo,\n takeSnapshot,\n restoreSnapshot,\n undo,\n redo,\n goto,\n clearSnapshots,\n getDiff,\n exportSnapshots,\n importSnapshots,\n }\n}\n\n"],"mappings":";;;;;;;;;;;;AA0FA,SAAS,aAAqB;CAC5B,OAAO,GAAG,KAAK,IAAI,EAAC,GAAI,KAAK,OAAO,CAAA,CAAE,SAAS,EAAE,CAAA,CAAE,OAAO,GAAG,CAAC;AAChE;AAKA,SAAS,YACP,QACA,QACA,OAAO,IACM;CACb,MAAM,QAAqB,CAAC;CAG5B,IAAI,OAAO,WAAW,OAAO,QAAQ;EACnC,MAAM,KAAK;GACT,MAAM,QAAQ;GACd,UAAU;GACV,UAAU;GACV,MAAM;EACR,CAAC;EACD,OAAO;CACT;CAGA,IAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,WAAW,MAAM;EACpE,IAAI,CAAC,OAAO,GAAG,QAAQ,MAAM,GAC3B,MAAM,KAAK;GACT,MAAM,QAAQ;GACd,UAAU;GACV,UAAU;GACV,MAAM;EACR,CAAC;EAEH,OAAO;CACT;CAGA,IAAI,MAAM,QAAQ,MAAM,KAAK,MAAM,QAAQ,MAAM,GAAG;EAClD,MAAM,SAAS,KAAK,IAAI,OAAO,QAAQ,OAAO,MAAM;EACpD,KAAA,IAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;GAC/B,MAAM,WAAW,OAAO,GAAG,KAAI,GAAI,EAAC,KAAM,IAAI,EAAC;GAC/C,IAAI,KAAK,OAAO,QACd,MAAM,KAAK;IACT,MAAM;IACN,UAAU,KAAA;IACV,UAAU,OAAO;IACjB,MAAM;GACR,CAAC;QACH,IACS,KAAK,OAAO,QACnB,MAAM,KAAK;IACT,MAAM;IACN,UAAU,OAAO;IACjB,UAAU,KAAA;IACV,MAAM;GACR,CAAC;QAGD,MAAM,KAAK,GAAG,YAAY,OAAO,IAAI,OAAO,IAAI,QAAQ,CAAC;EAE7D;EACA,OAAO;CACT;CAGA,MAAM,UAAU,IAAI,IAAI,OAAO,KAAK,MAAiC,CAAC;CACtE,MAAM,UAAU,IAAI,IAAI,OAAO,KAAK,MAAiC,CAAC;CAGtE,KAAA,MAAW,OAAO,SAChB,IAAI,CAAC,QAAQ,IAAI,GAAG,GAClB,MAAM,KAAK;EACT,MAAM,OAAO,GAAG,KAAI,GAAI,QAAQ;EAChC,UAAU,KAAA;EACV,UAAW,OAAmC;EAC9C,MAAM;CACR,CAAC;CAKL,KAAA,MAAW,OAAO,SAChB,IAAI,CAAC,QAAQ,IAAI,GAAG,GAClB,MAAM,KAAK;EACT,MAAM,OAAO,GAAG,KAAI,GAAI,QAAQ;EAChC,UAAW,OAAmC;EAC9C,UAAU,KAAA;EACV,MAAM;CACR,CAAC;CAKL,KAAA,MAAW,OAAO,SAChB,IAAI,QAAQ,IAAI,GAAG,GACjB,MAAM,KACJ,GAAG,YACA,OAAmC,MACnC,OAAmC,MACpC,OAAO,GAAG,KAAI,GAAI,QAAQ,GAC5B,CACF;CAIJ,OAAO;AACT;AA2CO,SAAS,iBACd,OACA,UACA,UAAmC,CAAC,GACT;CAC3B,MAAM,EACJ,eAAe,KACf,aAAa,OACb,aAAa,KACb,OAAO,SACL;CAEJ,MAAM,YAAY,IAA6B,CAAC,CAAC;CACjD,MAAM,eAAe,IAAI,EAAE;CAC3B,IAAI;CACJ,IAAI,cAAc;CAGlB,MAAM,iBAAoB;EACxB,OAAO,OAAO,UAAU,aAAa,MAAM,IAAI,MAAM;CACvD;CAGA,MAAM,UAAU,eAAe,aAAa,QAAQ,CAAC;CAGrD,MAAM,UAAU,eAAe,aAAa,QAAQ,UAAU,MAAM,SAAS,CAAC;CAK9E,MAAM,gBAAgB,OAAgB,SAAqD;EACzF,MAAM,eAAe,SAAS;EAC9B,MAAM,WAA6B;GACjC,IAAI,WAAW;GACf,OAAO,UAAU,YAAY;GAC7B,WAAW,KAAK,IAAI;GACpB;GACA;EACF;EAGA,IAAI,aAAa,QAAQ,UAAU,MAAM,SAAS,GAChD,UAAU,QAAQ,UAAU,MAAM,MAAM,GAAG,aAAa,QAAQ,CAAC;EAInE,UAAU,MAAM,KAAK,QAAQ;EAC7B,aAAa,QAAQ,UAAU,MAAM,SAAS;EAG9C,IAAI,UAAU,MAAM,SAAS,cAAc;GACzC,UAAU,MAAM,MAAM;GACtB,aAAa;EACf;EAEA,OAAO;CACT;CAKA,MAAM,mBAAmB,OAAwB;EAC/C,MAAM,QAAQ,UAAU,MAAM,WAAU,MAAK,EAAE,OAAO,EAAE;EACxD,IAAI,UAAU,IAAI,OAAO;EAEzB,cAAc;EACd,IAAI;GACF,SAAS,UAAU,UAAU,MAAM,MAAK,CAAE,KAAK,CAAC;GAChD,aAAa,QAAQ;GACrB,OAAO;EACT,UACA;GACE,cAAc;EAChB;CACF;CAKA,MAAM,aAAsB;EAC1B,IAAI,CAAC,QAAQ,OAAO,OAAO;EAE3B,aAAa;EACb,cAAc;EACd,IAAI;GACF,SAAS,UAAU,UAAU,MAAM,aAAa,MAAK,CAAE,KAAK,CAAC;GAC7D,OAAO;EACT,UACA;GACE,cAAc;EAChB;CACF;CAKA,MAAM,aAAsB;EAC1B,IAAI,CAAC,QAAQ,OAAO,OAAO;EAE3B,aAAa;EACb,cAAc;EACd,IAAI;GACF,SAAS,UAAU,UAAU,MAAM,aAAa,MAAK,CAAE,KAAK,CAAC;GAC7D,OAAO;EACT,UACA;GACE,cAAc;EAChB;CACF;CAKA,MAAM,QAAQ,UAA2B;EACvC,IAAI,QAAQ,KAAK,SAAS,UAAU,MAAM,QAAQ,OAAO;EAEzD,aAAa,QAAQ;EACrB,cAAc;EACd,IAAI;GACF,SAAS,UAAU,UAAU,MAAM,MAAK,CAAE,KAAK,CAAC;GAChD,OAAO;EACT,UACA;GACE,cAAc;EAChB;CACF;CAKA,MAAM,uBAA6B;EACjC,UAAU,QAAQ,CAAC;EACnB,aAAa,QAAQ;CACvB;CAKA,MAAM,WAAW,aAAqB,gBAAqC;EACzE,MAAM,YAAY,UAAU,MAAM,MAAK,MAAK,EAAE,OAAO,WAAW;EAChE,MAAM,YAAY,UAAU,MAAM,MAAK,MAAK,EAAE,OAAO,WAAW;EAEhE,IAAI,CAAC,aAAa,CAAC,WAAW,OAAO,CAAC;EAEtC,OAAO,YAAY,UAAU,OAAO,UAAU,KAAK;CACrD;CAKA,MAAM,wBAAgC;EACpC,OAAO,KAAK,UAAU;GACpB,WAAW,UAAU;GACrB,cAAc,aAAa;GAC3B,YAAY,KAAK,IAAI;EACvB,CAAC;CACH;CAKA,MAAM,mBAAmB,SAA0B;EACjD,IAAI;GACF,MAAM,SAAS,KAAK,MAAM,IAAI;GAK9B,IAAI,CAAC,MAAM,QAAQ,OAAO,SAAS,GAAG,OAAO;GAE7C,UAAU,QAAQ,OAAO;GACzB,aAAa,QAAQ,KAAK,IACxB,OAAO,gBAAgB,UAAU,MAAM,SAAS,GAChD,UAAU,MAAM,SAAS,CAC3B;GAEA,OAAO;EACT,QACM;GACJ,OAAO;EACT;CACF;CAGA,IAAI,YAAY;EAGd,MAAM,YAAY,MAFD,OAAO,UAAU,aAAa,SAAS,KAAK,IAAI,QAI9D,UAAU,aAAa;GAEtB,IAAI,aAAa;GAGjB,IAAI,UAAU,UAAU,QAAQ,GAAG;GAGnC,IAAI,eACF,aAAa,aAAa;GAG5B,gBAAgB,iBAAiB;IAC/B,aAAa;GACf,GAAG,UAAU;EACf,GACA,EAAE,KAAK,CACT;EAGA,aAAa,MAAM;EAEnB,kBAAkB;GAChB,UAAU;GACV,IAAI,eACF,aAAa,aAAa;EAE9B,CAAC;CACH;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"useStoreSnapshot.js","names":[],"sources":["../../src/composables/useStoreSnapshot.ts"],"sourcesContent":["/**\r\n * useStoreSnapshot - Store 状态快照工具\r\n *\r\n * 提供状态快照、历史记录和调试功能\r\n * @module composables/useStoreSnapshot\r\n */\r\n\r\nimport type { ComputedRef, Ref } from 'vue'\r\nimport { deepClone, deepEqual } from '@composy/store-core'\r\nimport { computed, onUnmounted, ref, watch } from 'vue'\r\n\r\n/**\r\n * 状态快照\r\n */\r\nexport interface StateSnapshot<S = unknown> {\r\n /** 快照 ID */\r\n id: string\r\n /** 状态数据 */\r\n state: S\r\n /** 时间戳 */\r\n timestamp: number\r\n /** 快照标签 */\r\n label?: string\r\n /** 元数据 */\r\n meta?: Record<string, unknown>\r\n}\r\n\r\n/**\r\n * 状态差异\r\n */\r\nexport interface StateDiff {\r\n /** 差异路径 */\r\n path: string\r\n /** 旧值 */\r\n oldValue: unknown\r\n /** 新值 */\r\n newValue: unknown\r\n /** 差异类型 */\r\n type: 'add' | 'remove' | 'change'\r\n}\r\n\r\n/**\r\n * useStoreSnapshot 选项\r\n */\r\nexport interface UseStoreSnapshotOptions {\r\n /** 最大快照数量 */\r\n maxSnapshots?: number\r\n /** 是否自动记录 */\r\n autoRecord?: boolean\r\n /** 自动记录防抖延迟(毫秒) */\r\n debounceMs?: number\r\n /** 是否深度监听 */\r\n deep?: boolean\r\n}\r\n\r\n/**\r\n * useStoreSnapshot 返回值\r\n */\r\nexport interface UseStoreSnapshotReturn<S> {\r\n /** 快照列表 */\r\n snapshots: Ref<Array<StateSnapshot<S>>>\r\n /** 当前快照索引 */\r\n currentIndex: Ref<number>\r\n /** 是否可以撤销 */\r\n canUndo: ComputedRef<boolean>\r\n /** 是否可以重做 */\r\n canRedo: ComputedRef<boolean>\r\n /** 创建快照 */\r\n takeSnapshot: (\r\n label?: string,\r\n meta?: Record<string, unknown>\r\n ) => StateSnapshot<S>\r\n /** 恢复快照 */\r\n restoreSnapshot: (id: string) => boolean\r\n /** 撤销 */\r\n undo: () => boolean\r\n /** 重做 */\r\n redo: () => boolean\r\n /** 跳转到指定快照 */\r\n goto: (index: number) => boolean\r\n /** 清空快照 */\r\n clearSnapshots: () => void\r\n /** 获取状态差异 */\r\n getDiff: (snapshotId1: string, snapshotId2: string) => StateDiff[]\r\n /** 导出快照 */\r\n exportSnapshots: () => string\r\n /** 导入快照 */\r\n importSnapshots: (data: string) => boolean\r\n}\r\n\r\n/**\r\n * 生成唯一 ID\r\n */\r\nfunction generateId(): string {\r\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`\r\n}\r\n\r\n/**\r\n * 计算两个对象的差异\r\n */\r\nfunction computeDiff(oldObj: unknown, newObj: unknown, path = ''): StateDiff[] {\r\n const diffs: StateDiff[] = []\r\n\r\n // 类型不同\r\n if (typeof oldObj !== typeof newObj) {\r\n diffs.push({\r\n path: path || '.',\r\n oldValue: oldObj,\r\n newValue: newObj,\r\n type: 'change',\r\n })\r\n return diffs\r\n }\r\n\r\n // 基础类型比较\r\n if (typeof oldObj !== 'object' || oldObj === null || newObj === null) {\r\n if (!Object.is(oldObj, newObj)) {\r\n diffs.push({\r\n path: path || '.',\r\n oldValue: oldObj,\r\n newValue: newObj,\r\n type: 'change',\r\n })\r\n }\r\n return diffs\r\n }\r\n\r\n // 数组比较\r\n if (Array.isArray(oldObj) && Array.isArray(newObj)) {\r\n const maxLen = Math.max(oldObj.length, newObj.length)\r\n for (let i = 0; i < maxLen; i++) {\r\n const itemPath = path ? `${path}[${i}]` : `[${i}]`\r\n if (i >= oldObj.length) {\r\n diffs.push({\r\n path: itemPath,\r\n oldValue: undefined,\r\n newValue: newObj[i],\r\n type: 'add',\r\n })\r\n } else if (i >= newObj.length) {\r\n diffs.push({\r\n path: itemPath,\r\n oldValue: oldObj[i],\r\n newValue: undefined,\r\n type: 'remove',\r\n })\r\n } else {\r\n diffs.push(...computeDiff(oldObj[i], newObj[i], itemPath))\r\n }\r\n }\r\n return diffs\r\n }\r\n\r\n // 对象比较\r\n const oldKeys = new Set(Object.keys(oldObj as Record<string, unknown>))\r\n const newKeys = new Set(Object.keys(newObj as Record<string, unknown>))\r\n\r\n // 检查新增的键\r\n for (const key of newKeys) {\r\n if (!oldKeys.has(key)) {\r\n diffs.push({\r\n path: path ? `${path}.${key}` : key,\r\n oldValue: undefined,\r\n newValue: (newObj as Record<string, unknown>)[key],\r\n type: 'add',\r\n })\r\n }\r\n }\r\n\r\n // 检查删除的键\r\n for (const key of oldKeys) {\r\n if (!newKeys.has(key)) {\r\n diffs.push({\r\n path: path ? `${path}.${key}` : key,\r\n oldValue: (oldObj as Record<string, unknown>)[key],\r\n newValue: undefined,\r\n type: 'remove',\r\n })\r\n }\r\n }\r\n\r\n // 检查修改的键\r\n for (const key of oldKeys) {\r\n if (newKeys.has(key)) {\r\n diffs.push(\r\n ...computeDiff(\r\n (oldObj as Record<string, unknown>)[key],\r\n (newObj as Record<string, unknown>)[key],\r\n path ? `${path}.${key}` : key\r\n )\r\n )\r\n }\r\n }\r\n\r\n return diffs\r\n}\r\n\r\n/**\r\n * Store 状态快照 Hook\r\n *\r\n * 提供状态快照、撤销/重做和调试功能\r\n *\r\n * @template S - 状态类型\r\n * @param state - 响应式状态\r\n * @param setState - 状态设置函数\r\n * @param options - 配置选项\r\n * @returns 快照操作对象\r\n *\r\n * @example\r\n * ```typescript\r\n * const store = useCounterStore()\r\n *\r\n * const {\r\n * snapshots,\r\n * canUndo,\r\n * canRedo,\r\n * takeSnapshot,\r\n * undo,\r\n * redo,\r\n * getDiff\r\n * } = useStoreSnapshot(\r\n * () => store.$state,\r\n * (state) => store.$patch(state),\r\n * { maxSnapshots: 50, autoRecord: true }\r\n * )\r\n *\r\n * // 手动创建快照\r\n * takeSnapshot('保存点 1')\r\n *\r\n * // 撤销操作\r\n * if (canUndo.value) {\r\n * undo()\r\n * }\r\n *\r\n * // 获取状态差异\r\n * const diff = getDiff(snapshots.value[0].id, snapshots.value[1].id)\r\n * ```\r\n */\r\nexport function useStoreSnapshot<S>(\r\n state: Ref<S> | (() => S),\r\n setState: (state: S) => void,\r\n options: UseStoreSnapshotOptions = {}\r\n): UseStoreSnapshotReturn<S> {\r\n const {\r\n maxSnapshots = 100,\r\n autoRecord = false,\r\n debounceMs = 300,\r\n deep = true,\r\n } = options\r\n\r\n const snapshots = ref<Array<StateSnapshot<S>>>([]) as Ref<\r\n Array<StateSnapshot<S>>\r\n >\r\n const currentIndex = ref(-1)\r\n let debounceTimer: ReturnType<typeof setTimeout> | undefined\r\n let isRestoring = false\r\n let previousAutoRecordState: S | undefined\r\n\r\n // 获取当前状态\r\n const getState = (): S => {\r\n return typeof state === 'function' ? state() : state.value\r\n }\r\n\r\n // 是否可以撤销\r\n const canUndo = computed(() => currentIndex.value > 0)\r\n\r\n // 是否可以重做\r\n const canRedo = computed(\r\n () => currentIndex.value < snapshots.value.length - 1\r\n )\r\n\r\n /**\r\n * 创建快照\r\n */\r\n const takeSnapshot = (\r\n label?: string,\r\n meta?: Record<string, unknown>\r\n ): StateSnapshot<S> => {\r\n const currentState = getState()\r\n const snapshot: StateSnapshot<S> = {\r\n id: generateId(),\r\n state: deepClone(currentState),\r\n timestamp: Date.now(),\r\n label,\r\n meta,\r\n }\r\n\r\n // 如果当前不在最新位置,删除后面的快照\r\n if (currentIndex.value < snapshots.value.length - 1) {\r\n snapshots.value = snapshots.value.slice(0, currentIndex.value + 1)\r\n }\r\n\r\n // 添加新快照\r\n snapshots.value.push(snapshot)\r\n currentIndex.value = snapshots.value.length - 1\r\n\r\n // 限制快照数量\r\n if (snapshots.value.length > maxSnapshots) {\r\n snapshots.value.shift()\r\n currentIndex.value--\r\n }\r\n\r\n return snapshot\r\n }\r\n\r\n /**\r\n * 恢复快照\r\n */\r\n const restoreSnapshot = (id: string): boolean => {\r\n const index = snapshots.value.findIndex(s => s.id === id)\r\n if (index === -1) return false\r\n\r\n isRestoring = true\r\n try {\r\n setState(deepClone(snapshots.value[index].state))\r\n currentIndex.value = index\r\n previousAutoRecordState = deepClone(getState())\r\n return true\r\n } finally {\r\n isRestoring = false\r\n }\r\n }\r\n\r\n /**\r\n * 撤销\r\n */\r\n const undo = (): boolean => {\r\n if (!canUndo.value) return false\r\n\r\n const targetIndex = currentIndex.value - 1\r\n isRestoring = true\r\n try {\r\n setState(deepClone(snapshots.value[targetIndex].state))\r\n currentIndex.value = targetIndex\r\n previousAutoRecordState = deepClone(getState())\r\n return true\r\n } finally {\r\n isRestoring = false\r\n }\r\n }\r\n\r\n /**\r\n * 重做\r\n */\r\n const redo = (): boolean => {\r\n if (!canRedo.value) return false\r\n\r\n const targetIndex = currentIndex.value + 1\r\n isRestoring = true\r\n try {\r\n setState(deepClone(snapshots.value[targetIndex].state))\r\n currentIndex.value = targetIndex\r\n previousAutoRecordState = deepClone(getState())\r\n return true\r\n } finally {\r\n isRestoring = false\r\n }\r\n }\r\n\r\n /**\r\n * 跳转到指定快照\r\n */\r\n const goto = (index: number): boolean => {\r\n if (index < 0 || index >= snapshots.value.length) return false\r\n\r\n isRestoring = true\r\n try {\r\n setState(deepClone(snapshots.value[index].state))\r\n currentIndex.value = index\r\n previousAutoRecordState = deepClone(getState())\r\n return true\r\n } finally {\r\n isRestoring = false\r\n }\r\n }\r\n\r\n /**\r\n * 清空快照\r\n */\r\n const clearSnapshots = (): void => {\r\n snapshots.value = []\r\n currentIndex.value = -1\r\n }\r\n\r\n /**\r\n * 获取状态差异\r\n */\r\n const getDiff = (snapshotId1: string, snapshotId2: string): StateDiff[] => {\r\n const snapshot1 = snapshots.value.find(s => s.id === snapshotId1)\r\n const snapshot2 = snapshots.value.find(s => s.id === snapshotId2)\r\n\r\n if (!snapshot1 || !snapshot2) return []\r\n\r\n return computeDiff(snapshot1.state, snapshot2.state)\r\n }\r\n\r\n /**\r\n * 导出快照\r\n */\r\n const exportSnapshots = (): string => {\r\n return JSON.stringify({\r\n snapshots: snapshots.value,\r\n currentIndex: currentIndex.value,\r\n exportedAt: Date.now(),\r\n })\r\n }\r\n\r\n /**\r\n * 导入快照\r\n */\r\n const importSnapshots = (data: string): boolean => {\r\n try {\r\n const parsed = JSON.parse(data) as {\r\n snapshots: Array<StateSnapshot<S>>\r\n currentIndex: number\r\n }\r\n\r\n if (!Array.isArray(parsed.snapshots)) return false\r\n\r\n snapshots.value = parsed.snapshots\r\n currentIndex.value = Math.min(\r\n parsed.currentIndex ?? snapshots.value.length - 1,\r\n snapshots.value.length - 1\r\n )\r\n\r\n return true\r\n } catch {\r\n return false\r\n }\r\n }\r\n\r\n // 自动记录\r\n if (autoRecord) {\r\n const stateRef = typeof state === 'function' ? computed(state) : state\r\n previousAutoRecordState = deepClone(getState())\r\n\r\n const stopWatch = watch(\r\n stateRef,\r\n (newState, oldState) => {\r\n // 如果是恢复操作,不记录\r\n if (isRestoring) return\r\n\r\n // 如果状态未变化,不记录\r\n if (deepEqual(newState, previousAutoRecordState ?? oldState)) return\r\n previousAutoRecordState = deepClone(newState)\r\n\r\n // 防抖记录\r\n if (debounceTimer) {\r\n clearTimeout(debounceTimer)\r\n }\r\n\r\n debounceTimer = setTimeout(() => {\r\n takeSnapshot()\r\n }, debounceMs)\r\n },\r\n { deep }\r\n )\r\n\r\n // 初始快照\r\n takeSnapshot('初始状态')\r\n\r\n onUnmounted(() => {\r\n stopWatch()\r\n if (debounceTimer) {\r\n clearTimeout(debounceTimer)\r\n }\r\n })\r\n }\r\n\r\n return {\r\n snapshots,\r\n currentIndex,\r\n canUndo,\r\n canRedo,\r\n takeSnapshot,\r\n restoreSnapshot,\r\n undo,\r\n redo,\r\n goto,\r\n clearSnapshots,\r\n getDiff,\r\n exportSnapshots,\r\n importSnapshots,\r\n }\r\n}\r\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSubscribe.d.ts","sourceRoot":"","sources":["../../src/composables/useSubscribe.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,KAAK,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAG9D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5D,KAAK,EAAE,CAAC,GAAG;
|
|
1
|
+
{"version":3,"file":"useSubscribe.d.ts","sourceRoot":"","sources":["../../src/composables/useSubscribe.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,KAAK,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAG9D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5D,KAAK,EAAE,CAAC,GAAG;IACT,UAAU,CAAC,EAAE,CACX,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,EACvD,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KAC7B,MAAM,IAAI,CAAA;CAChB,EACD,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,EACvD,OAAO,GAAE,gBAAqB,GAC7B,MAAM,IAAI,CAyBZ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,KAAK,EAAE,CAAC,GAAG;IACT,SAAS,CAAC,EAAE,CACV,QAAQ,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,EAC1C,QAAQ,CAAC,EAAE,OAAO,KACf,MAAM,IAAI,CAAA;CAChB,EACD,QAAQ,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,EAC1C,QAAQ,UAAQ,GACf,MAAM,IAAI,CAeZ;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe;IACf,KAAK,EAAE,OAAO,CAAA;IACd,gBAAgB;IAChB,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,mBAAmB;IACnB,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAA;IACpD,kBAAkB;IAClB,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAA;CACtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,EACd,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,EACxD,OAAO,GAAE,gBAAqB,GAC7B,eAAe,CAkBjB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSubscribe.js","names":[],"sources":["../../src/composables/useSubscribe.ts"],"sourcesContent":["/**\n * useSubscribe Composable\n *\n * 提供 Store 订阅功能\n * @module composables/useSubscribe\n */\n\nimport type { Ref, WatchStopHandle } from 'vue'\nimport type { MutationInfo, SubscribeOptions } from '../types'\nimport { onUnmounted, watch } from 'vue'\n\n/**\n * 订阅 Store 状态变化\n *\n * @template S - 状态类型\n * @param store - Pinia Store 实例\n * @param callback - 回调函数\n * @param options - 订阅选项\n * @returns 取消订阅函数\n *\n * @example\n * ```typescript\n * const store = useCounterStore()\n *\n * // 订阅状态变化\n * const unsubscribe = useSubscribe(store, (mutation, state) => {\n * console.log('State changed:', mutation.type, state)\n * })\n *\n * // 取消订阅\n * unsubscribe()\n * ```\n */\nexport function useSubscribe<S extends Record<string, unknown>>(\n store: S & {
|
|
1
|
+
{"version":3,"file":"useSubscribe.js","names":[],"sources":["../../src/composables/useSubscribe.ts"],"sourcesContent":["/**\r\n * useSubscribe Composable\r\n *\r\n * 提供 Store 订阅功能\r\n * @module composables/useSubscribe\r\n */\r\n\r\nimport type { Ref, WatchStopHandle } from 'vue'\r\nimport type { MutationInfo, SubscribeOptions } from '../types'\r\nimport { onUnmounted, watch } from 'vue'\r\n\r\n/**\r\n * 订阅 Store 状态变化\r\n *\r\n * @template S - 状态类型\r\n * @param store - Pinia Store 实例\r\n * @param callback - 回调函数\r\n * @param options - 订阅选项\r\n * @returns 取消订阅函数\r\n *\r\n * @example\r\n * ```typescript\r\n * const store = useCounterStore()\r\n *\r\n * // 订阅状态变化\r\n * const unsubscribe = useSubscribe(store, (mutation, state) => {\r\n * console.log('State changed:', mutation.type, state)\r\n * })\r\n *\r\n * // 取消订阅\r\n * unsubscribe()\r\n * ```\r\n */\r\nexport function useSubscribe<S extends Record<string, unknown>>(\r\n store: S & {\r\n $subscribe?: (\r\n callback: (mutation: MutationInfo<S>, state: S) => void,\r\n options?: { detached?: boolean }\r\n ) => () => void\r\n },\r\n callback: (mutation: MutationInfo<S>, state: S) => void,\r\n options: SubscribeOptions = {}\r\n): () => void {\r\n const { detached = false } = options\r\n\r\n // 使用 Pinia 的 $subscribe\r\n if (store.$subscribe) {\r\n const unsubscribe = store.$subscribe(\r\n (mutation, state) => {\r\n callback(mutation as MutationInfo<S>, state as S)\r\n },\r\n { detached }\r\n )\r\n\r\n // 组件卸载时自动取消订阅\r\n if (!detached) {\r\n onUnmounted(() => {\r\n unsubscribe()\r\n })\r\n }\r\n\r\n return unsubscribe\r\n }\r\n\r\n // 如果不是 Pinia Store,返回空函数\r\n console.warn('[useSubscribe] Store 不支持 $subscribe 方法')\r\n return () => {}\r\n}\r\n\r\n/**\r\n * 订阅 Store Action 调用\r\n *\r\n * @template S - Store 类型\r\n * @param store - Pinia Store 实例\r\n * @param callback - 回调函数\r\n * @returns 取消订阅函数\r\n *\r\n * @example\r\n * ```typescript\r\n * const store = useCounterStore()\r\n *\r\n * // 订阅 Action 调用\r\n * useOnAction(store, ({ name, args, after, onError }) => {\r\n * console.log(`Action ${name} called with:`, args)\r\n *\r\n * after((result) => {\r\n * console.log(`Action ${name} finished with:`, result)\r\n * })\r\n *\r\n * onError((error) => {\r\n * console.error(`Action ${name} failed:`, error)\r\n * })\r\n * })\r\n * ```\r\n */\r\nexport function useOnAction<S extends Record<string, unknown>>(\r\n store: S & {\r\n $onAction?: (\r\n callback: (context: ActionContext) => void,\r\n detached?: boolean\r\n ) => () => void\r\n },\r\n callback: (context: ActionContext) => void,\r\n detached = false\r\n): () => void {\r\n if (store.$onAction) {\r\n const unsubscribe = store.$onAction(callback, detached)\r\n\r\n if (!detached) {\r\n onUnmounted(() => {\r\n unsubscribe()\r\n })\r\n }\r\n\r\n return unsubscribe\r\n }\r\n\r\n console.warn('[useOnAction] Store 不支持 $onAction 方法')\r\n return () => {}\r\n}\r\n\r\n/**\r\n * Action 上下文\r\n */\r\nexport interface ActionContext {\r\n /** Action 名称 */\r\n name: string\r\n /** Store 实例 */\r\n store: unknown\r\n /** Action 参数 */\r\n args: unknown[]\r\n /** Action 完成后回调 */\r\n after: (callback: (result: unknown) => void) => void\r\n /** Action 错误回调 */\r\n onError: (callback: (error: unknown) => void) => void\r\n}\r\n\r\n/**\r\n * 监听 ref 变化\r\n *\r\n * @template T - 值类型\r\n * @param source - 监听源\r\n * @param callback - 回调函数\r\n * @param options - 监听选项\r\n * @returns 停止监听函数\r\n */\r\nexport function useWatch<T>(\r\n source: Ref<T>,\r\n callback: (newValue: T, oldValue: T | undefined) => void,\r\n options: SubscribeOptions = {}\r\n): WatchStopHandle {\r\n const { immediate = false, deep = false, detached = false } = options\r\n\r\n const stop = watch(\r\n source,\r\n (newValue, oldValue) => {\r\n callback(newValue, oldValue)\r\n },\r\n { immediate, deep }\r\n )\r\n\r\n if (!detached) {\r\n onUnmounted(() => {\r\n stop()\r\n })\r\n }\r\n\r\n return stop\r\n}\r\n"],"mappings":";;;;;;;;;;;AAiCO,SAAS,aACd,OAMA,UACA,UAA4B,CAAC,GACjB;CACZ,MAAM,EAAE,WAAW,UAAU;CAG7B,IAAI,MAAM,YAAY;EACpB,MAAM,cAAc,MAAM,YACvB,UAAU,UAAU;GACnB,SAAS,UAA6B,KAAU;EAClD,GACA,EAAE,SAAS,CACb;EAGA,IAAI,CAAC,UACH,kBAAkB;GAChB,YAAY;EACd,CAAC;EAGH,OAAO;CACT;CAGA,QAAQ,KAAK,wCAAwC;CACrD,aAAa,CAAC;AAChB;AA4BO,SAAS,YACd,OAMA,UACA,WAAW,OACC;CACZ,IAAI,MAAM,WAAW;EACnB,MAAM,cAAc,MAAM,UAAU,UAAU,QAAQ;EAEtD,IAAI,CAAC,UACH,kBAAkB;GAChB,YAAY;EACd,CAAC;EAGH,OAAO;CACT;CAEA,QAAQ,KAAK,sCAAsC;CACnD,aAAa,CAAC;AAChB;AA2BO,SAAS,SACd,QACA,UACA,UAA4B,CAAC,GACZ;CACjB,MAAM,EAAE,YAAY,OAAO,OAAO,OAAO,WAAW,UAAU;CAE9D,MAAM,OAAO,MACX,SACC,UAAU,aAAa;EACtB,SAAS,UAAU,QAAQ;CAC7B,GACA;EAAE;EAAW;CAAK,CACpB;CAEA,IAAI,CAAC,UACH,kBAAkB;EAChB,KAAK;CACP,CAAC;CAGH,OAAO;AACT"}
|
|
@@ -5,4 +5,3 @@ import type { GlobalStoreAPI } from '../types';
|
|
|
5
5
|
export declare const STORE_API_SYMBOL: InjectionKey<GlobalStoreAPI>;
|
|
6
6
|
export declare const STORE_PINIA_SYMBOL: InjectionKey<Pinia>;
|
|
7
7
|
export declare const STORE_RUNTIME_SYMBOL: InjectionKey<StoreRuntime>;
|
|
8
|
-
//# sourceMappingURL=injection-keys.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"injection-keys.d.ts","sourceRoot":"","sources":["../../src/context/injection-keys.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAElC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAEvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAG9C,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"injection-keys.d.ts","sourceRoot":"","sources":["../../src/context/injection-keys.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAElC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAEvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAG9C,eAAO,MAAM,gBAAgB,EAExB,YAAY,CAAC,cAAc,CAAC,CAAA;AAEjC,eAAO,MAAM,kBAAkB,EAE1B,YAAY,CAAC,KAAK,CAAC,CAAA;AAExB,eAAO,MAAM,oBAAoB,EAE5B,YAAY,CAAC,YAAY,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"injection-keys.js","names":[],"sources":["../../src/context/injection-keys.ts"],"sourcesContent":["// 从 pinia 导入 Pinia 类型\nimport type { Pinia } from 'pinia'\n// 从 vue 导入 InjectionKey 类型,用于类型安全的依赖注入\nimport type { InjectionKey } from 'vue'\n// 导入 StoreRuntime 类型,表示 store 运行时实例\nimport type { StoreRuntime } from '../runtime/store-runtime'\n// 导入 GlobalStoreAPI 类型,表示全局 store API 接口\nimport type { GlobalStoreAPI } from '../types'\n\n// store 全局 API 的注入键符号,用于 provide/inject 传递 GlobalStoreAPI 实例\nexport const STORE_API_SYMBOL = Symbol('ldesign-store-api') as InjectionKey<GlobalStoreAPI>\n// store Pinia 实例的注入键符号,用于 provide/inject 传递 Pinia 实例\nexport const STORE_PINIA_SYMBOL = Symbol('ldesign-store-pinia') as InjectionKey<Pinia>\n// store 运行时实例的注入键符号,用于 provide/inject 传递 StoreRuntime 实例\nexport const STORE_RUNTIME_SYMBOL = Symbol('ldesign-store-runtime') as InjectionKey<StoreRuntime>\n"],"mappings":";;;;;;;;;;AAUO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"injection-keys.js","names":[],"sources":["../../src/context/injection-keys.ts"],"sourcesContent":["// 从 pinia 导入 Pinia 类型\r\nimport type { Pinia } from 'pinia'\r\n// 从 vue 导入 InjectionKey 类型,用于类型安全的依赖注入\r\nimport type { InjectionKey } from 'vue'\r\n// 导入 StoreRuntime 类型,表示 store 运行时实例\r\nimport type { StoreRuntime } from '../runtime/store-runtime'\r\n// 导入 GlobalStoreAPI 类型,表示全局 store API 接口\r\nimport type { GlobalStoreAPI } from '../types'\r\n\r\n// store 全局 API 的注入键符号,用于 provide/inject 传递 GlobalStoreAPI 实例\r\nexport const STORE_API_SYMBOL = Symbol(\r\n 'ldesign-store-api'\r\n) as InjectionKey<GlobalStoreAPI>\r\n// store Pinia 实例的注入键符号,用于 provide/inject 传递 Pinia 实例\r\nexport const STORE_PINIA_SYMBOL = Symbol(\r\n 'ldesign-store-pinia'\r\n) as InjectionKey<Pinia>\r\n// store 运行时实例的注入键符号,用于 provide/inject 传递 StoreRuntime 实例\r\nexport const STORE_RUNTIME_SYMBOL = Symbol(\r\n 'ldesign-store-runtime'\r\n) as InjectionKey<StoreRuntime>\r\n"],"mappings":";;;;;;;;;;AAUO,MAAM,mBAAmB,uBAC9B,mBACF;AAEO,MAAM,qBAAqB,uBAChC,qBACF;AAEO,MAAM,uBAAuB,uBAClC,uBACF"}
|
package/es/index.d.ts
CHANGED
|
@@ -19,6 +19,5 @@ export type { GlobalStoreAPI, MutationInfo, PersistOptions, StoreEnginePluginOpt
|
|
|
19
19
|
export { batch, combineSelectors, composePlugins, createAsyncAction, createBatchManager, createEventBus, createLoggerMiddleware, createMiddlewareManager, createPluginFactory, createPubSub, createPubSubWithValue, createSelector, createTimeTravelMiddleware, createValidationMiddleware, deepClone, deepMerge, defineStorePlugin, globalEventBus, } from '@composy/store-core';
|
|
20
20
|
export { createPinia, defineStore, getActivePinia, mapActions, mapGetters, mapState, mapStores, mapWritableState, setActivePinia, storeToRefs, } from 'pinia';
|
|
21
21
|
export type { Pinia, PiniaPlugin, PiniaPluginContext, StateTree, Store, StoreDefinition, StoreGeneric, StoreGetters, StoreState, } from 'pinia';
|
|
22
|
-
export declare const version = "
|
|
22
|
+
export declare const version = "0.0.1";
|
|
23
23
|
export declare const name = "@composy/store-vue";
|
|
24
|
-
//# sourceMappingURL=index.d.ts.map
|
package/es/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ***********************************
|
|
3
3
|
* @composy/store-vue v0.0.1 *
|
|
4
4
|
* Built with rollup *
|
|
5
|
-
* Build time: 2024-
|
|
5
|
+
* Build time: 2024-10-12 17:40:04 *
|
|
6
6
|
* Build mode: production *
|
|
7
7
|
* Minified: No *
|
|
8
8
|
* ***********************************
|
|
@@ -24,7 +24,7 @@ import { createStoreVuePlugin } from "./plugins/vue-plugin.js";
|
|
|
24
24
|
import { createPinia, defineStore, getActivePinia, mapActions, mapGetters, mapState, mapStores, mapWritableState, setActivePinia, storeToRefs } from "pinia";
|
|
25
25
|
import { batch, combineSelectors, composePlugins, createAsyncAction, createBatchManager, createEventBus, createLoggerMiddleware, createMiddlewareManager, createPluginFactory, createPubSub, createPubSubWithValue, createSelector, createTimeTravelMiddleware, createValidationMiddleware, deepClone, deepMerge, defineStorePlugin, globalEventBus } from "@composy/store-core";
|
|
26
26
|
//#region src/index.ts
|
|
27
|
-
const version = "
|
|
27
|
+
const version = "0.0.1";
|
|
28
28
|
const name = "@composy/store-vue";
|
|
29
29
|
//#endregion
|
|
30
30
|
export { batch, cleanupEnhancedPersist, combineSelectors, composePlugins, createAsyncAction, createBatchManager, createEnhancedPersistPlugin, createEventBus, createLoggerMiddleware, createMiddlewareManager, createPersistPlugin, createPinia, createPluginFactory, createPubSub, createPubSubWithValue, createSelector, createStoreEnginePlugin, createStoreGetters, createStoreRegistryPlugin, createStoreState, createStoreVuePlugin, createTimeTravelMiddleware, createValidationMiddleware, deepClone, deepMerge, defineSetupStore, defineStore, defineStorePlugin, email, getActivePinia, getGlobalStoreAPI, getStoreRegistry, globalEventBus, mapActions, mapGetters, mapState, mapStores, mapWritableState, maxLength, minLength, name, numberRange, pattern, required, setActivePinia, storeToRefs, useComputedStore, useFormStore, useOnAction, useOptimistic, usePersist, usePersistedRef, usePersistedRefWithTTL, usePersistedState, useSharedState, useSimpleStore, useStore, useStoreDevtools, useStoreSnapshot, useSubscribe, useWatch, version };
|
package/es/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @composy/store-vue - Vue 3 状态管理适配器\n *\n * 基于 Pinia 的增强版状态管理解决方案\n *\n * 主要功能:\n * - Pinia 集成与增强\n * - 状态持久化\n * - Engine 插件集成\n * - 丰富的 Composables\n *\n * @module @composy/store-vue\n */\n\n// === Composables ===\nexport {\n createStoreGetters,\n createStoreState,\n // Setup Store 助手\n defineSetupStore,\n email,\n maxLength,\n minLength,\n numberRange,\n pattern,\n // 验证规则工具\n required,\n useComputedStore,\n useFormStore,\n useOnAction,\n useOptimistic,\n usePersist,\n usePersistedRef,\n usePersistedRefWithTTL,\n usePersistedState,\n useSharedState,\n useSimpleStore,\n useStore,\n useStoreDevtools,\n useStoreSnapshot,\n useSubscribe,\n useWatch,\n} from './composables'\n\nexport type {\n ActionContext,\n EventLogEntry,\n FormFieldRules,\n FormFieldState,\n FormValidator,\n OptimisticEntry,\n PersistedStateOptions,\n PersistedStateReturn,\n StateDiff,\n StateSnapshot,\n StoreInfo,\n UseFormStoreOptions,\n UseFormStoreReturn,\n UseOptimisticReturn,\n UseSharedStateOptions,\n UseSharedStateReturn,\n UseStoreDevtoolsReturn,\n UseStoreSnapshotOptions,\n UseStoreSnapshotReturn,\n} from './composables'\n\n// === 插件 ===\nexport {\n cleanupEnhancedPersist,\n createEnhancedPersistPlugin,\n createPersistPlugin,\n createStoreEnginePlugin,\n createStoreRegistryPlugin,\n createStoreVuePlugin,\n getGlobalStoreAPI,\n getStoreRegistry,\n} from './plugins'\n\nexport type {\n EnhancedPersistOptions,\n EnhancedPersistPluginOptions,\n PersistPluginOptions,\n StoreVuePluginOptions,\n} from './plugins'\n\n// === 类型 ===\nexport type {\n GlobalStoreAPI,\n MutationInfo,\n PersistOptions,\n StoreEnginePluginOptions,\n StoreOptions,\n StoreProviderProps,\n SubscribeOptions,\n UsePersistOptions,\n UsePersistReturn,\n UseStoreReturn,\n} from './types'\n\n// === 从 @composy/store-core 重新导出常用功能 ===\nexport {\n // 核心\n batch,\n // 工具\n combineSelectors,\n // 插件\n composePlugins,\n createAsyncAction,\n createBatchManager,\n createEventBus,\n // 中间件\n createLoggerMiddleware,\n createMiddlewareManager,\n createPluginFactory,\n createPubSub,\n createPubSubWithValue,\n createSelector,\n createTimeTravelMiddleware,\n createValidationMiddleware,\n deepClone,\n deepMerge,\n defineStorePlugin,\n globalEventBus,\n} from '@composy/store-core'\n\n// === 从 Pinia 重新导出 ===\nexport {\n createPinia,\n defineStore,\n getActivePinia,\n mapActions,\n mapGetters,\n mapState,\n mapStores,\n mapWritableState,\n setActivePinia,\n storeToRefs,\n} from 'pinia'\n\nexport type {\n Pinia,\n PiniaPlugin,\n PiniaPluginContext,\n StateTree,\n Store,\n StoreDefinition,\n StoreGeneric,\n StoreGetters,\n StoreState,\n} from 'pinia'\n\n// === 版本信息 ===\nexport const version = '
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\r\n * @composy/store-vue - Vue 3 状态管理适配器\r\n *\r\n * 基于 Pinia 的增强版状态管理解决方案\r\n *\r\n * 主要功能:\r\n * - Pinia 集成与增强\r\n * - 状态持久化\r\n * - Engine 插件集成\r\n * - 丰富的 Composables\r\n *\r\n * @module @composy/store-vue\r\n */\r\n\r\n// === Composables ===\r\nexport {\r\n createStoreGetters,\r\n createStoreState,\r\n // Setup Store 助手\r\n defineSetupStore,\r\n email,\r\n maxLength,\r\n minLength,\r\n numberRange,\r\n pattern,\r\n // 验证规则工具\r\n required,\r\n useComputedStore,\r\n useFormStore,\r\n useOnAction,\r\n useOptimistic,\r\n usePersist,\r\n usePersistedRef,\r\n usePersistedRefWithTTL,\r\n usePersistedState,\r\n useSharedState,\r\n useSimpleStore,\r\n useStore,\r\n useStoreDevtools,\r\n useStoreSnapshot,\r\n useSubscribe,\r\n useWatch,\r\n} from './composables'\r\n\r\nexport type {\r\n ActionContext,\r\n EventLogEntry,\r\n FormFieldRules,\r\n FormFieldState,\r\n FormValidator,\r\n OptimisticEntry,\r\n PersistedStateOptions,\r\n PersistedStateReturn,\r\n StateDiff,\r\n StateSnapshot,\r\n StoreInfo,\r\n UseFormStoreOptions,\r\n UseFormStoreReturn,\r\n UseOptimisticReturn,\r\n UseSharedStateOptions,\r\n UseSharedStateReturn,\r\n UseStoreDevtoolsReturn,\r\n UseStoreSnapshotOptions,\r\n UseStoreSnapshotReturn,\r\n} from './composables'\r\n\r\n// === 插件 ===\r\nexport {\r\n cleanupEnhancedPersist,\r\n createEnhancedPersistPlugin,\r\n createPersistPlugin,\r\n createStoreEnginePlugin,\r\n createStoreRegistryPlugin,\r\n createStoreVuePlugin,\r\n getGlobalStoreAPI,\r\n getStoreRegistry,\r\n} from './plugins'\r\n\r\nexport type {\r\n EnhancedPersistOptions,\r\n EnhancedPersistPluginOptions,\r\n PersistPluginOptions,\r\n StoreVuePluginOptions,\r\n} from './plugins'\r\n\r\n// === 类型 ===\r\nexport type {\r\n GlobalStoreAPI,\r\n MutationInfo,\r\n PersistOptions,\r\n StoreEnginePluginOptions,\r\n StoreOptions,\r\n StoreProviderProps,\r\n SubscribeOptions,\r\n UsePersistOptions,\r\n UsePersistReturn,\r\n UseStoreReturn,\r\n} from './types'\r\n\r\n// === 从 @composy/store-core 重新导出常用功能 ===\r\nexport {\r\n // 核心\r\n batch,\r\n // 工具\r\n combineSelectors,\r\n // 插件\r\n composePlugins,\r\n createAsyncAction,\r\n createBatchManager,\r\n createEventBus,\r\n // 中间件\r\n createLoggerMiddleware,\r\n createMiddlewareManager,\r\n createPluginFactory,\r\n createPubSub,\r\n createPubSubWithValue,\r\n createSelector,\r\n createTimeTravelMiddleware,\r\n createValidationMiddleware,\r\n deepClone,\r\n deepMerge,\r\n defineStorePlugin,\r\n globalEventBus,\r\n} from '@composy/store-core'\r\n\r\n// === 从 Pinia 重新导出 ===\r\nexport {\r\n createPinia,\r\n defineStore,\r\n getActivePinia,\r\n mapActions,\r\n mapGetters,\r\n mapState,\r\n mapStores,\r\n mapWritableState,\r\n setActivePinia,\r\n storeToRefs,\r\n} from 'pinia'\r\n\r\nexport type {\r\n Pinia,\r\n PiniaPlugin,\r\n PiniaPluginContext,\r\n StateTree,\r\n Store,\r\n StoreDefinition,\r\n StoreGeneric,\r\n StoreGetters,\r\n StoreState,\r\n} from 'pinia'\r\n\r\n// === 版本信息 ===\r\nexport const version = '0.0.1'\r\nexport const name = '@composy/store-vue'\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAwJO,MAAM,UAAU;AAChB,MAAM,OAAO"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { StoreEnginePluginContextLike } from '@composy/store-core/engine';
|
|
2
2
|
import type { App } from 'vue';
|
|
3
3
|
import type { GlobalStoreAPI, StoreEnginePluginOptions } from '../types';
|
|
4
4
|
/**
|
|
@@ -33,4 +33,3 @@ export declare function getGlobalStoreAPI(): GlobalStoreAPI | null;
|
|
|
33
33
|
*/
|
|
34
34
|
export declare function createStoreEnginePlugin(options?: StoreEnginePluginOptions): StoreEnginePlugin;
|
|
35
35
|
export default createStoreEnginePlugin;
|
|
36
|
-
//# sourceMappingURL=engine-plugin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/engine-plugin.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"engine-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/engine-plugin.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAGV,4BAA4B,EAC7B,MAAM,4BAA4B,CAAA;AAEnC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAI9B,OAAO,KAAK,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAA;AAcxE;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,CAAC,OAAO,EAAE,4BAA4B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACjE,UAAU,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC9B,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE,4BAA4B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACrE;AAsCD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,IAAI,CAEzD;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B,GACrC,iBAAiB,CAyFnB;AAGD,eAAe,uBAAuB,CAAA"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ***********************************
|
|
3
3
|
* @composy/store-vue v0.0.1 *
|
|
4
4
|
* Built with rollup *
|
|
5
|
-
* Build time: 2024-
|
|
5
|
+
* Build time: 2024-10-12 17:40:04 *
|
|
6
6
|
* Build mode: production *
|
|
7
7
|
* Minified: No *
|
|
8
8
|
* ***********************************
|
|
@@ -23,7 +23,7 @@ function getGlobalStoreAPI() {
|
|
|
23
23
|
return getGlobalStoreAPI$1();
|
|
24
24
|
}
|
|
25
25
|
function createStoreEnginePlugin(options = {}) {
|
|
26
|
-
const { debug = false, dependencies = [], name = "store", version = "
|
|
26
|
+
const { debug = false, dependencies = [], name = "store", version = "0.0.1" } = options;
|
|
27
27
|
let bridge = null;
|
|
28
28
|
const createBridge = () => {
|
|
29
29
|
const activeBridge = createStoreEngineBridge({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine-plugin.js","names":[],"sources":["../../src/plugins/engine-plugin.ts"],"sourcesContent":["/* ============================================================\n * engine-plugin.ts\n * Store Engine 插件(Vue 适配层)\n * 将 core 层的 createStoreEngineBridge 包装为 Vue 友好的插件接口\n * 负责创建 bridge、管理运行时生命周期、连接 Vue App\n * ============================================================ */\n\n/* 从 core/engine 导入 bridge 相关类型 */\nimport type {\n StoreEngineBridge
|
|
1
|
+
{"version":3,"file":"engine-plugin.js","names":[],"sources":["../../src/plugins/engine-plugin.ts"],"sourcesContent":["/* ============================================================\r\n * engine-plugin.ts\r\n * Store Engine 插件(Vue 适配层)\r\n * 将 core 层的 createStoreEngineBridge 包装为 Vue 友好的插件接口\r\n * 负责创建 bridge、管理运行时生命周期、连接 Vue App\r\n * ============================================================ */\r\n\r\n/* 从 core/engine 导入 bridge 相关类型 */\r\nimport type {\r\n StoreEngineBridge /* 引擎桥接实例接口 */,\r\n StoreEngineLike /* 引擎抽象接口 */,\r\n StoreEnginePluginContextLike /* 插件上下文接口 */,\r\n} from '@composy/store-core/engine'\r\n/* Vue App 类型 */\r\nimport type { App } from 'vue'\r\n/* 本地 store 运行时类型 */\r\nimport type { StoreRuntime } from '../runtime/store-runtime'\r\n/* 全局 Store API 和插件选项类型 */\r\nimport type { GlobalStoreAPI, StoreEnginePluginOptions } from '../types'\r\n/* 从 core/engine 导入 bridge 创建函数 */\r\nimport { createStoreEngineBridge } from '@composy/store-core/engine'\r\n/* 导入 Vue App 运行时安装相关函数 */\r\nimport {\r\n getInstalledStoreRuntime,\r\n installStoreRuntime,\r\n} from '../runtime/install-store-app'\r\n/* 导入运行时创建函数和全局 API 获取函数 */\r\nimport {\r\n createStoreRuntime,\r\n getGlobalStoreAPI as getActiveStoreAPI,\r\n} from '../runtime/store-runtime'\r\n\r\n/**\r\n * Store Engine 插件接口\r\n * 定义了插件的元信息和生命周期方法\r\n * @property dependencies - 依赖的其他插件名称列表\r\n * @property name - 插件名称\r\n * @property version - 插件版本\r\n * @property install - 安装到引擎上下文(异步)\r\n * @property installVue - 安装到 Vue App\r\n * @property uninstall - 从引擎上下文卸载(异步)\r\n */\r\nexport interface StoreEnginePlugin {\r\n dependencies: string[]\r\n name: string\r\n version: string\r\n install: (context: StoreEnginePluginContextLike) => Promise<void>\r\n installVue: (app: App) => void\r\n uninstall: (context?: StoreEnginePluginContextLike) => Promise<void>\r\n}\r\n\r\n/**\r\n * 类型守卫:判断值是否为 Vue App 实例\r\n * 通过检查是否有 use 方法来识别\r\n */\r\nfunction isVueApp(value: unknown): value is App {\r\n return !!value && typeof (value as App).use === 'function'\r\n}\r\n\r\n/**\r\n * 从各种可能的来源解析 Vue App 实例\r\n * 按优先级尝试:payload.app → payload.framework.app → engine.getApp() → context.framework.app\r\n *\r\n * @param engine - Store 引擎实例\r\n * @param context - 插件上下文\r\n * @param payload - 安装时传入的额外数据\r\n * @returns 解析到的 Vue App 或 undefined\r\n */\r\nfunction resolveVueApp(\r\n engine: StoreEngineLike,\r\n context: StoreEnginePluginContextLike,\r\n payload?: unknown\r\n): App | undefined {\r\n /* 将 payload 宽松转为可能包含 app 字段的对象 */\r\n const appPayload = payload as\r\n { app?: unknown; framework?: { app?: unknown } } | undefined\r\n /* 按优先级尝试多个来源获取 app */\r\n const app =\r\n appPayload?.app ??\r\n appPayload?.framework?.app ??\r\n engine.getApp?.() ??\r\n context.framework?.app\r\n\r\n /* 使用类型守卫验证是否为 Vue App */\r\n return isVueApp(app) ? app : undefined\r\n}\r\n\r\n/**\r\n * 获取当前活跃的全局 Store API\r\n * @returns 全局 Store API 或 null(未初始化时)\r\n */\r\nexport function getGlobalStoreAPI(): GlobalStoreAPI | null {\r\n return getActiveStoreAPI()\r\n}\r\n\r\n/**\r\n * 创建 Store Engine 插件工厂函数\r\n * 这是 Vue 适配层的核心入口,将 core 层的 bridge 包装为 Vue 友好的插件\r\n *\r\n * @param options - 插件配置选项\r\n * @returns StoreEnginePlugin 插件实例\r\n */\r\nexport function createStoreEnginePlugin(\r\n options: StoreEnginePluginOptions = {}\r\n): StoreEnginePlugin {\r\n /* 解构配置选项,设置默认值 */\r\n const {\r\n debug = false /* 是否开启调试日志 */,\r\n dependencies = [] /* 依赖的插件列表 */,\r\n name = 'store' /* 插件名称 */,\r\n version = '0.0.1' /* 插件版本 */,\r\n } = options\r\n\r\n /* 延迟创建的 bridge 实例引用 */\r\n let bridge: StoreEngineBridge | null = null\r\n\r\n /**\r\n * 创建 bridge 的工厂函数\r\n * 内部调用 core 层的 createStoreEngineBridge,并传入 Vue 特定的实现\r\n */\r\n const createBridge = (): StoreEngineBridge => {\r\n const activeBridge = createStoreEngineBridge<StoreRuntime>({\r\n name /* 插件名称 */,\r\n version /* 插件版本 */,\r\n debug /* 调试模式 */,\r\n\r\n /* 创建运行时:传入所有 Vue 相关配置 */\r\n createRuntime: () =>\r\n createStoreRuntime({\r\n pinia: options.pinia /* 可选的外部 Pinia 实例 */,\r\n persist: options.persist /* 是否启用持久化 */,\r\n persistOptions: options.persistOptions /* 持久化选项 */,\r\n globalProperties: options.globalProperties /* 是否挂载到全局属性 */,\r\n provide: options.provide /* 是否通过 provide 注入 */,\r\n debug,\r\n }),\r\n\r\n /* 将运行时安装到 Vue App */\r\n attachToApp: (app, runtime) => installStoreRuntime(app as App, runtime),\r\n\r\n /* 从 Vue App 获取已安装的运行时 */\r\n getRuntimeFromApp: app => getInstalledStoreRuntime(app as App),\r\n\r\n /* Vue App 类型守卫 */\r\n isApp: isVueApp,\r\n\r\n /* Vue App 解析函数 */\r\n resolveApp: resolveVueApp,\r\n })\r\n\r\n bridge = activeBridge\r\n return activeBridge\r\n }\r\n\r\n /**\r\n * 获取 bridge 实例(懒创建)\r\n * 首次调用时自动创建\r\n */\r\n const getBridge = (): StoreEngineBridge => {\r\n return bridge ?? createBridge()\r\n }\r\n\r\n /* ── 返回插件对象 ── */\r\n return {\r\n name /* 插件名称 */,\r\n version /* 插件版本 */,\r\n dependencies /* 插件依赖 */,\r\n\r\n /**\r\n * 安装到引擎上下文(异步)\r\n * 通过 bridge 将 store 能力注册到引擎\r\n */\r\n async install(context: StoreEnginePluginContextLike): Promise<void> {\r\n await getBridge().install(context)\r\n },\r\n\r\n /**\r\n * 安装到 Vue App\r\n * 将 store 运行时连接到 Vue 应用实例\r\n */\r\n installVue(app: App): void {\r\n getBridge().attachToApp(app)\r\n },\r\n\r\n /**\r\n * 从引擎上下文卸载(异步)\r\n * 清理 bridge 引用\r\n */\r\n async uninstall(context?: StoreEnginePluginContextLike): Promise<void> {\r\n await getBridge().uninstall(context)\r\n bridge = null /* 释放 bridge 引用,允许垃圾回收 */\r\n },\r\n }\r\n}\r\n\r\n/* 默认导出工厂函数 */\r\nexport default createStoreEnginePlugin\r\n"],"mappings":";;;;;;;;;;;;;AAuDA,SAAS,SAAS,OAA8B;CAC9C,OAAO,CAAC,CAAC,SAAS,OAAQ,MAAc,QAAQ;AAClD;AAWA,SAAS,cACP,QACA,SACA,SACiB;CAEjB,MAAM,aAAa;CAGnB,MAAM,MACJ,YAAY,OACZ,YAAY,WAAW,OACvB,OAAO,SAAS,KAChB,QAAQ,WAAW;CAGrB,OAAO,SAAS,GAAG,IAAI,MAAM,KAAA;AAC/B;AAMO,SAAS,oBAA2C;CACzD,OAAO,oBAAkB;AAC3B;AASO,SAAS,wBACd,UAAoC,CAAC,GAClB;CAEnB,MAAM,EACJ,QAAQ,OACR,eAAe,CAAC,GAChB,OAAO,SACP,UAAU,YACR;CAGJ,IAAI,SAAmC;CAMvC,MAAM,qBAAwC;EAC5C,MAAM,eAAe,wBAAsC;GACzD;GACA;GACA;GAGA,qBACE,mBAAmB;IACjB,OAAO,QAAQ;IACf,SAAS,QAAQ;IACjB,gBAAgB,QAAQ;IACxB,kBAAkB,QAAQ;IAC1B,SAAS,QAAQ;IACjB;GACF,CAAC;GAGH,cAAc,KAAK,YAAY,oBAAoB,KAAY,OAAO;GAGtE,oBAAmB,QAAO,yBAAyB,GAAU;GAG7D,OAAO;GAGP,YAAY;EACd,CAAC;EAED,SAAS;EACT,OAAO;CACT;CAMA,MAAM,kBAAqC;EACzC,OAAO,UAAU,aAAa;CAChC;CAGA,OAAO;EACL;EACA;EACA;;;;;EAMA,MAAM,QAAQ,SAAsD;GAClE,MAAM,UAAU,CAAA,CAAE,QAAQ,OAAO;EACnC;;;;;EAMA,WAAW,KAAgB;GACzB,UAAU,CAAA,CAAE,YAAY,GAAG;EAC7B;;;;;EAMA,MAAM,UAAU,SAAuD;GACrE,MAAM,UAAU,CAAA,CAAE,UAAU,OAAO;GACnC,SAAS;EACX;CACF;AACF"}
|
package/es/plugins/index.d.ts
CHANGED
|
@@ -4,4 +4,3 @@ export type { EnhancedPersistOptions, EnhancedPersistPluginOptions, PersistPlugi
|
|
|
4
4
|
export { createRegistryMethods, createStoreRegistry, createStoreRegistryPlugin, getStoreRegistry, setGlobalStoreRegistry, } from './registry-plugin';
|
|
5
5
|
export { createStoreVuePlugin, } from './vue-plugin';
|
|
6
6
|
export type { StoreVuePluginOptions } from './vue-plugin';
|
|
7
|
-
//# sourceMappingURL=index.d.ts.map
|
package/es/plugins/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pinia-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/pinia-plugin.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAoC,MAAM,OAAO,CAAA;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"pinia-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/pinia-plugin.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAoC,MAAM,OAAO,CAAA;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAe9C,KAAK,cAAc,GAAG,cAAc,GAAG,gBAAgB,CAAA;AAkBvD;;;;GAIG;AACH,UAAU,uBAAuB;IAC/B,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,cAAc,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAGD,MAAM,WAAW,4BAA6B,SAAQ,oBAAoB;CAAG;AAE7E;;;GAGG;AACH,MAAM,WAAW,sBAAuB,SAAQ,cAAc;IAC5D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AAwHD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,oBAAyB,GACjC,uBAAuB,CA6OzB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,oBAAyB,GACjC,WAAW,CAEb;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,4BAAiC,GACzC,WAAW,CAEb;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ***********************************
|
|
3
3
|
* @composy/store-vue v0.0.1 *
|
|
4
4
|
* Built with rollup *
|
|
5
|
-
* Build time: 2024-
|
|
5
|
+
* Build time: 2024-10-12 17:40:04 *
|
|
6
6
|
* Build mode: production *
|
|
7
7
|
* Minified: No *
|
|
8
8
|
* ***********************************
|
|
@@ -13,7 +13,11 @@ const STORE_PERSIST_DISPOSE_KEY = /* @__PURE__ */ Symbol("ldesign-store-persist-
|
|
|
13
13
|
const activePersistControllers = /* @__PURE__ */ new Set();
|
|
14
14
|
function getStorage(type) {
|
|
15
15
|
if (typeof window === "undefined") return null;
|
|
16
|
-
|
|
16
|
+
try {
|
|
17
|
+
return type === "localStorage" ? window.localStorage : window.sessionStorage;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
function normalizePersistConfig(value) {
|
|
19
23
|
if (!value) return null;
|
|
@@ -58,17 +62,21 @@ function patchPersistDispose(store, cleanup) {
|
|
|
58
62
|
function createPersistPluginController(options = {}) {
|
|
59
63
|
const { storage: defaultStorage = "localStorage", keyPrefix = "ldesign-store:", debounce: debounceDelay = 100, debug = false, syncTabs: defaultSyncTabs = false, defaultTTL } = options;
|
|
60
64
|
const cleanupMap = /* @__PURE__ */ new Map();
|
|
65
|
+
let isDisposed = false;
|
|
61
66
|
const dispose = () => {
|
|
67
|
+
if (isDisposed) return;
|
|
68
|
+
isDisposed = true;
|
|
62
69
|
cleanupMap.forEach((cleanup) => cleanup());
|
|
63
70
|
cleanupMap.clear();
|
|
64
71
|
activePersistControllers.delete(dispose);
|
|
65
72
|
};
|
|
66
73
|
activePersistControllers.add(dispose);
|
|
67
74
|
const plugin = (context) => {
|
|
75
|
+
if (isDisposed) return;
|
|
68
76
|
const { store, options: storeOptions } = context;
|
|
69
77
|
const config = normalizePersistConfig(storeOptions.persist);
|
|
70
78
|
if (!config) return;
|
|
71
|
-
const storage = getStorage(config.storage
|
|
79
|
+
const storage = getStorage(config.storage ?? defaultStorage);
|
|
72
80
|
const serializer = config.serializer?.serialize ?? JSON.stringify;
|
|
73
81
|
const deserialize = config.serializer?.deserialize ?? JSON.parse;
|
|
74
82
|
const syncTabs = config.syncTabs ?? defaultSyncTabs;
|
|
@@ -128,7 +136,16 @@ function createPersistPluginController(options = {}) {
|
|
|
128
136
|
let stopStorageSync = () => {};
|
|
129
137
|
if (syncTabs && typeof window !== "undefined") {
|
|
130
138
|
const handleStorage = (event) => {
|
|
131
|
-
if (event.key !== key
|
|
139
|
+
if (event.key !== key) return;
|
|
140
|
+
if (event.newValue === null) {
|
|
141
|
+
isRestoring = true;
|
|
142
|
+
try {
|
|
143
|
+
store.$reset?.();
|
|
144
|
+
} finally {
|
|
145
|
+
isRestoring = false;
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
132
149
|
try {
|
|
133
150
|
const storageItem = JSON.parse(event.newValue);
|
|
134
151
|
if (storageItem.expiresAt && Date.now() > storageItem.expiresAt) return;
|
|
@@ -146,9 +163,10 @@ function createPersistPluginController(options = {}) {
|
|
|
146
163
|
saveState.cancel();
|
|
147
164
|
stopSubscribe();
|
|
148
165
|
stopStorageSync();
|
|
149
|
-
cleanupMap.delete(
|
|
166
|
+
cleanupMap.delete(store);
|
|
167
|
+
store[STORE_PERSIST_DISPOSE_KEY]?.();
|
|
150
168
|
};
|
|
151
|
-
cleanupMap.set(
|
|
169
|
+
cleanupMap.set(store, cleanup);
|
|
152
170
|
patchPersistDispose(store, cleanup);
|
|
153
171
|
restoreState();
|
|
154
172
|
};
|