@composy/store-core 0.0.1
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 +5 -0
- package/README.md +172 -0
- package/dist/index.js +1852 -0
- package/dist/index.js.map +1 -0
- package/dist/index.min.js +1082 -0
- package/dist/index.min.js.map +1 -0
- package/es/core/async-action.d.ts +95 -0
- package/es/core/async-action.d.ts.map +1 -0
- package/es/core/async-action.js +116 -0
- package/es/core/async-action.js.map +1 -0
- package/es/core/base-store.d.ts +85 -0
- package/es/core/base-store.d.ts.map +1 -0
- package/es/core/base-store.js +185 -0
- package/es/core/base-store.js.map +1 -0
- package/es/core/batch-manager.d.ts +46 -0
- package/es/core/batch-manager.d.ts.map +1 -0
- package/es/core/batch-manager.js +64 -0
- package/es/core/batch-manager.js.map +1 -0
- package/es/core/create-store.d.ts +66 -0
- package/es/core/create-store.d.ts.map +1 -0
- package/es/core/create-store.js +29 -0
- package/es/core/create-store.js.map +1 -0
- package/es/core/event-bus.d.ts +34 -0
- package/es/core/event-bus.d.ts.map +1 -0
- package/es/core/event-bus.js +71 -0
- package/es/core/event-bus.js.map +1 -0
- package/es/core/index.d.ts +14 -0
- package/es/core/index.d.ts.map +1 -0
- package/es/core/index.js +17 -0
- package/es/core/pub-sub.d.ts +41 -0
- package/es/core/pub-sub.d.ts.map +1 -0
- package/es/core/pub-sub.js +71 -0
- package/es/core/pub-sub.js.map +1 -0
- package/es/engine/index.d.ts +10 -0
- package/es/engine/index.d.ts.map +1 -0
- package/es/engine/index.js +12 -0
- package/es/engine/plugin.d.ts +20 -0
- package/es/engine/plugin.d.ts.map +1 -0
- package/es/engine/plugin.js +153 -0
- package/es/engine/plugin.js.map +1 -0
- package/es/engine/types.d.ts +81 -0
- package/es/engine/types.d.ts.map +1 -0
- package/es/engine/types.js +10 -0
- package/es/errors/index.d.ts +208 -0
- package/es/errors/index.d.ts.map +1 -0
- package/es/errors/index.js +217 -0
- package/es/errors/index.js.map +1 -0
- package/es/index.d.ts +27 -0
- package/es/index.d.ts.map +1 -0
- package/es/index.js +34 -0
- package/es/index.js.map +1 -0
- package/es/middleware/index.d.ts +13 -0
- package/es/middleware/index.d.ts.map +1 -0
- package/es/middleware/index.js +16 -0
- package/es/middleware/logger.d.ts +67 -0
- package/es/middleware/logger.d.ts.map +1 -0
- package/es/middleware/logger.js +136 -0
- package/es/middleware/logger.js.map +1 -0
- package/es/middleware/middleware-manager.d.ts +40 -0
- package/es/middleware/middleware-manager.d.ts.map +1 -0
- package/es/middleware/middleware-manager.js +79 -0
- package/es/middleware/middleware-manager.js.map +1 -0
- package/es/middleware/persist.d.ts +34 -0
- package/es/middleware/persist.d.ts.map +1 -0
- package/es/middleware/persist.js +102 -0
- package/es/middleware/persist.js.map +1 -0
- package/es/middleware/time-travel.d.ts +30 -0
- package/es/middleware/time-travel.d.ts.map +1 -0
- package/es/middleware/time-travel.js +151 -0
- package/es/middleware/time-travel.js.map +1 -0
- package/es/middleware/validation.d.ts +100 -0
- package/es/middleware/validation.d.ts.map +1 -0
- package/es/middleware/validation.js +74 -0
- package/es/middleware/validation.js.map +1 -0
- package/es/plugins/define-plugin.d.ts +74 -0
- package/es/plugins/define-plugin.d.ts.map +1 -0
- package/es/plugins/define-plugin.js +52 -0
- package/es/plugins/define-plugin.js.map +1 -0
- package/es/plugins/index.d.ts +8 -0
- package/es/plugins/index.d.ts.map +1 -0
- package/es/plugins/index.js +13 -0
- package/es/plugins/plugin-manager.d.ts +35 -0
- package/es/plugins/plugin-manager.d.ts.map +1 -0
- package/es/plugins/plugin-manager.js +77 -0
- package/es/plugins/plugin-manager.js.map +1 -0
- package/es/types/index.d.ts +12 -0
- package/es/types/index.d.ts.map +1 -0
- package/es/types/index.js +10 -0
- package/es/types/middleware.d.ts +172 -0
- package/es/types/middleware.d.ts.map +1 -0
- package/es/types/middleware.js +10 -0
- package/es/types/plugin.d.ts +136 -0
- package/es/types/plugin.d.ts.map +1 -0
- package/es/types/plugin.js +10 -0
- package/es/types/store.d.ts +198 -0
- package/es/types/store.d.ts.map +1 -0
- package/es/types/store.js +10 -0
- package/es/types/subscription.d.ts +158 -0
- package/es/types/subscription.d.ts.map +1 -0
- package/es/types/subscription.js +10 -0
- package/es/types/utility.d.ts +312 -0
- package/es/types/utility.d.ts.map +1 -0
- package/es/types/utility.js +10 -0
- package/es/utils/debounce-throttle.d.ts +86 -0
- package/es/utils/debounce-throttle.d.ts.map +1 -0
- package/es/utils/debounce-throttle.js +86 -0
- package/es/utils/debounce-throttle.js.map +1 -0
- package/es/utils/deep-clone.d.ts +143 -0
- package/es/utils/deep-clone.d.ts.map +1 -0
- package/es/utils/deep-clone.js +193 -0
- package/es/utils/deep-clone.js.map +1 -0
- package/es/utils/index.d.ts +13 -0
- package/es/utils/index.d.ts.map +1 -0
- package/es/utils/index.js +14 -0
- package/es/utils/selector.d.ts +213 -0
- package/es/utils/selector.d.ts.map +1 -0
- package/es/utils/selector.js +151 -0
- package/es/utils/selector.js.map +1 -0
- package/esm/core/async-action.d.ts +95 -0
- package/esm/core/async-action.js +116 -0
- package/esm/core/async-action.js.map +1 -0
- package/esm/core/base-store.d.ts +85 -0
- package/esm/core/base-store.js +185 -0
- package/esm/core/base-store.js.map +1 -0
- package/esm/core/batch-manager.d.ts +46 -0
- package/esm/core/batch-manager.js +64 -0
- package/esm/core/batch-manager.js.map +1 -0
- package/esm/core/create-store.d.ts +66 -0
- package/esm/core/create-store.js +29 -0
- package/esm/core/create-store.js.map +1 -0
- package/esm/core/event-bus.d.ts +34 -0
- package/esm/core/event-bus.js +71 -0
- package/esm/core/event-bus.js.map +1 -0
- package/esm/core/index.d.ts +14 -0
- package/esm/core/index.js +17 -0
- package/esm/core/pub-sub.d.ts +41 -0
- package/esm/core/pub-sub.js +71 -0
- package/esm/core/pub-sub.js.map +1 -0
- package/esm/engine/index.d.ts +10 -0
- package/esm/engine/index.js +12 -0
- package/esm/engine/plugin.d.ts +20 -0
- package/esm/engine/plugin.js +153 -0
- package/esm/engine/plugin.js.map +1 -0
- package/esm/engine/types.d.ts +81 -0
- package/esm/errors/index.d.ts +208 -0
- package/esm/errors/index.js +217 -0
- package/esm/errors/index.js.map +1 -0
- package/esm/index.d.ts +27 -0
- package/esm/index.js +36 -0
- package/esm/index.js.map +1 -0
- package/esm/middleware/index.d.ts +13 -0
- package/esm/middleware/logger.d.ts +67 -0
- package/esm/middleware/logger.js +119 -0
- package/esm/middleware/logger.js.map +1 -0
- package/esm/middleware/middleware-manager.d.ts +40 -0
- package/esm/middleware/middleware-manager.js +79 -0
- package/esm/middleware/middleware-manager.js.map +1 -0
- package/esm/middleware/persist.d.ts +34 -0
- package/esm/middleware/persist.js +102 -0
- package/esm/middleware/persist.js.map +1 -0
- package/esm/middleware/time-travel.d.ts +30 -0
- package/esm/middleware/time-travel.js +151 -0
- package/esm/middleware/time-travel.js.map +1 -0
- package/esm/middleware/validation.d.ts +100 -0
- package/esm/middleware/validation.js +74 -0
- package/esm/middleware/validation.js.map +1 -0
- package/esm/plugins/define-plugin.d.ts +74 -0
- package/esm/plugins/define-plugin.js +52 -0
- package/esm/plugins/define-plugin.js.map +1 -0
- package/esm/plugins/index.d.ts +8 -0
- package/esm/plugins/plugin-manager.d.ts +35 -0
- package/esm/plugins/plugin-manager.js +77 -0
- package/esm/plugins/plugin-manager.js.map +1 -0
- package/esm/types/index.d.ts +12 -0
- package/esm/types/middleware.d.ts +172 -0
- package/esm/types/plugin.d.ts +136 -0
- package/esm/types/store.d.ts +198 -0
- package/esm/types/subscription.d.ts +158 -0
- package/esm/types/utility.d.ts +312 -0
- package/esm/utils/debounce-throttle.d.ts +86 -0
- package/esm/utils/debounce-throttle.js +86 -0
- package/esm/utils/debounce-throttle.js.map +1 -0
- package/esm/utils/deep-clone.d.ts +143 -0
- package/esm/utils/deep-clone.js +193 -0
- package/esm/utils/deep-clone.js.map +1 -0
- package/esm/utils/index.d.ts +13 -0
- package/esm/utils/selector.d.ts +213 -0
- package/esm/utils/selector.js +151 -0
- package/esm/utils/selector.js.map +1 -0
- package/lib/core/async-action.cjs +118 -0
- package/lib/core/async-action.cjs.map +1 -0
- package/lib/core/async-action.d.ts +95 -0
- package/lib/core/base-store.cjs +187 -0
- package/lib/core/base-store.cjs.map +1 -0
- package/lib/core/base-store.d.ts +85 -0
- package/lib/core/batch-manager.cjs +69 -0
- package/lib/core/batch-manager.cjs.map +1 -0
- package/lib/core/batch-manager.d.ts +46 -0
- package/lib/core/create-store.cjs +31 -0
- package/lib/core/create-store.cjs.map +1 -0
- package/lib/core/create-store.d.ts +66 -0
- package/lib/core/event-bus.cjs +74 -0
- package/lib/core/event-bus.cjs.map +1 -0
- package/lib/core/event-bus.d.ts +34 -0
- package/lib/core/index.cjs +29 -0
- package/lib/core/index.d.ts +14 -0
- package/lib/core/pub-sub.cjs +74 -0
- package/lib/core/pub-sub.cjs.map +1 -0
- package/lib/core/pub-sub.d.ts +41 -0
- package/lib/engine/index.cjs +16 -0
- package/lib/engine/index.d.ts +10 -0
- package/lib/engine/plugin.cjs +157 -0
- package/lib/engine/plugin.cjs.map +1 -0
- package/lib/engine/plugin.d.ts +20 -0
- package/lib/engine/types.cjs +12 -0
- package/lib/engine/types.d.ts +81 -0
- package/lib/errors/index.cjs +233 -0
- package/lib/errors/index.cjs.map +1 -0
- package/lib/errors/index.d.ts +208 -0
- package/lib/index.cjs +101 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.ts +27 -0
- package/lib/middleware/index.cjs +28 -0
- package/lib/middleware/index.d.ts +13 -0
- package/lib/middleware/logger.cjs +140 -0
- package/lib/middleware/logger.cjs.map +1 -0
- package/lib/middleware/logger.d.ts +67 -0
- package/lib/middleware/middleware-manager.cjs +83 -0
- package/lib/middleware/middleware-manager.cjs.map +1 -0
- package/lib/middleware/middleware-manager.d.ts +40 -0
- package/lib/middleware/persist.cjs +105 -0
- package/lib/middleware/persist.cjs.map +1 -0
- package/lib/middleware/persist.d.ts +34 -0
- package/lib/middleware/time-travel.cjs +154 -0
- package/lib/middleware/time-travel.cjs.map +1 -0
- package/lib/middleware/time-travel.d.ts +30 -0
- package/lib/middleware/validation.cjs +76 -0
- package/lib/middleware/validation.cjs.map +1 -0
- package/lib/middleware/validation.d.ts +100 -0
- package/lib/package.json +3 -0
- package/lib/plugins/define-plugin.cjs +56 -0
- package/lib/plugins/define-plugin.cjs.map +1 -0
- package/lib/plugins/define-plugin.d.ts +74 -0
- package/lib/plugins/index.cjs +20 -0
- package/lib/plugins/index.d.ts +8 -0
- package/lib/plugins/plugin-manager.cjs +81 -0
- package/lib/plugins/plugin-manager.cjs.map +1 -0
- package/lib/plugins/plugin-manager.d.ts +35 -0
- package/lib/types/index.cjs +12 -0
- package/lib/types/index.d.ts +12 -0
- package/lib/types/middleware.cjs +12 -0
- package/lib/types/middleware.d.ts +172 -0
- package/lib/types/plugin.cjs +12 -0
- package/lib/types/plugin.d.ts +136 -0
- package/lib/types/store.cjs +12 -0
- package/lib/types/store.d.ts +198 -0
- package/lib/types/subscription.cjs +12 -0
- package/lib/types/subscription.d.ts +158 -0
- package/lib/types/utility.cjs +12 -0
- package/lib/types/utility.d.ts +312 -0
- package/lib/utils/debounce-throttle.cjs +89 -0
- package/lib/utils/debounce-throttle.cjs.map +1 -0
- package/lib/utils/debounce-throttle.d.ts +86 -0
- package/lib/utils/deep-clone.cjs +202 -0
- package/lib/utils/deep-clone.cjs.map +1 -0
- package/lib/utils/deep-clone.d.ts +143 -0
- package/lib/utils/index.cjs +34 -0
- package/lib/utils/index.d.ts +13 -0
- package/lib/utils/selector.cjs +161 -0
- package/lib/utils/selector.cjs.map +1 -0
- package/lib/utils/selector.d.ts +213 -0
- package/package.json +155 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* ***********************************
|
|
3
|
+
* @composy/store-core v0.0.1 *
|
|
4
|
+
* Built with rollup *
|
|
5
|
+
* Build time: 2024-08-17 11:27:33 *
|
|
6
|
+
* Build mode: production *
|
|
7
|
+
* Minified: No *
|
|
8
|
+
* ***********************************
|
|
9
|
+
*/
|
|
10
|
+
"use strict";
|
|
11
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
12
|
+
const require_deep_clone = require("../utils/deep-clone.cjs");
|
|
13
|
+
//#region src/middleware/time-travel.ts
|
|
14
|
+
const defaultOptions = {
|
|
15
|
+
maxHistory: 100,
|
|
16
|
+
enabled: true
|
|
17
|
+
};
|
|
18
|
+
function createTimeTravelMiddleware(options = {}) {
|
|
19
|
+
const opts = {
|
|
20
|
+
...defaultOptions,
|
|
21
|
+
...options
|
|
22
|
+
};
|
|
23
|
+
const maxHistory = opts.maxHistory ?? defaultOptions.maxHistory ?? 100;
|
|
24
|
+
const history = [];
|
|
25
|
+
let currentIndex = -1;
|
|
26
|
+
let isTraveling = false;
|
|
27
|
+
let restoreCallback = () => {};
|
|
28
|
+
const addSnapshot = (snapshot) => {
|
|
29
|
+
if (!opts.enabled || isTraveling) return;
|
|
30
|
+
if (currentIndex < history.length - 1) history.splice(currentIndex + 1);
|
|
31
|
+
history.push(snapshot);
|
|
32
|
+
currentIndex = history.length - 1;
|
|
33
|
+
if (history.length > maxHistory) {
|
|
34
|
+
history.shift();
|
|
35
|
+
currentIndex--;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const controller = {
|
|
39
|
+
/**
|
|
40
|
+
* 撤销操作
|
|
41
|
+
* 将当前指针前移一步,恢复到上一个历史快照状态
|
|
42
|
+
*/
|
|
43
|
+
undo: () => {
|
|
44
|
+
if (!controller.canUndo()) return;
|
|
45
|
+
isTraveling = true;
|
|
46
|
+
currentIndex--;
|
|
47
|
+
const snapshot = history[currentIndex];
|
|
48
|
+
restoreCallback(snapshot.storeId, require_deep_clone.deepClone(snapshot.state));
|
|
49
|
+
isTraveling = false;
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* 重做操作
|
|
53
|
+
* 将当前指针后移一步,恢复到下一个历史快照状态
|
|
54
|
+
*/
|
|
55
|
+
redo: () => {
|
|
56
|
+
if (!controller.canRedo()) return;
|
|
57
|
+
isTraveling = true;
|
|
58
|
+
currentIndex++;
|
|
59
|
+
const snapshot = history[currentIndex];
|
|
60
|
+
restoreCallback(snapshot.storeId, require_deep_clone.deepClone(snapshot.state));
|
|
61
|
+
isTraveling = false;
|
|
62
|
+
},
|
|
63
|
+
/**
|
|
64
|
+
* 跳转到指定历史索引
|
|
65
|
+
* 将当前指针直接移动到指定位置,恢复对应的快照状态
|
|
66
|
+
*
|
|
67
|
+
* @param index - 目标历史索引(0-based)
|
|
68
|
+
*/
|
|
69
|
+
goto: (index) => {
|
|
70
|
+
if (index < 0 || index >= history.length) return;
|
|
71
|
+
isTraveling = true;
|
|
72
|
+
currentIndex = index;
|
|
73
|
+
const snapshot = history[currentIndex];
|
|
74
|
+
restoreCallback(snapshot.storeId, require_deep_clone.deepClone(snapshot.state));
|
|
75
|
+
isTraveling = false;
|
|
76
|
+
},
|
|
77
|
+
/**
|
|
78
|
+
* 获取完整的快照历史(返回深拷贝,防止外部修改内部历史记录)
|
|
79
|
+
* @returns 历史快照数组的深拷贝
|
|
80
|
+
*/
|
|
81
|
+
getHistory: () => require_deep_clone.deepClone(history),
|
|
82
|
+
/**
|
|
83
|
+
* 获取当前快照指针索引
|
|
84
|
+
* @returns 当前索引值,-1 表示无快照
|
|
85
|
+
*/
|
|
86
|
+
getCurrentIndex: () => currentIndex,
|
|
87
|
+
/**
|
|
88
|
+
* 判断是否可以撤销
|
|
89
|
+
* 当前索引大于 0 时,说明前面还有历史快照
|
|
90
|
+
* @returns 是否可以执行 undo 操作
|
|
91
|
+
*/
|
|
92
|
+
canUndo: () => currentIndex > 0,
|
|
93
|
+
/**
|
|
94
|
+
* 判断是否可以重做
|
|
95
|
+
* 当前索引小于历史长度 - 1 时,说明后面还有快照
|
|
96
|
+
* @returns 是否可以执行 redo 操作
|
|
97
|
+
*/
|
|
98
|
+
canRedo: () => currentIndex < history.length - 1,
|
|
99
|
+
/**
|
|
100
|
+
* 清除所有历史快照并重置指针
|
|
101
|
+
* 通常在 Store 销毁或需要重置历史时调用
|
|
102
|
+
*/
|
|
103
|
+
clear: () => {
|
|
104
|
+
history.length = 0;
|
|
105
|
+
currentIndex = -1;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
controller.__setRestoreCallback = (callback) => {
|
|
109
|
+
restoreCallback = callback;
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
middleware: {
|
|
113
|
+
name: "time-travel",
|
|
114
|
+
priority: 50,
|
|
115
|
+
/**
|
|
116
|
+
* 中间件处理函数
|
|
117
|
+
* 只拦截 "state:" 类型的操作(状态变更操作),其他类型直接放行
|
|
118
|
+
* 正在时间旅行时也不记录快照,避免循环
|
|
119
|
+
*
|
|
120
|
+
* @param context - 中间件上下文,包含 storeId、操作类型、状态等信息
|
|
121
|
+
* @param next - 调用下一个中间件或最终操作的函数
|
|
122
|
+
*/
|
|
123
|
+
handler: async (context, next) => {
|
|
124
|
+
const { storeId, type, state } = context;
|
|
125
|
+
if (!type.startsWith("state:") || isTraveling) {
|
|
126
|
+
await next();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const snapshot = {
|
|
130
|
+
timestamp: Date.now(),
|
|
131
|
+
state: require_deep_clone.deepClone(state),
|
|
132
|
+
type,
|
|
133
|
+
storeId
|
|
134
|
+
};
|
|
135
|
+
await next();
|
|
136
|
+
addSnapshot(snapshot);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
controller
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function setRestoreCallback(controller, callback) {
|
|
143
|
+
const controllerWithSetter = controller;
|
|
144
|
+
if (typeof controllerWithSetter.__setRestoreCallback === "function") {
|
|
145
|
+
controllerWithSetter.__setRestoreCallback(callback);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
console.warn("[TimeTravel] 请使用 createTimeTravelMiddleware 返回的 controller");
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
exports.createTimeTravelMiddleware = createTimeTravelMiddleware;
|
|
152
|
+
exports.setRestoreCallback = setRestoreCallback;
|
|
153
|
+
/*! End of @composy/store-core | Powered by @composy/builder */
|
|
154
|
+
//# sourceMappingURL=time-travel.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-travel.cjs","names":[],"sources":["../../src/middleware/time-travel.ts"],"sourcesContent":["/* ============================================================\n * time-travel.ts\n * 时间旅行中间件\n * 提供状态快照历史记录、撤销(undo)、重做(redo)、跳转(goto)等能力\n * 用于实现 Store 状态的可回溯调试与操作历史管理\n * ============================================================ */\n\n/* 从类型文件导入中间件、上下文、快照、状态树等核心类型 */\nimport type {\n Middleware, // 中间件接口\n MiddlewareContext, // 中间件执行上下文接口\n StateSnapshot, // 状态快照接口\n StateTree, // 状态树类型\n StoreId, // Store 标识类型\n TimeTravelController, // 时间旅行控制器接口\n TimeTravelMiddlewareOptions, // 时间旅行中间件配置选项接口\n} from '../types'\n/* 从工具库导入深拷贝函数,用于快照的隔离存储 */\nimport { deepClone } from '../utils/deep-clone'\n\n/* ── 内部类型定义 ── */\n\n/**\n * 恢复回调函数类型\n * 当执行 undo/redo/goto 操作时,通过此回调将历史快照状态应用到对应 Store\n * @param storeId - 目标 Store 的标识\n * @param state - 要恢复的状态树(已深拷贝,可安全修改)\n */\ntype RestoreCallback = (storeId: StoreId, state: StateTree) => void\n\n/**\n * 带有内部恢复回调设置器的控制器扩展类型\n * 在 TimeTravelController 基础上增加 __setRestoreCallback 内部方法\n * 用于在创建后注入实际的恢复回调(由 Store 运行时提供)\n */\ntype TimeTravelControllerWithRestoreSetter = TimeTravelController & {\n __setRestoreCallback?: (callback: RestoreCallback) => void\n}\n\n/* ── 默认配置 ── */\n\n/** 中间件默认选项:最多保留 100 条历史记录,功能默认启用 */\nconst defaultOptions: TimeTravelMiddlewareOptions = {\n maxHistory: 100,\n enabled: true,\n}\n\n/**\n * 创建时间旅行中间件\n *\n * 返回一个中间件函数和一个控制器对象:\n * - 中间件负责拦截 state: 类型的操作,在操作前后记录状态快照\n * - 控制器提供 undo/redo/goto/clear 等方法,用于在历史快照之间跳转\n *\n * @param options - 中间件配置选项(可选,与默认值合并)\n * @returns 包含 middleware(中间件)和 controller(控制器)的对象\n */\nexport function createTimeTravelMiddleware(\n options: TimeTravelMiddlewareOptions = {},\n): {\n middleware: Middleware\n controller: TimeTravelController\n} {\n /* 将用户选项与默认选项合并 */\n const opts = { ...defaultOptions, ...options }\n /* 最大历史记录条数,确保至少为 1 */\n const maxHistory = opts.maxHistory ?? defaultOptions.maxHistory ?? 100\n /* 历史快照数组,存储所有状态变更的快照 */\n const history: StateSnapshot[] = []\n\n /* 当前快照指针,指向 history 数组中的当前位置,-1 表示无快照 */\n let currentIndex = -1\n /* 标记是否正在进行时间旅行(undo/redo/goto),防止在恢复过程中重复记录快照 */\n let isTraveling = false\n /* 恢复回调函数,初始为空函数,后续通过 __setRestoreCallback 注入实际实现 */\n let restoreCallback: RestoreCallback = () => {}\n\n /**\n * 添加新的状态快照到历史记录\n * 业务处理过程:\n * 1. 如果中间件未启用或正在时间旅行中,则跳过\n * 2. 如果当前指针不在末尾(即之前执行过 undo),则丢弃指针之后的快照(分支覆盖)\n * 3. 推入新快照并更新指针\n * 4. 如果超过最大历史条数,移除最早的快照并调整指针\n *\n * @param snapshot - 要添加的状态快照\n */\n const addSnapshot = (snapshot: StateSnapshot): void => {\n /* 中间件未启用或正在时间旅行中,不记录快照 */\n if (!opts.enabled || isTraveling) {\n return\n }\n\n /* 如果当前指针不在历史末尾(之前执行过 undo),丢弃后续快照(创建新分支) */\n if (currentIndex < history.length - 1) {\n history.splice(currentIndex + 1)\n }\n\n /* 将新快照推入历史数组 */\n history.push(snapshot)\n /* 更新指针到最新快照 */\n currentIndex = history.length - 1\n\n /* 如果历史记录超过上限,移除最早的快照 */\n if (history.length > maxHistory) {\n history.shift()\n /* 移除头部元素后指针需要前移一位 */\n currentIndex--\n }\n }\n\n /**\n * 时间旅行控制器\n * 提供 undo(撤销)、redo(重做)、goto(跳转)、clear(清除)等操作\n * 所有恢复操作都通过 restoreCallback 将历史快照应用到 Store\n */\n const controller: TimeTravelControllerWithRestoreSetter = {\n /**\n * 撤销操作\n * 将当前指针前移一步,恢复到上一个历史快照状态\n */\n undo: () => {\n /* 无法撤销(已到最早快照)则跳过 */\n if (!controller.canUndo()) {\n return\n }\n\n /* 标记进入时间旅行模式,防止恢复过程触发新的快照记录 */\n isTraveling = true\n /* 指针前移 */\n currentIndex--\n /* 获取目标快照 */\n const snapshot = history[currentIndex]\n /* 通过恢复回调将快照状态(深拷贝)应用到 Store,避免污染历史记录 */\n restoreCallback(snapshot.storeId, deepClone(snapshot.state))\n /* 退出时间旅行模式 */\n isTraveling = false\n },\n\n /**\n * 重做操作\n * 将当前指针后移一步,恢复到下一个历史快照状态\n */\n redo: () => {\n /* 无法重做(已到最新快照)则跳过 */\n if (!controller.canRedo()) {\n return\n }\n\n /* 标记进入时间旅行模式 */\n isTraveling = true\n /* 指针后移 */\n currentIndex++\n /* 获取目标快照 */\n const snapshot = history[currentIndex]\n /* 恢复快照状态(深拷贝) */\n restoreCallback(snapshot.storeId, deepClone(snapshot.state))\n /* 退出时间旅行模式 */\n isTraveling = false\n },\n\n /**\n * 跳转到指定历史索引\n * 将当前指针直接移动到指定位置,恢复对应的快照状态\n *\n * @param index - 目标历史索引(0-based)\n */\n goto: (index: number) => {\n /* 索引越界检查 */\n if (index < 0 || index >= history.length) {\n return\n }\n\n /* 标记进入时间旅行模式 */\n isTraveling = true\n /* 直接设置指针到目标索引 */\n currentIndex = index\n /* 获取目标快照 */\n const snapshot = history[currentIndex]\n /* 恢复快照状态(深拷贝) */\n restoreCallback(snapshot.storeId, deepClone(snapshot.state))\n /* 退出时间旅行模式 */\n isTraveling = false\n },\n\n /**\n * 获取完整的快照历史(返回深拷贝,防止外部修改内部历史记录)\n * @returns 历史快照数组的深拷贝\n */\n getHistory: () => deepClone(history),\n\n /**\n * 获取当前快照指针索引\n * @returns 当前索引值,-1 表示无快照\n */\n getCurrentIndex: () => currentIndex,\n\n /**\n * 判断是否可以撤销\n * 当前索引大于 0 时,说明前面还有历史快照\n * @returns 是否可以执行 undo 操作\n */\n canUndo: () => currentIndex > 0,\n\n /**\n * 判断是否可以重做\n * 当前索引小于历史长度 - 1 时,说明后面还有快照\n * @returns 是否可以执行 redo 操作\n */\n canRedo: () => currentIndex < history.length - 1,\n\n /**\n * 清除所有历史快照并重置指针\n * 通常在 Store 销毁或需要重置历史时调用\n */\n clear: () => {\n /* 清空历史数组 */\n history.length = 0\n /* 重置指针到初始状态 */\n currentIndex = -1\n },\n }\n\n /**\n * 在控制器上挂载内部恢复回调设置器\n * 允许 Store 运行时在初始化时注入实际的状态恢复逻辑\n * 使用 __ 前缀表示这是内部方法,不建议外部直接调用\n */\n controller.__setRestoreCallback = (callback: RestoreCallback) => {\n restoreCallback = callback\n }\n\n /**\n * 时间旅行中间件\n * 拦截所有 \"state:\" 前缀的操作(如 state:update),在操作执行前后\n * 记录状态快照,从而构建可回溯的状态历史\n */\n const middleware: Middleware = {\n /* 中间件名称,用于调试和日志 */\n name: 'time-travel',\n /* 中间件优先级,50 为中等优先级(数值越小越先执行) */\n priority: 50,\n /**\n * 中间件处理函数\n * 只拦截 \"state:\" 类型的操作(状态变更操作),其他类型直接放行\n * 正在时间旅行时也不记录快照,避免循环\n *\n * @param context - 中间件上下文,包含 storeId、操作类型、状态等信息\n * @param next - 调用下一个中间件或最终操作的函数\n */\n handler: async (context: MiddlewareContext, next: () => void | Promise<void>) => {\n /* 解构上下文:Store 标识、操作类型、当前状态 */\n const { storeId, type, state } = context\n\n /* 非状态变更操作(不以 \"state:\" 开头)或正在时间旅行中,直接执行后续操作 */\n if (!type.startsWith('state:') || isTraveling) {\n await next()\n return\n }\n\n /* 在执行操作前,创建当前状态的快照(before snapshot) */\n const snapshot: StateSnapshot = {\n timestamp: Date.now(), /* 快照时间戳 */\n state: deepClone(state), /* 深拷贝当前状态,隔离引用 */\n type, /* 操作类型 */\n storeId, /* Store 标识 */\n }\n\n /* 执行后续中间件或最终操作 */\n await next()\n\n /* 操作执行完成后,将快照添加到历史记录 */\n addSnapshot(snapshot)\n },\n }\n\n /* 返回中间件和控制器 */\n return {\n middleware,\n controller,\n }\n}\n\n/**\n * 外部设置恢复回调的辅助函数\n * 将外部提供的状态恢复函数注入到时间旅行控制器中\n *\n * 使用场景:Store 运行时初始化时调用此函数,将控制器与 Store 的状态恢复逻辑连接\n *\n * @param controller - 时间旅行控制器实例\n * @param callback - 恢复回调函数,接收 storeId 和已深拷贝的状态树\n */\nexport function setRestoreCallback(\n controller: TimeTravelController,\n callback: (storeId: StoreId, state: StateTree) => void,\n): void {\n /* 将通用控制器类型断言为带内部设置器的扩展类型 */\n const controllerWithSetter = controller as TimeTravelControllerWithRestoreSetter\n /* 检查控制器是否支持 __setRestoreCallback 方法 */\n if (typeof controllerWithSetter.__setRestoreCallback === 'function') {\n /* 注入恢复回调 */\n controllerWithSetter.__setRestoreCallback(callback)\n return\n }\n\n /* 如果控制器不支持,输出警告(说明传入的不是 createTimeTravelMiddleware 创建的控制器) */\n console.warn('[TimeTravel] 请使用 createTimeTravelMiddleware 返回的 controller')\n}\n"],"mappings":";;;;;;;;;;;;;AA0CA,MAAM,iBAA8C;CAClD,YAAY;CACZ,SAAS;AACX;AAYO,SAAS,2BACd,UAAuC,CAAC,GAIxC;CAEA,MAAM,OAAO;EAAE,GAAG;EAAgB,GAAG;CAAQ;CAE7C,MAAM,aAAa,KAAK,cAAc,eAAe,cAAc;CAEnE,MAAM,UAA2B,CAAC;CAGlC,IAAI,eAAe;CAEnB,IAAI,cAAc;CAElB,IAAI,wBAAyC,CAAC;CAY9C,MAAM,eAAe,aAAkC;EAErD,IAAI,CAAC,KAAK,WAAW,aACnB;EAIF,IAAI,eAAe,QAAQ,SAAS,GAClC,QAAQ,OAAO,eAAe,CAAC;EAIjC,QAAQ,KAAK,QAAQ;EAErB,eAAe,QAAQ,SAAS;EAGhC,IAAI,QAAQ,SAAS,YAAY;GAC/B,QAAQ,MAAM;GAEd;EACF;CACF;CAOA,MAAM,aAAoD;;;;;EAKxD,YAAY;GAEV,IAAI,CAAC,WAAW,QAAQ,GACtB;GAIF,cAAc;GAEd;GAEA,MAAM,WAAW,QAAQ;GAEzB,gBAAgB,SAAS,SAAS,mBAAA,UAAU,SAAS,KAAK,CAAC;GAE3D,cAAc;EAChB;;;;;EAMA,YAAY;GAEV,IAAI,CAAC,WAAW,QAAQ,GACtB;GAIF,cAAc;GAEd;GAEA,MAAM,WAAW,QAAQ;GAEzB,gBAAgB,SAAS,SAAS,mBAAA,UAAU,SAAS,KAAK,CAAC;GAE3D,cAAc;EAChB;;;;;;;EAQA,OAAO,UAAkB;GAEvB,IAAI,QAAQ,KAAK,SAAS,QAAQ,QAChC;GAIF,cAAc;GAEd,eAAe;GAEf,MAAM,WAAW,QAAQ;GAEzB,gBAAgB,SAAS,SAAS,mBAAA,UAAU,SAAS,KAAK,CAAC;GAE3D,cAAc;EAChB;;;;;EAMA,kBAAkB,mBAAA,UAAU,OAAO;;;;;EAMnC,uBAAuB;;;;;;EAOvB,eAAe,eAAe;;;;;;EAO9B,eAAe,eAAe,QAAQ,SAAS;;;;;EAM/C,aAAa;GAEX,QAAQ,SAAS;GAEjB,eAAe;EACjB;CACF;CAOA,WAAW,wBAAwB,aAA8B;EAC/D,kBAAkB;CACpB;CA+CA,OAAO;EACL,YAAA;GAvCA,MAAM;GAEN,UAAU;;;;;;;;;GASV,SAAS,OAAO,SAA4B,SAAqC;IAE/E,MAAM,EAAE,SAAS,MAAM,UAAU;IAGjC,IAAI,CAAC,KAAK,WAAW,QAAQ,KAAK,aAAa;KAC7C,MAAM,KAAK;KACX;IACF;IAGA,MAAM,WAA0B;KAC9B,WAAW,KAAK,IAAI;KACpB,OAAO,mBAAA,UAAU,KAAK;KACtB;KACA;IACF;IAGA,MAAM,KAAK;IAGX,YAAY,QAAQ;GACtB;EAKA;EACA;CACF;AACF;AAWO,SAAS,mBACd,YACA,UACM;CAEN,MAAM,uBAAuB;CAE7B,IAAI,OAAO,qBAAqB,yBAAyB,YAAY;EAEnE,qBAAqB,qBAAqB,QAAQ;EAClD;CACF;CAGA,QAAQ,KAAK,4DAA4D;AAC3E"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Middleware, // 状态快照接口
|
|
2
|
+
StateTree, // 状态树类型
|
|
3
|
+
StoreId, // Store 标识类型
|
|
4
|
+
TimeTravelController, // 时间旅行控制器接口
|
|
5
|
+
TimeTravelMiddlewareOptions } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* 创建时间旅行中间件
|
|
8
|
+
*
|
|
9
|
+
* 返回一个中间件函数和一个控制器对象:
|
|
10
|
+
* - 中间件负责拦截 state: 类型的操作,在操作前后记录状态快照
|
|
11
|
+
* - 控制器提供 undo/redo/goto/clear 等方法,用于在历史快照之间跳转
|
|
12
|
+
*
|
|
13
|
+
* @param options - 中间件配置选项(可选,与默认值合并)
|
|
14
|
+
* @returns 包含 middleware(中间件)和 controller(控制器)的对象
|
|
15
|
+
*/
|
|
16
|
+
export declare function createTimeTravelMiddleware(options?: TimeTravelMiddlewareOptions): {
|
|
17
|
+
middleware: Middleware;
|
|
18
|
+
controller: TimeTravelController;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 外部设置恢复回调的辅助函数
|
|
22
|
+
* 将外部提供的状态恢复函数注入到时间旅行控制器中
|
|
23
|
+
*
|
|
24
|
+
* 使用场景:Store 运行时初始化时调用此函数,将控制器与 Store 的状态恢复逻辑连接
|
|
25
|
+
*
|
|
26
|
+
* @param controller - 时间旅行控制器实例
|
|
27
|
+
* @param callback - 恢复回调函数,接收 storeId 和已深拷贝的状态树
|
|
28
|
+
*/
|
|
29
|
+
export declare function setRestoreCallback(controller: TimeTravelController, callback: (storeId: StoreId, state: StateTree) => void): void;
|
|
30
|
+
//# sourceMappingURL=time-travel.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* ***********************************
|
|
3
|
+
* @composy/store-core v0.0.1 *
|
|
4
|
+
* Built with rollup *
|
|
5
|
+
* Build time: 2024-08-17 11:27:33 *
|
|
6
|
+
* Build mode: production *
|
|
7
|
+
* Minified: No *
|
|
8
|
+
* ***********************************
|
|
9
|
+
*/
|
|
10
|
+
"use strict";
|
|
11
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
12
|
+
//#region src/middleware/validation.ts
|
|
13
|
+
function createValidationMiddleware(options) {
|
|
14
|
+
const { rules, onValidationError, blocking = false, patchOnly = false } = options;
|
|
15
|
+
let lastErrors = [];
|
|
16
|
+
async function validateState(storeId, state) {
|
|
17
|
+
const storeRules = rules[storeId];
|
|
18
|
+
if (!storeRules) return {
|
|
19
|
+
valid: true,
|
|
20
|
+
errors: []
|
|
21
|
+
};
|
|
22
|
+
const errors = [];
|
|
23
|
+
for (const [field, fieldRules] of Object.entries(storeRules)) {
|
|
24
|
+
const ruleList = Array.isArray(fieldRules) ? fieldRules : [fieldRules];
|
|
25
|
+
const value = state[field];
|
|
26
|
+
for (const rule of ruleList) try {
|
|
27
|
+
if (!await rule.validate(value, state)) errors.push({
|
|
28
|
+
storeId,
|
|
29
|
+
field,
|
|
30
|
+
message: rule.message,
|
|
31
|
+
value
|
|
32
|
+
});
|
|
33
|
+
} catch {
|
|
34
|
+
errors.push({
|
|
35
|
+
storeId,
|
|
36
|
+
field,
|
|
37
|
+
message: rule.message || `Validation error on field "${field}"`,
|
|
38
|
+
value
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
valid: errors.length === 0,
|
|
44
|
+
errors
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
middleware: {
|
|
49
|
+
name: "validation",
|
|
50
|
+
priority: 5,
|
|
51
|
+
handler: async (context, next) => {
|
|
52
|
+
const { storeId, type, state } = context;
|
|
53
|
+
if (!(patchOnly ? type === "state:patch" : type.startsWith("state:")) || !rules[storeId]) {
|
|
54
|
+
await next();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await next();
|
|
58
|
+
const result = await validateState(storeId, state);
|
|
59
|
+
if (!result.valid) {
|
|
60
|
+
lastErrors = result.errors;
|
|
61
|
+
onValidationError?.(result);
|
|
62
|
+
if (blocking) throw new Error(`[Validation] ${storeId}: ${result.errors.map((e) => e.message).join("; ")}`);
|
|
63
|
+
} else lastErrors = lastErrors.filter((e) => e.storeId !== storeId);
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
validate: validateState,
|
|
67
|
+
getErrors: () => [...lastErrors],
|
|
68
|
+
clearErrors: () => {
|
|
69
|
+
lastErrors = [];
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
exports.createValidationMiddleware = createValidationMiddleware;
|
|
75
|
+
/*! End of @composy/store-core | Powered by @composy/builder */
|
|
76
|
+
//# sourceMappingURL=validation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.cjs","names":[],"sources":["../../src/middleware/validation.ts"],"sourcesContent":["/**\n * 验证中间件\n *\n * 对状态变化进行规则校验,支持同步/异步校验器\n * @module middleware/validation\n */\n\nimport type { Middleware, MiddlewareContext, StateTree, StoreId } from '../types'\n\n/**\n * 单个字段校验规则\n */\nexport interface ValidationRule<T = unknown> {\n /** 校验函数,返回 true 表示通过 */\n validate: (value: T, state: StateTree) => boolean | Promise<boolean>\n /** 校验失败时的错误信息 */\n message: string\n}\n\n/**\n * Store 校验规则集\n */\nexport type StoreValidationRules = Record<string, ValidationRule | ValidationRule[]>\n\n/**\n * 校验错误\n */\nexport interface ValidationError {\n /** Store ID */\n storeId: StoreId\n /** 字段路径 */\n field: string\n /** 错误信息 */\n message: string\n /** 字段值 */\n value: unknown\n}\n\n/**\n * 校验结果\n */\nexport interface ValidationResult {\n /** 是否全部通过 */\n valid: boolean\n /** 错误列表 */\n errors: ValidationError[]\n}\n\n/**\n * 验证中间件选项\n */\nexport interface ValidationMiddlewareOptions {\n /** 按 Store ID 配置的校验规则 */\n rules: Record<StoreId, StoreValidationRules>\n /** 校验失败时的回调 */\n onValidationError?: (result: ValidationResult) => void\n /** 校验失败是否阻止状态变化 (默认 false,仅报告) */\n blocking?: boolean\n /** 是否只在 state:patch 时校验 (默认 false,所有 state: 前缀均校验) */\n patchOnly?: boolean\n}\n\n/**\n * 创建验证中间件\n *\n * 对状态变化进行规则校验,可选择阻止无效变化或仅报告错误\n *\n * @param options - 验证配置\n * @returns 中间件实例和校验辅助 API\n *\n * @example\n * ```typescript\n * const { middleware, validate, getErrors, clearErrors } = createValidationMiddleware({\n * rules: {\n * 'user-form': {\n * name: {\n * validate: (v) => typeof v === 'string' && v.length >= 2,\n * message: 'Name must be at least 2 characters',\n * },\n * age: [\n * { validate: (v) => typeof v === 'number', message: 'Age must be a number' },\n * { validate: (v) => (v as number) >= 0 && (v as number) <= 150, message: 'Age must be 0-150' },\n * ],\n * email: {\n * validate: (v) => /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(String(v)),\n * message: 'Invalid email format',\n * },\n * },\n * },\n * onValidationError: (result) => {\n * console.warn('Validation failed:', result.errors)\n * },\n * })\n *\n * manager.use(middleware)\n * ```\n */\nexport function createValidationMiddleware(options: ValidationMiddlewareOptions): {\n middleware: Middleware\n /** 手动校验某个 store 的当前状态 */\n validate: (storeId: StoreId, state: StateTree) => Promise<ValidationResult>\n /** 获取最近一次的校验错误 */\n getErrors: () => ValidationError[]\n /** 清空错误 */\n clearErrors: () => void\n} {\n const {\n rules,\n onValidationError,\n blocking = false,\n patchOnly = false,\n } = options\n\n let lastErrors: ValidationError[] = []\n\n /**\n * 校验单个 store 状态\n */\n async function validateState(storeId: StoreId, state: StateTree): Promise<ValidationResult> {\n const storeRules = rules[storeId]\n if (!storeRules) return { valid: true, errors: [] }\n\n const errors: ValidationError[] = []\n\n for (const [field, fieldRules] of Object.entries(storeRules)) {\n const ruleList = Array.isArray(fieldRules) ? fieldRules : [fieldRules]\n const value = state[field]\n\n for (const rule of ruleList) {\n try {\n const isValid = await rule.validate(value, state)\n if (!isValid) {\n errors.push({\n storeId,\n field,\n message: rule.message,\n value,\n })\n }\n }\n catch {\n errors.push({\n storeId,\n field,\n message: rule.message || `Validation error on field \"${field}\"`,\n value,\n })\n }\n }\n }\n\n return { valid: errors.length === 0, errors }\n }\n\n const middleware: Middleware = {\n name: 'validation',\n priority: 5, // 在 logger (0) 之后、大多数业务中间件之前\n\n handler: async (context: MiddlewareContext, next: () => void | Promise<void>) => {\n const { storeId, type, state } = context\n\n // 检查是否需要校验\n const shouldValidate = patchOnly\n ? type === 'state:patch'\n : type.startsWith('state:')\n\n if (!shouldValidate || !rules[storeId]) {\n await next()\n return\n }\n\n // 先执行状态变化\n await next()\n\n // 校验变化后的状态\n const result = await validateState(storeId, state)\n\n if (!result.valid) {\n lastErrors = result.errors\n onValidationError?.(result)\n\n if (blocking) {\n throw new Error(\n `[Validation] ${storeId}: ${result.errors.map(e => e.message).join('; ')}`,\n )\n }\n }\n else {\n // 校验通过,清除该 store 的错误\n lastErrors = lastErrors.filter(e => e.storeId !== storeId)\n }\n },\n }\n\n return {\n middleware,\n validate: validateState,\n getErrors: () => [...lastErrors],\n clearErrors: () => { lastErrors = [] },\n }\n}\n"],"mappings":";;;;;;;;;;;;AAiGO,SAAS,2BAA2B,SAQzC;CACA,MAAM,EACJ,OACA,mBACA,WAAW,OACX,YAAY,UACV;CAEJ,IAAI,aAAgC,CAAC;CAKrC,eAAe,cAAc,SAAkB,OAA6C;EAC1F,MAAM,aAAa,MAAM;EACzB,IAAI,CAAC,YAAY,OAAO;GAAE,OAAO;GAAM,QAAQ,CAAC;EAAE;EAElD,MAAM,SAA4B,CAAC;EAEnC,KAAA,MAAW,CAAC,OAAO,eAAe,OAAO,QAAQ,UAAU,GAAG;GAC5D,MAAM,WAAW,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC,UAAU;GACrE,MAAM,QAAQ,MAAM;GAEpB,KAAA,MAAW,QAAQ,UACjB,IAAI;IAEF,IAAI,CAAC,MADiB,KAAK,SAAS,OAAO,KAAK,GAE9C,OAAO,KAAK;KACV;KACA;KACA,SAAS,KAAK;KACd;IACF,CAAC;GAEL,QACM;IACJ,OAAO,KAAK;KACV;KACA;KACA,SAAS,KAAK,WAAW,8BAA8B,MAAK;KAC5D;IACF,CAAC;GACH;EAEJ;EAEA,OAAO;GAAE,OAAO,OAAO,WAAW;GAAG;EAAO;CAC9C;CA0CA,OAAO;EACL,YAAA;GAxCA,MAAM;GACN,UAAU;GAEV,SAAS,OAAO,SAA4B,SAAqC;IAC/E,MAAM,EAAE,SAAS,MAAM,UAAU;IAOjC,IAAI,EAJmB,YACnB,SAAS,gBACT,KAAK,WAAW,QAAQ,MAEL,CAAC,MAAM,UAAU;KACtC,MAAM,KAAK;KACX;IACF;IAGA,MAAM,KAAK;IAGX,MAAM,SAAS,MAAM,cAAc,SAAS,KAAK;IAEjD,IAAI,CAAC,OAAO,OAAO;KACjB,aAAa,OAAO;KACpB,oBAAoB,MAAM;KAE1B,IAAI,UACF,MAAM,IAAI,MACR,gBAAgB,QAAO,IAAK,OAAO,OAAO,KAAI,MAAK,EAAE,OAAO,CAAA,CAAE,KAAK,IAAI,GACzE;IAEJ,OAGE,aAAa,WAAW,QAAO,MAAK,EAAE,YAAY,OAAO;GAE7D;EAIA;EACA,UAAU;EACV,iBAAiB,CAAC,GAAG,UAAU;EAC/B,mBAAmB;GAAE,aAAa,CAAC;EAAE;CACvC;AACF"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 验证中间件
|
|
3
|
+
*
|
|
4
|
+
* 对状态变化进行规则校验,支持同步/异步校验器
|
|
5
|
+
* @module middleware/validation
|
|
6
|
+
*/
|
|
7
|
+
import type { Middleware, StateTree, StoreId } from '../types';
|
|
8
|
+
/**
|
|
9
|
+
* 单个字段校验规则
|
|
10
|
+
*/
|
|
11
|
+
export interface ValidationRule<T = unknown> {
|
|
12
|
+
/** 校验函数,返回 true 表示通过 */
|
|
13
|
+
validate: (value: T, state: StateTree) => boolean | Promise<boolean>;
|
|
14
|
+
/** 校验失败时的错误信息 */
|
|
15
|
+
message: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Store 校验规则集
|
|
19
|
+
*/
|
|
20
|
+
export type StoreValidationRules = Record<string, ValidationRule | ValidationRule[]>;
|
|
21
|
+
/**
|
|
22
|
+
* 校验错误
|
|
23
|
+
*/
|
|
24
|
+
export interface ValidationError {
|
|
25
|
+
/** Store ID */
|
|
26
|
+
storeId: StoreId;
|
|
27
|
+
/** 字段路径 */
|
|
28
|
+
field: string;
|
|
29
|
+
/** 错误信息 */
|
|
30
|
+
message: string;
|
|
31
|
+
/** 字段值 */
|
|
32
|
+
value: unknown;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 校验结果
|
|
36
|
+
*/
|
|
37
|
+
export interface ValidationResult {
|
|
38
|
+
/** 是否全部通过 */
|
|
39
|
+
valid: boolean;
|
|
40
|
+
/** 错误列表 */
|
|
41
|
+
errors: ValidationError[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 验证中间件选项
|
|
45
|
+
*/
|
|
46
|
+
export interface ValidationMiddlewareOptions {
|
|
47
|
+
/** 按 Store ID 配置的校验规则 */
|
|
48
|
+
rules: Record<StoreId, StoreValidationRules>;
|
|
49
|
+
/** 校验失败时的回调 */
|
|
50
|
+
onValidationError?: (result: ValidationResult) => void;
|
|
51
|
+
/** 校验失败是否阻止状态变化 (默认 false,仅报告) */
|
|
52
|
+
blocking?: boolean;
|
|
53
|
+
/** 是否只在 state:patch 时校验 (默认 false,所有 state: 前缀均校验) */
|
|
54
|
+
patchOnly?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 创建验证中间件
|
|
58
|
+
*
|
|
59
|
+
* 对状态变化进行规则校验,可选择阻止无效变化或仅报告错误
|
|
60
|
+
*
|
|
61
|
+
* @param options - 验证配置
|
|
62
|
+
* @returns 中间件实例和校验辅助 API
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const { middleware, validate, getErrors, clearErrors } = createValidationMiddleware({
|
|
67
|
+
* rules: {
|
|
68
|
+
* 'user-form': {
|
|
69
|
+
* name: {
|
|
70
|
+
* validate: (v) => typeof v === 'string' && v.length >= 2,
|
|
71
|
+
* message: 'Name must be at least 2 characters',
|
|
72
|
+
* },
|
|
73
|
+
* age: [
|
|
74
|
+
* { validate: (v) => typeof v === 'number', message: 'Age must be a number' },
|
|
75
|
+
* { validate: (v) => (v as number) >= 0 && (v as number) <= 150, message: 'Age must be 0-150' },
|
|
76
|
+
* ],
|
|
77
|
+
* email: {
|
|
78
|
+
* validate: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(v)),
|
|
79
|
+
* message: 'Invalid email format',
|
|
80
|
+
* },
|
|
81
|
+
* },
|
|
82
|
+
* },
|
|
83
|
+
* onValidationError: (result) => {
|
|
84
|
+
* console.warn('Validation failed:', result.errors)
|
|
85
|
+
* },
|
|
86
|
+
* })
|
|
87
|
+
*
|
|
88
|
+
* manager.use(middleware)
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare function createValidationMiddleware(options: ValidationMiddlewareOptions): {
|
|
92
|
+
middleware: Middleware;
|
|
93
|
+
/** 手动校验某个 store 的当前状态 */
|
|
94
|
+
validate: (storeId: StoreId, state: StateTree) => Promise<ValidationResult>;
|
|
95
|
+
/** 获取最近一次的校验错误 */
|
|
96
|
+
getErrors: () => ValidationError[];
|
|
97
|
+
/** 清空错误 */
|
|
98
|
+
clearErrors: () => void;
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=validation.d.ts.map
|
package/lib/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* ***********************************
|
|
3
|
+
* @composy/store-core v0.0.1 *
|
|
4
|
+
* Built with rollup *
|
|
5
|
+
* Build time: 2024-08-17 11:27:33 *
|
|
6
|
+
* Build mode: production *
|
|
7
|
+
* Minified: No *
|
|
8
|
+
* ***********************************
|
|
9
|
+
*/
|
|
10
|
+
"use strict";
|
|
11
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
12
|
+
//#region src/plugins/define-plugin.ts
|
|
13
|
+
function defineStorePlugin(options) {
|
|
14
|
+
return {
|
|
15
|
+
name: options.name,
|
|
16
|
+
version: options.version,
|
|
17
|
+
description: options.description,
|
|
18
|
+
install: options.install,
|
|
19
|
+
uninstall: options.uninstall,
|
|
20
|
+
onStoreCreated: options.onStoreCreated,
|
|
21
|
+
onStoreDisposed: options.onStoreDisposed,
|
|
22
|
+
onStateChange: options.onStateChange
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function createPluginFactory(factory) {
|
|
26
|
+
return (options) => {
|
|
27
|
+
return defineStorePlugin(factory(options));
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function composePlugins(plugins) {
|
|
31
|
+
return {
|
|
32
|
+
name: "composed-plugins",
|
|
33
|
+
version: "1.0.0",
|
|
34
|
+
async install(context, options) {
|
|
35
|
+
for (const plugin of plugins) await plugin.install(context, options);
|
|
36
|
+
},
|
|
37
|
+
async uninstall(context) {
|
|
38
|
+
for (let i = plugins.length - 1; i >= 0; i--) await plugins[i].uninstall?.(context);
|
|
39
|
+
},
|
|
40
|
+
onStoreCreated(store) {
|
|
41
|
+
plugins.forEach((plugin) => plugin.onStoreCreated?.(store));
|
|
42
|
+
},
|
|
43
|
+
onStoreDisposed(store) {
|
|
44
|
+
plugins.forEach((plugin) => plugin.onStoreDisposed?.(store));
|
|
45
|
+
},
|
|
46
|
+
onStateChange(storeId, state, oldState) {
|
|
47
|
+
plugins.forEach((plugin) => plugin.onStateChange?.(storeId, state, oldState));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
exports.composePlugins = composePlugins;
|
|
53
|
+
exports.createPluginFactory = createPluginFactory;
|
|
54
|
+
exports.defineStorePlugin = defineStorePlugin;
|
|
55
|
+
/*! End of @composy/store-core | Powered by @composy/builder */
|
|
56
|
+
//# sourceMappingURL=define-plugin.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-plugin.cjs","names":[],"sources":["../../src/plugins/define-plugin.ts"],"sourcesContent":["/**\n * 插件定义辅助函数\n *\n * @module plugins/define-plugin\n */\n\nimport type { DefinePluginOptions, PluginFactory, StorePlugin } from '../types'\n\n/**\n * 定义 Store 插件\n *\n * 提供类型安全的插件定义方式\n *\n * @template Options - 插件选项类型\n * @param options - 插件定义选项\n * @returns 插件实例\n *\n * @example\n * ```typescript\n * // 定义一个简单的日志插件\n * const loggerPlugin = defineStorePlugin({\n * name: 'logger',\n * version: '1.0.0',\n * install(context) {\n * console.log('Logger plugin installed')\n * },\n * onStateChange(storeId, state, oldState) {\n * console.log(`[${storeId}] State changed:`, state)\n * },\n * })\n * ```\n */\nexport function defineStorePlugin<Options = Record<string, unknown>>(\n options: DefinePluginOptions<Options>,\n): StorePlugin<Options> {\n return {\n name: options.name,\n version: options.version,\n description: options.description,\n install: options.install,\n uninstall: options.uninstall,\n onStoreCreated: options.onStoreCreated,\n onStoreDisposed: options.onStoreDisposed,\n onStateChange: options.onStateChange,\n }\n}\n\n/**\n * 创建插件工厂函数\n *\n * 用于创建可配置的插件\n *\n * @template Options - 插件选项类型\n * @param factory - 插件工厂函数\n * @returns 插件工厂\n *\n * @example\n * ```typescript\n * // 创建可配置的持久化插件\n * const createPersistPlugin = createPluginFactory<{ key: string }>(\n * (options) => ({\n * name: 'persist',\n * install(context) {\n * console.log('Persist key:', options?.key)\n * },\n * })\n * )\n *\n * // 使用\n * const plugin = createPersistPlugin({ key: 'my-store' })\n * ```\n */\nexport function createPluginFactory<Options = Record<string, unknown>>(\n factory: (options?: Options) => DefinePluginOptions<Options>,\n): PluginFactory<Options> {\n return (options?: Options): StorePlugin<Options> => {\n return defineStorePlugin(factory(options))\n }\n}\n\n/**\n * 组合多个插件\n *\n * @param plugins - 插件列表\n * @returns 组合后的插件\n *\n * @example\n * ```typescript\n * const combinedPlugin = composePlugins([\n * loggerPlugin,\n * persistPlugin,\n * devtoolsPlugin,\n * ])\n * ```\n */\nexport function composePlugins(\n plugins: StorePlugin[],\n): StorePlugin {\n return {\n name: 'composed-plugins',\n version: '1.0.0',\n\n async install(context, options) {\n for (const plugin of plugins) {\n await plugin.install(context, options)\n }\n },\n\n async uninstall(context) {\n // 逆序卸载\n for (let i = plugins.length - 1; i >= 0; i--) {\n await plugins[i].uninstall?.(context)\n }\n },\n\n onStoreCreated(store) {\n plugins.forEach(plugin => plugin.onStoreCreated?.(store))\n },\n\n onStoreDisposed(store) {\n plugins.forEach(plugin => plugin.onStoreDisposed?.(store))\n },\n\n onStateChange(storeId, state, oldState) {\n plugins.forEach(plugin => plugin.onStateChange?.(storeId, state, oldState))\n },\n }\n}\n\n"],"mappings":";;;;;;;;;;;;AAgCO,SAAS,kBACd,SACsB;CACtB,OAAO;EACL,MAAM,QAAQ;EACd,SAAS,QAAQ;EACjB,aAAa,QAAQ;EACrB,SAAS,QAAQ;EACjB,WAAW,QAAQ;EACnB,gBAAgB,QAAQ;EACxB,iBAAiB,QAAQ;EACzB,eAAe,QAAQ;CACzB;AACF;AA2BO,SAAS,oBACd,SACwB;CACxB,QAAQ,YAA4C;EAClD,OAAO,kBAAkB,QAAQ,OAAO,CAAC;CAC3C;AACF;AAiBO,SAAS,eACd,SACa;CACb,OAAO;EACL,MAAM;EACN,SAAS;EAET,MAAM,QAAQ,SAAS,SAAS;GAC9B,KAAA,MAAW,UAAU,SACnB,MAAM,OAAO,QAAQ,SAAS,OAAO;EAEzC;EAEA,MAAM,UAAU,SAAS;GAEvB,KAAA,IAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KACvC,MAAM,QAAQ,EAAC,CAAE,YAAY,OAAO;EAExC;EAEA,eAAe,OAAO;GACpB,QAAQ,SAAQ,WAAU,OAAO,iBAAiB,KAAK,CAAC;EAC1D;EAEA,gBAAgB,OAAO;GACrB,QAAQ,SAAQ,WAAU,OAAO,kBAAkB,KAAK,CAAC;EAC3D;EAEA,cAAc,SAAS,OAAO,UAAU;GACtC,QAAQ,SAAQ,WAAU,OAAO,gBAAgB,SAAS,OAAO,QAAQ,CAAC;EAC5E;CACF;AACF"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件定义辅助函数
|
|
3
|
+
*
|
|
4
|
+
* @module plugins/define-plugin
|
|
5
|
+
*/
|
|
6
|
+
import type { DefinePluginOptions, PluginFactory, StorePlugin } from '../types';
|
|
7
|
+
/**
|
|
8
|
+
* 定义 Store 插件
|
|
9
|
+
*
|
|
10
|
+
* 提供类型安全的插件定义方式
|
|
11
|
+
*
|
|
12
|
+
* @template Options - 插件选项类型
|
|
13
|
+
* @param options - 插件定义选项
|
|
14
|
+
* @returns 插件实例
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // 定义一个简单的日志插件
|
|
19
|
+
* const loggerPlugin = defineStorePlugin({
|
|
20
|
+
* name: 'logger',
|
|
21
|
+
* version: '1.0.0',
|
|
22
|
+
* install(context) {
|
|
23
|
+
* console.log('Logger plugin installed')
|
|
24
|
+
* },
|
|
25
|
+
* onStateChange(storeId, state, oldState) {
|
|
26
|
+
* console.log(`[${storeId}] State changed:`, state)
|
|
27
|
+
* },
|
|
28
|
+
* })
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function defineStorePlugin<Options = Record<string, unknown>>(options: DefinePluginOptions<Options>): StorePlugin<Options>;
|
|
32
|
+
/**
|
|
33
|
+
* 创建插件工厂函数
|
|
34
|
+
*
|
|
35
|
+
* 用于创建可配置的插件
|
|
36
|
+
*
|
|
37
|
+
* @template Options - 插件选项类型
|
|
38
|
+
* @param factory - 插件工厂函数
|
|
39
|
+
* @returns 插件工厂
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* // 创建可配置的持久化插件
|
|
44
|
+
* const createPersistPlugin = createPluginFactory<{ key: string }>(
|
|
45
|
+
* (options) => ({
|
|
46
|
+
* name: 'persist',
|
|
47
|
+
* install(context) {
|
|
48
|
+
* console.log('Persist key:', options?.key)
|
|
49
|
+
* },
|
|
50
|
+
* })
|
|
51
|
+
* )
|
|
52
|
+
*
|
|
53
|
+
* // 使用
|
|
54
|
+
* const plugin = createPersistPlugin({ key: 'my-store' })
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function createPluginFactory<Options = Record<string, unknown>>(factory: (options?: Options) => DefinePluginOptions<Options>): PluginFactory<Options>;
|
|
58
|
+
/**
|
|
59
|
+
* 组合多个插件
|
|
60
|
+
*
|
|
61
|
+
* @param plugins - 插件列表
|
|
62
|
+
* @returns 组合后的插件
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const combinedPlugin = composePlugins([
|
|
67
|
+
* loggerPlugin,
|
|
68
|
+
* persistPlugin,
|
|
69
|
+
* devtoolsPlugin,
|
|
70
|
+
* ])
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare function composePlugins(plugins: StorePlugin[]): StorePlugin;
|
|
74
|
+
//# sourceMappingURL=define-plugin.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* ***********************************
|
|
3
|
+
* @composy/store-core v0.0.1 *
|
|
4
|
+
* Built with rollup *
|
|
5
|
+
* Build time: 2024-08-17 11:27:33 *
|
|
6
|
+
* Build mode: production *
|
|
7
|
+
* Minified: No *
|
|
8
|
+
* ***********************************
|
|
9
|
+
*/
|
|
10
|
+
"use strict";
|
|
11
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
12
|
+
const require_define_plugin = require("./define-plugin.cjs");
|
|
13
|
+
const require_plugin_manager = require("./plugin-manager.cjs");
|
|
14
|
+
exports.composePlugins = require_define_plugin.composePlugins;
|
|
15
|
+
exports.createPluginFactory = require_define_plugin.createPluginFactory;
|
|
16
|
+
exports.createPluginManager = require_plugin_manager.createPluginManager;
|
|
17
|
+
exports.defineStorePlugin = require_define_plugin.defineStorePlugin;
|
|
18
|
+
exports.getGlobalPluginManager = require_plugin_manager.getGlobalPluginManager;
|
|
19
|
+
exports.setGlobalPluginManager = require_plugin_manager.setGlobalPluginManager;
|
|
20
|
+
/*! End of @composy/store-core | Powered by @composy/builder */
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件模块导出
|
|
3
|
+
*
|
|
4
|
+
* @module plugins
|
|
5
|
+
*/
|
|
6
|
+
export { composePlugins, createPluginFactory, defineStorePlugin, } from './define-plugin';
|
|
7
|
+
export { createPluginManager, getGlobalPluginManager, setGlobalPluginManager, } from './plugin-manager';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* ***********************************
|
|
3
|
+
* @composy/store-core v0.0.1 *
|
|
4
|
+
* Built with rollup *
|
|
5
|
+
* Build time: 2024-08-17 11:27:33 *
|
|
6
|
+
* Build mode: production *
|
|
7
|
+
* Minified: No *
|
|
8
|
+
* ***********************************
|
|
9
|
+
*/
|
|
10
|
+
"use strict";
|
|
11
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
12
|
+
//#region src/plugins/plugin-manager.ts
|
|
13
|
+
function createPluginManager() {
|
|
14
|
+
const plugins = /* @__PURE__ */ new Map();
|
|
15
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
16
|
+
const use = (plugin, options) => {
|
|
17
|
+
if (plugins.has(plugin.name)) console.warn(`[Plugin] \u63D2\u4EF6 "${plugin.name}" \u5DF2\u5B58\u5728\uFF0C\u5C06\u88AB\u8986\u76D6`);
|
|
18
|
+
plugins.set(plugin.name, plugin);
|
|
19
|
+
const context = {
|
|
20
|
+
storeManager: {
|
|
21
|
+
getStore: () => null,
|
|
22
|
+
hasStore: () => false,
|
|
23
|
+
getStoreIds: () => []
|
|
24
|
+
},
|
|
25
|
+
storeId: "",
|
|
26
|
+
store: null,
|
|
27
|
+
options: options || {}
|
|
28
|
+
};
|
|
29
|
+
contexts.set(plugin.name, context);
|
|
30
|
+
};
|
|
31
|
+
const remove = (name) => {
|
|
32
|
+
const plugin = plugins.get(name);
|
|
33
|
+
const context = contexts.get(name);
|
|
34
|
+
if (plugin && context) plugin.uninstall?.(context);
|
|
35
|
+
plugins.delete(name);
|
|
36
|
+
contexts.delete(name);
|
|
37
|
+
};
|
|
38
|
+
const get = (name) => {
|
|
39
|
+
return plugins.get(name);
|
|
40
|
+
};
|
|
41
|
+
const has = (name) => {
|
|
42
|
+
return plugins.has(name);
|
|
43
|
+
};
|
|
44
|
+
const getAll = () => {
|
|
45
|
+
return new Map(plugins);
|
|
46
|
+
};
|
|
47
|
+
const size = () => {
|
|
48
|
+
return plugins.size;
|
|
49
|
+
};
|
|
50
|
+
const clear = () => {
|
|
51
|
+
plugins.forEach((plugin, name) => {
|
|
52
|
+
const context = contexts.get(name);
|
|
53
|
+
if (context) plugin.uninstall?.(context);
|
|
54
|
+
});
|
|
55
|
+
plugins.clear();
|
|
56
|
+
contexts.clear();
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
use,
|
|
60
|
+
remove,
|
|
61
|
+
get,
|
|
62
|
+
has,
|
|
63
|
+
getAll,
|
|
64
|
+
size,
|
|
65
|
+
clear
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
let globalPluginManager = null;
|
|
69
|
+
function getGlobalPluginManager() {
|
|
70
|
+
if (!globalPluginManager) globalPluginManager = createPluginManager();
|
|
71
|
+
return globalPluginManager;
|
|
72
|
+
}
|
|
73
|
+
function setGlobalPluginManager(manager) {
|
|
74
|
+
globalPluginManager = manager;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
exports.createPluginManager = createPluginManager;
|
|
78
|
+
exports.getGlobalPluginManager = getGlobalPluginManager;
|
|
79
|
+
exports.setGlobalPluginManager = setGlobalPluginManager;
|
|
80
|
+
/*! End of @composy/store-core | Powered by @composy/builder */
|
|
81
|
+
//# sourceMappingURL=plugin-manager.cjs.map
|