synapse-storage 3.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.cjs +891 -0
- package/dist/api.cjs.map +1 -0
- package/dist/api.d.cts +365 -0
- package/dist/api.d.ts +365 -0
- package/dist/api.js +860 -0
- package/dist/api.js.map +1 -0
- package/dist/core.cjs +2389 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +397 -0
- package/dist/{core/storage/modules/plugin/plugin.interface.d.ts → core.d.ts} +203 -7
- package/dist/core.js +2356 -0
- package/dist/core.js.map +1 -0
- package/dist/dispatcher.module-CdpmkplA.d.cts +363 -0
- package/dist/dispatcher.module-jd8U_ZEs.d.ts +363 -0
- package/dist/index.cjs +4148 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -10
- package/dist/index.js +4092 -13
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +268 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +74 -0
- package/dist/react.d.ts +74 -0
- package/dist/react.js +239 -0
- package/dist/react.js.map +1 -0
- package/dist/reactive.cjs +643 -0
- package/dist/reactive.cjs.map +1 -0
- package/dist/reactive.d.cts +35 -0
- package/dist/reactive.d.ts +35 -0
- package/dist/reactive.js +604 -0
- package/dist/reactive.js.map +1 -0
- package/dist/{core/selector/selector.interface.d.ts → selector.interface-CA5y-kD_.d.cts} +7 -35
- package/dist/selector.interface-CA5y-kD_.d.ts +63 -0
- package/dist/storage.interface-Dl8SLUd1.d.cts +128 -0
- package/dist/storage.interface-Dl8SLUd1.d.ts +128 -0
- package/dist/utils.cjs +601 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.cts +92 -0
- package/dist/utils.d.ts +92 -0
- package/dist/utils.js +574 -0
- package/dist/utils.js.map +1 -0
- package/package.json +54 -17
- package/dist/_utils/chunk.util.d.ts +0 -8
- package/dist/_utils/chunk.util.d.ts.map +0 -1
- package/dist/_utils/chunk.util.js +0 -21
- package/dist/_utils/chunk.util.js.map +0 -1
- package/dist/_utils/deepMerge.util.d.ts +0 -2
- package/dist/_utils/deepMerge.util.d.ts.map +0 -1
- package/dist/_utils/deepMerge.util.js +0 -16
- package/dist/_utils/deepMerge.util.js.map +0 -1
- package/dist/_utils/flatMap.util.d.ts +0 -10
- package/dist/_utils/flatMap.util.d.ts.map +0 -1
- package/dist/_utils/flatMap.util.js +0 -23
- package/dist/_utils/flatMap.util.js.map +0 -1
- package/dist/_utils/index.d.ts +0 -4
- package/dist/_utils/index.d.ts.map +0 -1
- package/dist/_utils/index.js +0 -4
- package/dist/_utils/index.js.map +0 -1
- package/dist/api/api.module.d.ts +0 -38
- package/dist/api/api.module.d.ts.map +0 -1
- package/dist/api/api.module.js +0 -82
- package/dist/api/api.module.js.map +0 -1
- package/dist/api/components/endpoint.d.ts +0 -26
- package/dist/api/components/endpoint.d.ts.map +0 -1
- package/dist/api/components/endpoint.js +0 -253
- package/dist/api/components/endpoint.js.map +0 -1
- package/dist/api/components/query-storage.d.ts +0 -84
- package/dist/api/components/query-storage.d.ts.map +0 -1
- package/dist/api/components/query-storage.js +0 -221
- package/dist/api/components/query-storage.js.map +0 -1
- package/dist/api/example.d.ts +0 -83
- package/dist/api/example.d.ts.map +0 -1
- package/dist/api/example.js +0 -85
- package/dist/api/example.js.map +0 -1
- package/dist/api/index.d.ts +0 -4
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/index.js +0 -6
- package/dist/api/index.js.map +0 -1
- package/dist/api/types/api.interface.d.ts +0 -108
- package/dist/api/types/api.interface.d.ts.map +0 -1
- package/dist/api/types/api.interface.js +0 -19
- package/dist/api/types/api.interface.js.map +0 -1
- package/dist/api/types/endpoint.interface.d.ts +0 -116
- package/dist/api/types/endpoint.interface.d.ts.map +0 -1
- package/dist/api/types/endpoint.interface.js +0 -2
- package/dist/api/types/endpoint.interface.js.map +0 -1
- package/dist/api/types/query.interface.d.ts +0 -87
- package/dist/api/types/query.interface.d.ts.map +0 -1
- package/dist/api/types/query.interface.js +0 -2
- package/dist/api/types/query.interface.js.map +0 -1
- package/dist/api/utils/api-helpers.d.ts +0 -22
- package/dist/api/utils/api-helpers.d.ts.map +0 -1
- package/dist/api/utils/api-helpers.js +0 -44
- package/dist/api/utils/api-helpers.js.map +0 -1
- package/dist/api/utils/create-header-context.d.ts +0 -10
- package/dist/api/utils/create-header-context.d.ts.map +0 -1
- package/dist/api/utils/create-header-context.js +0 -36
- package/dist/api/utils/create-header-context.js.map +0 -1
- package/dist/api/utils/endpoint-headers.d.ts +0 -23
- package/dist/api/utils/endpoint-headers.d.ts.map +0 -1
- package/dist/api/utils/endpoint-headers.js +0 -57
- package/dist/api/utils/endpoint-headers.js.map +0 -1
- package/dist/api/utils/fetch-base-query.d.ts +0 -9
- package/dist/api/utils/fetch-base-query.d.ts.map +0 -1
- package/dist/api/utils/fetch-base-query.js +0 -181
- package/dist/api/utils/fetch-base-query.js.map +0 -1
- package/dist/api/utils/file-utils.d.ts +0 -43
- package/dist/api/utils/file-utils.d.ts.map +0 -1
- package/dist/api/utils/file-utils.js +0 -108
- package/dist/api/utils/file-utils.js.map +0 -1
- package/dist/api/utils/get-cacheable-headers.d.ts +0 -8
- package/dist/api/utils/get-cacheable-headers.d.ts.map +0 -1
- package/dist/api/utils/get-cacheable-headers.js +0 -21
- package/dist/api/utils/get-cacheable-headers.js.map +0 -1
- package/dist/core/index.d.ts +0 -3
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -3
- package/dist/core/index.js.map +0 -1
- package/dist/core/selector/index.d.ts +0 -3
- package/dist/core/selector/index.d.ts.map +0 -1
- package/dist/core/selector/index.js +0 -2
- package/dist/core/selector/index.js.map +0 -1
- package/dist/core/selector/selector.interface.d.ts.map +0 -1
- package/dist/core/selector/selector.interface.js +0 -2
- package/dist/core/selector/selector.interface.js.map +0 -1
- package/dist/core/selector/selector.module.d.ts +0 -29
- package/dist/core/selector/selector.module.d.ts.map +0 -1
- package/dist/core/selector/selector.module.js +0 -467
- package/dist/core/selector/selector.module.js.map +0 -1
- package/dist/core/storage/adapters/base-storage.service.d.ts +0 -65
- package/dist/core/storage/adapters/base-storage.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/base-storage.service.js +0 -660
- package/dist/core/storage/adapters/base-storage.service.js.map +0 -1
- package/dist/core/storage/adapters/indexed-DB.service.d.ts +0 -63
- package/dist/core/storage/adapters/indexed-DB.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/indexed-DB.service.js +0 -595
- package/dist/core/storage/adapters/indexed-DB.service.js.map +0 -1
- package/dist/core/storage/adapters/indexed-DB.service.old.d.ts +0 -38
- package/dist/core/storage/adapters/indexed-DB.service.old.d.ts.map +0 -1
- package/dist/core/storage/adapters/indexed-DB.service.old.js +0 -318
- package/dist/core/storage/adapters/indexed-DB.service.old.js.map +0 -1
- package/dist/core/storage/adapters/local-storage.service.d.ts +0 -21
- package/dist/core/storage/adapters/local-storage.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/local-storage.service.js +0 -99
- package/dist/core/storage/adapters/local-storage.service.js.map +0 -1
- package/dist/core/storage/adapters/memory-storage.service.d.ts +0 -22
- package/dist/core/storage/adapters/memory-storage.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/memory-storage.service.js +0 -99
- package/dist/core/storage/adapters/memory-storage.service.js.map +0 -1
- package/dist/core/storage/adapters/path.utils.d.ts +0 -5
- package/dist/core/storage/adapters/path.utils.d.ts.map +0 -1
- package/dist/core/storage/adapters/path.utils.js +0 -35
- package/dist/core/storage/adapters/path.utils.js.map +0 -1
- package/dist/core/storage/index.d.ts +0 -11
- package/dist/core/storage/index.d.ts.map +0 -1
- package/dist/core/storage/index.js +0 -12
- package/dist/core/storage/index.js.map +0 -1
- package/dist/core/storage/middlewares/broadcast.middleware.d.ts +0 -9
- package/dist/core/storage/middlewares/broadcast.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/broadcast.middleware.js +0 -115
- package/dist/core/storage/middlewares/broadcast.middleware.js.map +0 -1
- package/dist/core/storage/middlewares/index.d.ts +0 -4
- package/dist/core/storage/middlewares/index.d.ts.map +0 -1
- package/dist/core/storage/middlewares/index.js +0 -3
- package/dist/core/storage/middlewares/index.js.map +0 -1
- package/dist/core/storage/middlewares/storage-batching.middleware.d.ts +0 -7
- package/dist/core/storage/middlewares/storage-batching.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/storage-batching.middleware.js +0 -36
- package/dist/core/storage/middlewares/storage-batching.middleware.js.map +0 -1
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.d.ts +0 -7
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.js +0 -46
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.js.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.interface.d.ts.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.interface.js +0 -2
- package/dist/core/storage/modules/plugin/plugin.interface.js.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.service.d.ts +0 -25
- package/dist/core/storage/modules/plugin/plugin.service.d.ts.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.service.js +0 -186
- package/dist/core/storage/modules/plugin/plugin.service.js.map +0 -1
- package/dist/core/storage/storage.interface.d.ts +0 -70
- package/dist/core/storage/storage.interface.d.ts.map +0 -1
- package/dist/core/storage/storage.interface.js +0 -10
- package/dist/core/storage/storage.interface.js.map +0 -1
- package/dist/core/storage/utils/batch.utils.d.ts +0 -33
- package/dist/core/storage/utils/batch.utils.d.ts.map +0 -1
- package/dist/core/storage/utils/batch.utils.js +0 -88
- package/dist/core/storage/utils/batch.utils.js.map +0 -1
- package/dist/core/storage/utils/broadcast.util.d.ts +0 -48
- package/dist/core/storage/utils/broadcast.util.d.ts.map +0 -1
- package/dist/core/storage/utils/broadcast.util.js +0 -162
- package/dist/core/storage/utils/broadcast.util.js.map +0 -1
- package/dist/core/storage/utils/cache.util.d.ts +0 -33
- package/dist/core/storage/utils/cache.util.d.ts.map +0 -1
- package/dist/core/storage/utils/cache.util.js +0 -47
- package/dist/core/storage/utils/cache.util.js.map +0 -1
- package/dist/core/storage/utils/middleware-module.d.ts +0 -46
- package/dist/core/storage/utils/middleware-module.d.ts.map +0 -1
- package/dist/core/storage/utils/middleware-module.js +0 -109
- package/dist/core/storage/utils/middleware-module.js.map +0 -1
- package/dist/core/storage/utils/storage-key.d.ts +0 -11
- package/dist/core/storage/utils/storage-key.d.ts.map +0 -1
- package/dist/core/storage/utils/storage-key.js +0 -21
- package/dist/core/storage/utils/storage-key.js.map +0 -1
- package/dist/core/storage/utils/storage.utils.d.ts +0 -17
- package/dist/core/storage/utils/storage.utils.d.ts.map +0 -1
- package/dist/core/storage/utils/storage.utils.js +0 -57
- package/dist/core/storage/utils/storage.utils.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/react/hooks/index.d.ts +0 -3
- package/dist/react/hooks/index.d.ts.map +0 -1
- package/dist/react/hooks/index.js +0 -3
- package/dist/react/hooks/index.js.map +0 -1
- package/dist/react/hooks/useSelector.d.ts +0 -22
- package/dist/react/hooks/useSelector.d.ts.map +0 -1
- package/dist/react/hooks/useSelector.js +0 -104
- package/dist/react/hooks/useSelector.js.map +0 -1
- package/dist/react/hooks/useStorageSubscribe.d.ts +0 -12
- package/dist/react/hooks/useStorageSubscribe.d.ts.map +0 -1
- package/dist/react/hooks/useStorageSubscribe.js +0 -49
- package/dist/react/hooks/useStorageSubscribe.js.map +0 -1
- package/dist/react/index.d.ts +0 -3
- package/dist/react/index.d.ts.map +0 -1
- package/dist/react/index.js +0 -3
- package/dist/react/index.js.map +0 -1
- package/dist/react/utils/createSynapseCtx.d.ts +0 -40
- package/dist/react/utils/createSynapseCtx.d.ts.map +0 -1
- package/dist/react/utils/createSynapseCtx.js +0 -125
- package/dist/react/utils/createSynapseCtx.js.map +0 -1
- package/dist/react/utils/index.d.ts +0 -2
- package/dist/react/utils/index.d.ts.map +0 -1
- package/dist/react/utils/index.js +0 -2
- package/dist/react/utils/index.js.map +0 -1
- package/dist/reactive/dispatcher/dispatcher.module.d.ts +0 -195
- package/dist/reactive/dispatcher/dispatcher.module.d.ts.map +0 -1
- package/dist/reactive/dispatcher/dispatcher.module.js +0 -288
- package/dist/reactive/dispatcher/dispatcher.module.js.map +0 -1
- package/dist/reactive/dispatcher/index.d.ts +0 -3
- package/dist/reactive/dispatcher/index.d.ts.map +0 -1
- package/dist/reactive/dispatcher/index.js +0 -3
- package/dist/reactive/dispatcher/index.js.map +0 -1
- package/dist/reactive/dispatcher/middlewares/index.d.ts +0 -2
- package/dist/reactive/dispatcher/middlewares/index.d.ts.map +0 -1
- package/dist/reactive/dispatcher/middlewares/index.js +0 -2
- package/dist/reactive/dispatcher/middlewares/index.js.map +0 -1
- package/dist/reactive/dispatcher/middlewares/logger.middleware.d.ts +0 -31
- package/dist/reactive/dispatcher/middlewares/logger.middleware.d.ts.map +0 -1
- package/dist/reactive/dispatcher/middlewares/logger.middleware.js +0 -126
- package/dist/reactive/dispatcher/middlewares/logger.middleware.js.map +0 -1
- package/dist/reactive/effects/effects.module.d.ts +0 -150
- package/dist/reactive/effects/effects.module.d.ts.map +0 -1
- package/dist/reactive/effects/effects.module.js +0 -277
- package/dist/reactive/effects/effects.module.js.map +0 -1
- package/dist/reactive/effects/index.d.ts +0 -2
- package/dist/reactive/effects/index.d.ts.map +0 -1
- package/dist/reactive/effects/index.js +0 -2
- package/dist/reactive/effects/index.js.map +0 -1
- package/dist/reactive/effects/utils/chunkRequestConsistent.d.ts +0 -12
- package/dist/reactive/effects/utils/chunkRequestConsistent.d.ts.map +0 -1
- package/dist/reactive/effects/utils/chunkRequestConsistent.js +0 -16
- package/dist/reactive/effects/utils/chunkRequestConsistent.js.map +0 -1
- package/dist/reactive/effects/utils/chunkRequestParallel.d.ts +0 -12
- package/dist/reactive/effects/utils/chunkRequestParallel.d.ts.map +0 -1
- package/dist/reactive/effects/utils/chunkRequestParallel.js +0 -13
- package/dist/reactive/effects/utils/chunkRequestParallel.js.map +0 -1
- package/dist/reactive/effects/utils/index.d.ts +0 -3
- package/dist/reactive/effects/utils/index.d.ts.map +0 -1
- package/dist/reactive/effects/utils/index.js +0 -3
- package/dist/reactive/effects/utils/index.js.map +0 -1
- package/dist/reactive/index.d.ts +0 -3
- package/dist/reactive/index.d.ts.map +0 -1
- package/dist/reactive/index.js +0 -3
- package/dist/reactive/index.js.map +0 -1
- package/dist/utils/createSynapse.d.ts +0 -92
- package/dist/utils/createSynapse.d.ts.map +0 -1
- package/dist/utils/createSynapse.js +0 -79
- package/dist/utils/createSynapse.js.map +0 -1
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -2
- package/dist/utils/index.js.map +0 -1
package/dist/core.js
ADDED
|
@@ -0,0 +1,2356 @@
|
|
|
1
|
+
// src/core/selector/selector.module.ts
|
|
2
|
+
var DEBUG = false;
|
|
3
|
+
var GLOBAL_SELECTOR_CACHE = /* @__PURE__ */ new Map();
|
|
4
|
+
function getStringHash(str) {
|
|
5
|
+
let hash = 0;
|
|
6
|
+
if (str.length === 0) return hash.toString(36);
|
|
7
|
+
for (let i = 0; i < str.length; i++) {
|
|
8
|
+
const char = str.charCodeAt(i);
|
|
9
|
+
hash = (hash << 5) - hash + char;
|
|
10
|
+
hash = hash & hash;
|
|
11
|
+
}
|
|
12
|
+
return Math.abs(hash).toString(36).substring(0, 6);
|
|
13
|
+
}
|
|
14
|
+
function defaultEquals(a, b) {
|
|
15
|
+
if (a === b) return true;
|
|
16
|
+
if (a == null || b == null) return false;
|
|
17
|
+
if (typeof a !== "object" && typeof a !== "function" && typeof b !== "object" && typeof b !== "function") {
|
|
18
|
+
return a === b;
|
|
19
|
+
}
|
|
20
|
+
if (typeof a !== typeof b) return false;
|
|
21
|
+
if (a instanceof Date && b instanceof Date) {
|
|
22
|
+
return a.getTime() === b.getTime();
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
25
|
+
if (a.length !== b.length) return false;
|
|
26
|
+
for (let i = 0; i < a.length; i++) {
|
|
27
|
+
if (!defaultEquals(a[i], b[i])) return false;
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
32
|
+
const keysA = Object.keys(a);
|
|
33
|
+
const keysB = Object.keys(b);
|
|
34
|
+
if (keysA.length !== keysB.length) return false;
|
|
35
|
+
return keysA.every((key) => {
|
|
36
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
|
|
37
|
+
return defaultEquals(a[key], b[key]);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
function memoizeSelector(selectorFn, equals = defaultEquals) {
|
|
43
|
+
let lastState;
|
|
44
|
+
let lastResult;
|
|
45
|
+
let hasResult = false;
|
|
46
|
+
return function memoized(state) {
|
|
47
|
+
if (!hasResult || lastState !== state) {
|
|
48
|
+
const newResult = selectorFn(state);
|
|
49
|
+
if (!hasResult || !equals(newResult, lastResult)) {
|
|
50
|
+
lastResult = newResult;
|
|
51
|
+
}
|
|
52
|
+
lastState = state;
|
|
53
|
+
hasResult = true;
|
|
54
|
+
}
|
|
55
|
+
return lastResult;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
var SelectorSubscription = class {
|
|
59
|
+
constructor(name, getState, equals = defaultEquals, logger) {
|
|
60
|
+
this.name = name;
|
|
61
|
+
this.equals = equals;
|
|
62
|
+
this.logger = logger;
|
|
63
|
+
this.id = name;
|
|
64
|
+
this.memoizedGetState = this.createMemoizedGetState(getState);
|
|
65
|
+
if (DEBUG) {
|
|
66
|
+
console.log(`[${this.id}] \u0421\u043E\u0437\u0434\u0430\u043D new SelectorSubscription`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
id;
|
|
70
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
71
|
+
lastValue;
|
|
72
|
+
memoizedGetState;
|
|
73
|
+
// Создает мемоизированную версию getState с кешированием результата
|
|
74
|
+
createMemoizedGetState(getState) {
|
|
75
|
+
let lastPromise = null;
|
|
76
|
+
let isExecuting = false;
|
|
77
|
+
return async () => {
|
|
78
|
+
if (isExecuting && lastPromise) {
|
|
79
|
+
return lastPromise;
|
|
80
|
+
}
|
|
81
|
+
isExecuting = true;
|
|
82
|
+
try {
|
|
83
|
+
lastPromise = getState();
|
|
84
|
+
return await lastPromise;
|
|
85
|
+
} finally {
|
|
86
|
+
isExecuting = false;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async notify() {
|
|
91
|
+
try {
|
|
92
|
+
const newValue = await this.memoizedGetState();
|
|
93
|
+
if (this.lastValue === void 0 || !this.equals(newValue, this.lastValue)) {
|
|
94
|
+
if (DEBUG) {
|
|
95
|
+
console.log(`[${this.id}] \u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C, notify()`, {
|
|
96
|
+
old: this.lastValue,
|
|
97
|
+
new: newValue
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
this.lastValue = newValue;
|
|
101
|
+
const promises = Array.from(this.subscribers).map(async (subscriber) => {
|
|
102
|
+
try {
|
|
103
|
+
await subscriber.notify(newValue);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
this.logger?.error(`[${this.id}] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0438 \u043F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A\u0430`, { error });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
await Promise.all(promises);
|
|
109
|
+
} else if (DEBUG) {
|
|
110
|
+
console.log(`[${this.id}] \u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043D\u0435 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C in notify(), \u043F\u0440\u043E\u043F\u0443\u0441\u043A \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F`);
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
this.logger?.error(`[${this.id}] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 notify()`, { error });
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
subscribe(subscriber) {
|
|
118
|
+
if (DEBUG) {
|
|
119
|
+
console.log(`[${this.id}] \u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E \u043D\u043E\u0432\u044B\u0439 \u043F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A, \u0432\u0441\u0435\u0433\u043E: ${this.subscribers.size + 1}`);
|
|
120
|
+
}
|
|
121
|
+
this.subscribers.add(subscriber);
|
|
122
|
+
if (this.lastValue !== void 0) {
|
|
123
|
+
Promise.resolve().then(() => {
|
|
124
|
+
try {
|
|
125
|
+
subscriber.notify(this.lastValue);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
this.logger?.error(`[${this.id}] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u0435\u0440\u0432\u043E\u043D\u0430\u0447\u0430\u043B\u044C\u043D\u043E\u043C \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0438`, { error });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
} else {
|
|
131
|
+
this.notify().catch((error) => {
|
|
132
|
+
this.logger?.error(`[${this.id}] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u0435\u0440\u0432\u043E\u043D\u0430\u0447\u0430\u043B\u044C\u043D\u043E\u043C \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0438`, { error });
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return () => {
|
|
136
|
+
if (DEBUG) {
|
|
137
|
+
console.log(`[${this.id}] \u041F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A \u0443\u0434\u0430\u043B\u0435\u043D, \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C: ${this.subscribers.size - 1}`);
|
|
138
|
+
}
|
|
139
|
+
this.subscribers.delete(subscriber);
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
cleanup() {
|
|
143
|
+
if (DEBUG) {
|
|
144
|
+
console.log(`[${this.id}] \u041E\u0447\u0438\u0441\u0442\u043A\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438, \u0431\u044B\u043B\u043E ${this.subscribers.size} \u043F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A\u043E\u0432`);
|
|
145
|
+
}
|
|
146
|
+
this.subscribers.clear();
|
|
147
|
+
this.lastValue = void 0;
|
|
148
|
+
}
|
|
149
|
+
getId() {
|
|
150
|
+
return this.id;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
var SelectorModule = class {
|
|
154
|
+
constructor(source, logger) {
|
|
155
|
+
this.source = source;
|
|
156
|
+
this.logger = logger;
|
|
157
|
+
this.storageName = source.name;
|
|
158
|
+
if (DEBUG) {
|
|
159
|
+
console.log(`\u0421\u043E\u0437\u0434\u0430\u043D SelectorModule \u0434\u043B\u044F \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430: ${this.storageName}`);
|
|
160
|
+
}
|
|
161
|
+
this.source.getState().then((state) => {
|
|
162
|
+
this.cachedState = state;
|
|
163
|
+
if (DEBUG) {
|
|
164
|
+
console.log(`\u041A\u044D\u0448\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E\u0435 \u043D\u0430\u0447\u0430\u043B\u044C\u043D\u043E\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u0434\u043B\u044F ${this.storageName}`);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
storageName;
|
|
169
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
170
|
+
cachedState;
|
|
171
|
+
localSelectorCache = /* @__PURE__ */ new Map();
|
|
172
|
+
// Флаг для батчинга обновлений
|
|
173
|
+
batchUpdateInProgress = false;
|
|
174
|
+
pendingUpdates = /* @__PURE__ */ new Set();
|
|
175
|
+
/**
|
|
176
|
+
* Генерирует имя для селектора на основе его типа и функции
|
|
177
|
+
*/
|
|
178
|
+
generateName(isSimpleSelector, selectorOrDeps, resultFnOrOptions) {
|
|
179
|
+
const type = isSimpleSelector ? "simple" : "combined";
|
|
180
|
+
let hash = "";
|
|
181
|
+
if (isSimpleSelector) {
|
|
182
|
+
const selectorStr = selectorOrDeps.toString();
|
|
183
|
+
hash = getStringHash(selectorStr);
|
|
184
|
+
} else {
|
|
185
|
+
const depsIds = selectorOrDeps.map((s) => s.getId()).join("_");
|
|
186
|
+
const resultFnStr = resultFnOrOptions.toString();
|
|
187
|
+
hash = getStringHash(depsIds + resultFnStr);
|
|
188
|
+
}
|
|
189
|
+
return `${this.storageName}_${type}_${hash}`;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Обрабатывает отложенные обновления, чтобы избежать каскадных уведомлений
|
|
193
|
+
*/
|
|
194
|
+
processPendingUpdates() {
|
|
195
|
+
if (this.pendingUpdates.size === 0 || this.batchUpdateInProgress) return;
|
|
196
|
+
this.batchUpdateInProgress = true;
|
|
197
|
+
setTimeout(async () => {
|
|
198
|
+
try {
|
|
199
|
+
const subscriptionsToUpdate = Array.from(this.pendingUpdates);
|
|
200
|
+
this.pendingUpdates.clear();
|
|
201
|
+
this.cachedState = await this.source.getState();
|
|
202
|
+
const updatePromises = subscriptionsToUpdate.map(async (id) => {
|
|
203
|
+
const subscription = this.subscriptions.get(id);
|
|
204
|
+
if (subscription) {
|
|
205
|
+
try {
|
|
206
|
+
return await subscription.notify();
|
|
207
|
+
} catch (error) {
|
|
208
|
+
this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u043F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A\u0430 ${id}`, { error });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return Promise.resolve();
|
|
212
|
+
});
|
|
213
|
+
await Promise.all(updatePromises);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
this.logger?.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0438 \u043E\u0436\u0438\u0434\u0430\u044E\u0449\u0438\u0445 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439", { error });
|
|
216
|
+
} finally {
|
|
217
|
+
this.batchUpdateInProgress = false;
|
|
218
|
+
if (this.pendingUpdates.size > 0) {
|
|
219
|
+
this.processPendingUpdates();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}, 0);
|
|
223
|
+
}
|
|
224
|
+
createSelector(selectorOrDeps, resultFnOrOptions, optionsArg) {
|
|
225
|
+
const isSimpleSelector = !Array.isArray(selectorOrDeps);
|
|
226
|
+
const options = isSimpleSelector ? resultFnOrOptions || {} : optionsArg || {};
|
|
227
|
+
const selectorId = options.name || this.generateName(isSimpleSelector, selectorOrDeps, isSimpleSelector ? void 0 : resultFnOrOptions);
|
|
228
|
+
if (this.localSelectorCache.has(selectorId)) {
|
|
229
|
+
if (DEBUG) {
|
|
230
|
+
console.log(`[${this.storageName}] Reusing cached selector: ${selectorId}`);
|
|
231
|
+
}
|
|
232
|
+
return this.localSelectorCache.get(selectorId).api;
|
|
233
|
+
}
|
|
234
|
+
if (GLOBAL_SELECTOR_CACHE.has(selectorId)) {
|
|
235
|
+
const cached = GLOBAL_SELECTOR_CACHE.get(selectorId);
|
|
236
|
+
cached.refCount++;
|
|
237
|
+
if (DEBUG) {
|
|
238
|
+
console.log(`[${this.storageName}] \u041F\u043E\u0432\u0442\u043E\u0440\u043D\u043E\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435 \u0433\u043B\u043E\u0431\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u044D\u0448\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E\u0433\u043E \u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440\u0430: ${selectorId}, refCount: ${cached.refCount}`);
|
|
239
|
+
}
|
|
240
|
+
return cached.api;
|
|
241
|
+
}
|
|
242
|
+
let result;
|
|
243
|
+
let dependencies;
|
|
244
|
+
let unsubscribeFunctions = [];
|
|
245
|
+
if (isSimpleSelector) {
|
|
246
|
+
const memoized = memoizeSelector(selectorOrDeps, options.equals || defaultEquals);
|
|
247
|
+
const created = this.createSimpleSelector(memoized, { ...options, name: selectorId, equals: options.equals || defaultEquals });
|
|
248
|
+
result = created.api;
|
|
249
|
+
unsubscribeFunctions = created.unsubscribeFunctions;
|
|
250
|
+
} else {
|
|
251
|
+
dependencies = selectorOrDeps;
|
|
252
|
+
const created = this.createCombinedSelector(dependencies, resultFnOrOptions, {
|
|
253
|
+
...options,
|
|
254
|
+
name: selectorId,
|
|
255
|
+
equals: options.equals || defaultEquals
|
|
256
|
+
});
|
|
257
|
+
result = created.api;
|
|
258
|
+
unsubscribeFunctions = created.unsubscribeFunctions;
|
|
259
|
+
}
|
|
260
|
+
this.localSelectorCache.set(selectorId, {
|
|
261
|
+
api: result,
|
|
262
|
+
dependencies,
|
|
263
|
+
unsubscribeFunctions
|
|
264
|
+
});
|
|
265
|
+
GLOBAL_SELECTOR_CACHE.set(selectorId, {
|
|
266
|
+
api: result,
|
|
267
|
+
refCount: 1,
|
|
268
|
+
unsubscribeFunctions
|
|
269
|
+
});
|
|
270
|
+
if (DEBUG) {
|
|
271
|
+
console.log(`[${this.storageName}] \u0421\u043E\u0437\u0434\u0430\u043D \u043D\u043E\u0432\u044B\u0439 \u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440: ${selectorId}`);
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
createSimpleSelector(selector, options) {
|
|
276
|
+
if (DEBUG) {
|
|
277
|
+
console.log(`[${this.storageName}] \u0421\u043E\u0437\u0434\u0430\u043D \u043F\u0440\u043E\u0441\u0442\u043E\u0439 \u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440: ${options.name}`);
|
|
278
|
+
}
|
|
279
|
+
const getState = async () => {
|
|
280
|
+
if (this.cachedState) {
|
|
281
|
+
return selector(this.cachedState);
|
|
282
|
+
}
|
|
283
|
+
const state = await this.source.getState();
|
|
284
|
+
this.cachedState = state;
|
|
285
|
+
return selector(state);
|
|
286
|
+
};
|
|
287
|
+
const subscription = new SelectorSubscription(options.name, getState, options.equals || defaultEquals, this.logger);
|
|
288
|
+
const id = subscription.getId();
|
|
289
|
+
this.subscriptions.set(id, subscription);
|
|
290
|
+
const unsubscribeFromStorage = this.source.subscribeToAll(async (event) => {
|
|
291
|
+
if (event?.type === "storage:update") {
|
|
292
|
+
if (DEBUG) {
|
|
293
|
+
console.log(`[${id}] \u041F\u043E\u043B\u0443\u0447\u0435\u043D\u043E \u0441\u043E\u0431\u044B\u0442\u0438\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430`);
|
|
294
|
+
}
|
|
295
|
+
this.pendingUpdates.add(id);
|
|
296
|
+
this.processPendingUpdates();
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
const unsubscribeFunctions = [unsubscribeFromStorage];
|
|
300
|
+
return {
|
|
301
|
+
api: {
|
|
302
|
+
select: () => getState(),
|
|
303
|
+
subscribe: (subscriber) => {
|
|
304
|
+
return subscription.subscribe(subscriber);
|
|
305
|
+
},
|
|
306
|
+
getId: () => id
|
|
307
|
+
},
|
|
308
|
+
unsubscribeFunctions
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
createCombinedSelector(selectors, resultFn, options) {
|
|
312
|
+
const memoizedResultFn = memoizeSelector((args) => resultFn(...args), options.equals || defaultEquals);
|
|
313
|
+
const getState = async () => {
|
|
314
|
+
const values = await Promise.all(selectors.map((s) => s.select()));
|
|
315
|
+
return memoizedResultFn(values);
|
|
316
|
+
};
|
|
317
|
+
const subscription = new SelectorSubscription(options.name, getState, options.equals || defaultEquals, this.logger);
|
|
318
|
+
const id = subscription.getId();
|
|
319
|
+
this.subscriptions.set(id, subscription);
|
|
320
|
+
let debounceTimer = null;
|
|
321
|
+
const triggerUpdate = () => {
|
|
322
|
+
if (debounceTimer !== null) {
|
|
323
|
+
clearTimeout(debounceTimer);
|
|
324
|
+
}
|
|
325
|
+
debounceTimer = setTimeout(() => {
|
|
326
|
+
debounceTimer = null;
|
|
327
|
+
subscription.notify().catch((error) => this.logger?.error(`[${id}] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043E\u0431\u044A\u0435\u0434\u0438\u043D\u0435\u043D\u043D\u043E\u043C \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0438:`, { error }));
|
|
328
|
+
}, 10);
|
|
329
|
+
};
|
|
330
|
+
const unsubscribeFunctions = selectors.map(
|
|
331
|
+
(selector) => selector.subscribe({
|
|
332
|
+
notify: () => {
|
|
333
|
+
triggerUpdate();
|
|
334
|
+
}
|
|
335
|
+
})
|
|
336
|
+
);
|
|
337
|
+
return {
|
|
338
|
+
api: {
|
|
339
|
+
select: () => getState(),
|
|
340
|
+
subscribe: (subscriber) => {
|
|
341
|
+
return subscription.subscribe(subscriber);
|
|
342
|
+
},
|
|
343
|
+
getId: () => id
|
|
344
|
+
},
|
|
345
|
+
unsubscribeFunctions
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
destroy() {
|
|
349
|
+
if (DEBUG) {
|
|
350
|
+
console.log(`[${this.storageName}] \u041D\u0430\u0447\u0430\u043B\u043E\u0441\u044C \u0443\u043D\u0438\u0447\u0442\u043E\u0436\u0435\u043D\u0438\u0435 SelectorModule`);
|
|
351
|
+
}
|
|
352
|
+
this.subscriptions.forEach((sub) => sub.cleanup());
|
|
353
|
+
this.subscriptions.clear();
|
|
354
|
+
this.cachedState = void 0;
|
|
355
|
+
this.pendingUpdates.clear();
|
|
356
|
+
this.localSelectorCache.forEach((cached) => {
|
|
357
|
+
cached.unsubscribeFunctions.forEach((unsub) => unsub());
|
|
358
|
+
});
|
|
359
|
+
const keysToCheck = /* @__PURE__ */ new Set();
|
|
360
|
+
this.localSelectorCache.forEach((_, key) => {
|
|
361
|
+
keysToCheck.add(key);
|
|
362
|
+
});
|
|
363
|
+
this.localSelectorCache.clear();
|
|
364
|
+
keysToCheck.forEach((key) => {
|
|
365
|
+
const globalCached = GLOBAL_SELECTOR_CACHE.get(key);
|
|
366
|
+
if (globalCached) {
|
|
367
|
+
globalCached.refCount--;
|
|
368
|
+
if (globalCached.refCount <= 0) {
|
|
369
|
+
globalCached.unsubscribeFunctions.forEach((unsub) => unsub());
|
|
370
|
+
GLOBAL_SELECTOR_CACHE.delete(key);
|
|
371
|
+
if (DEBUG) {
|
|
372
|
+
console.log(`[${this.storageName}] \u0423\u0434\u0430\u043B\u0435\u043D \u0441\u0435\u043B\u0435\u043A\u0442\u043E\u0440 \u0438\u0437 \u0433\u043B\u043E\u0431\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u044D\u0448\u0430: ${key}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
if (DEBUG) {
|
|
378
|
+
console.log(`[${this.storageName}] \u0423\u043D\u0438\u0447\u0442\u043E\u0436\u0435\u043D`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// src/core/storage/modules/plugin/plugin.service.ts
|
|
384
|
+
var StoragePluginModule = class {
|
|
385
|
+
constructor(parentExecutor, logger, storageName = "default") {
|
|
386
|
+
this.parentExecutor = parentExecutor;
|
|
387
|
+
this.logger = logger;
|
|
388
|
+
this.storageName = storageName;
|
|
389
|
+
}
|
|
390
|
+
plugins = /* @__PURE__ */ new Map();
|
|
391
|
+
createContext(metadata) {
|
|
392
|
+
return {
|
|
393
|
+
storageName: this.storageName,
|
|
394
|
+
timestamp: Date.now(),
|
|
395
|
+
metadata
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
async add(plugin) {
|
|
399
|
+
if (this.plugins.has(plugin.name)) {
|
|
400
|
+
this.logger?.warn(`\u041F\u043B\u0430\u0433\u0438\u043D ${plugin.name} \u0443\u0436\u0435 \u0431\u044B\u043B \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D`);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
try {
|
|
404
|
+
await plugin.initialize?.();
|
|
405
|
+
this.plugins.set(plugin.name, plugin);
|
|
406
|
+
this.logger?.info("\u041F\u043B\u0430\u0433\u0438\u043D \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D", { name: plugin.name });
|
|
407
|
+
} catch (error) {
|
|
408
|
+
throw error;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async remove(name) {
|
|
412
|
+
const plugin = this.plugins.get(name);
|
|
413
|
+
if (plugin) {
|
|
414
|
+
await plugin.destroy?.();
|
|
415
|
+
this.plugins.delete(name);
|
|
416
|
+
this.logger?.info("\u041F\u043B\u0430\u0433\u0438\u043D \u0443\u0434\u0430\u043B\u0435\u043D", { name });
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
get(name) {
|
|
420
|
+
return this.plugins.get(name);
|
|
421
|
+
}
|
|
422
|
+
getAll() {
|
|
423
|
+
return Array.from(this.plugins.values());
|
|
424
|
+
}
|
|
425
|
+
async initialize() {
|
|
426
|
+
for (const plugin of this.plugins.values()) {
|
|
427
|
+
await plugin.initialize?.();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async destroy() {
|
|
431
|
+
await Promise.all(Array.from(this.plugins.values()).map((plugin) => plugin.destroy?.() ?? Promise.resolve()));
|
|
432
|
+
this.plugins.clear();
|
|
433
|
+
}
|
|
434
|
+
async executeBeforeSet(value, metadata) {
|
|
435
|
+
let result = value;
|
|
436
|
+
const context = this.createContext(metadata);
|
|
437
|
+
if (this.parentExecutor) {
|
|
438
|
+
result = await this.parentExecutor.executeBeforeSet(result, context);
|
|
439
|
+
}
|
|
440
|
+
for (const plugin of this.plugins.values()) {
|
|
441
|
+
if (plugin.onBeforeSet) {
|
|
442
|
+
try {
|
|
443
|
+
result = await plugin.onBeforeSet(result, context);
|
|
444
|
+
} catch (error) {
|
|
445
|
+
this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onBeforeSet`, { error });
|
|
446
|
+
throw error;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return result;
|
|
451
|
+
}
|
|
452
|
+
async executeAfterSet(key, value, metadata) {
|
|
453
|
+
let result = value;
|
|
454
|
+
const context = this.createContext(metadata);
|
|
455
|
+
if (this.parentExecutor) {
|
|
456
|
+
result = await this.parentExecutor.executeAfterSet(key, result, context);
|
|
457
|
+
}
|
|
458
|
+
for (const plugin of this.plugins.values()) {
|
|
459
|
+
if (plugin.onAfterSet) {
|
|
460
|
+
try {
|
|
461
|
+
result = await plugin.onAfterSet(key, result, context);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onAfterSet`, { key, error });
|
|
464
|
+
throw error;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return result;
|
|
469
|
+
}
|
|
470
|
+
async executeBeforeGet(key, metadata) {
|
|
471
|
+
let processedKey = key;
|
|
472
|
+
const context = this.createContext(metadata);
|
|
473
|
+
if (this.parentExecutor) {
|
|
474
|
+
processedKey = await this.parentExecutor.executeBeforeGet(processedKey, context);
|
|
475
|
+
}
|
|
476
|
+
for (const plugin of this.plugins.values()) {
|
|
477
|
+
if (plugin.onBeforeGet) {
|
|
478
|
+
try {
|
|
479
|
+
processedKey = await plugin.onBeforeGet(processedKey, context);
|
|
480
|
+
} catch (error) {
|
|
481
|
+
this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onBeforeGet`, { key, error });
|
|
482
|
+
throw error;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return processedKey;
|
|
487
|
+
}
|
|
488
|
+
async executeAfterGet(key, value, metadata) {
|
|
489
|
+
let result = value;
|
|
490
|
+
const context = this.createContext(metadata);
|
|
491
|
+
if (this.parentExecutor) {
|
|
492
|
+
result = await this.parentExecutor.executeAfterGet(key, result, context);
|
|
493
|
+
}
|
|
494
|
+
console.log("executeAfterGet", key, value, metadata);
|
|
495
|
+
for (const plugin of this.plugins.values()) {
|
|
496
|
+
if (plugin.onAfterGet) {
|
|
497
|
+
try {
|
|
498
|
+
result = await plugin.onAfterGet(key, result, context);
|
|
499
|
+
} catch (error) {
|
|
500
|
+
this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onAfterGet`, { key, error });
|
|
501
|
+
throw error;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return result;
|
|
506
|
+
}
|
|
507
|
+
async executeBeforeDelete(key, metadata) {
|
|
508
|
+
let canDelete = true;
|
|
509
|
+
const context = this.createContext(metadata);
|
|
510
|
+
if (this.parentExecutor) {
|
|
511
|
+
canDelete = await this.parentExecutor.executeBeforeDelete(key, context);
|
|
512
|
+
}
|
|
513
|
+
for (const plugin of this.plugins.values()) {
|
|
514
|
+
if (plugin.onBeforeDelete) {
|
|
515
|
+
try {
|
|
516
|
+
canDelete = await plugin.onBeforeDelete(key, context) && canDelete;
|
|
517
|
+
} catch (error) {
|
|
518
|
+
this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onBeforeDelete`, { key, error });
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return canDelete;
|
|
524
|
+
}
|
|
525
|
+
async executeAfterDelete(key, metadata) {
|
|
526
|
+
const context = this.createContext(metadata);
|
|
527
|
+
if (this.parentExecutor) {
|
|
528
|
+
await this.parentExecutor.executeAfterDelete(key, context);
|
|
529
|
+
}
|
|
530
|
+
for (const plugin of this.plugins.values()) {
|
|
531
|
+
if (plugin.onAfterDelete) {
|
|
532
|
+
try {
|
|
533
|
+
await plugin.onAfterDelete(key, context);
|
|
534
|
+
} catch (error) {
|
|
535
|
+
this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onAfterDelete`, { key, error });
|
|
536
|
+
throw error;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async executeOnClear(metadata) {
|
|
542
|
+
const context = this.createContext(metadata);
|
|
543
|
+
if (this.parentExecutor) {
|
|
544
|
+
await this.parentExecutor.executeOnClear(context);
|
|
545
|
+
}
|
|
546
|
+
for (const plugin of this.plugins.values()) {
|
|
547
|
+
if (plugin.onClear) {
|
|
548
|
+
try {
|
|
549
|
+
await plugin.onClear(context);
|
|
550
|
+
} catch (error) {
|
|
551
|
+
this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043B\u0430\u0433\u0438\u043D\u0435 ${plugin.name} onClear`, { error });
|
|
552
|
+
throw error;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// src/core/storage/storage.interface.ts
|
|
560
|
+
var StorageEvents = /* @__PURE__ */ ((StorageEvents2) => {
|
|
561
|
+
StorageEvents2["STORAGE_UPDATE"] = "storage:update";
|
|
562
|
+
StorageEvents2["STORAGE_DELETE"] = "storage:delete";
|
|
563
|
+
StorageEvents2["STORAGE_PATCH"] = "storage:patch";
|
|
564
|
+
StorageEvents2["STORAGE_SELECT"] = "storage:select";
|
|
565
|
+
StorageEvents2["STORAGE_CLEAR"] = "storage:clear";
|
|
566
|
+
StorageEvents2["STORAGE_DESTROY"] = "storage:destroy";
|
|
567
|
+
return StorageEvents2;
|
|
568
|
+
})(StorageEvents || {});
|
|
569
|
+
|
|
570
|
+
// src/core/storage/utils/broadcast.util.ts
|
|
571
|
+
var SyncBroadcastChannel = class {
|
|
572
|
+
channel;
|
|
573
|
+
tabId;
|
|
574
|
+
messageHandlers;
|
|
575
|
+
syncHandler;
|
|
576
|
+
debug;
|
|
577
|
+
syncTimeoutMs = 1e3;
|
|
578
|
+
pendingSyncRequests;
|
|
579
|
+
constructor(channelName, options = {}) {
|
|
580
|
+
this.channel = new BroadcastChannel(channelName);
|
|
581
|
+
this.tabId = crypto.randomUUID();
|
|
582
|
+
this.messageHandlers = /* @__PURE__ */ new Set();
|
|
583
|
+
this.debug = options.debug ?? false;
|
|
584
|
+
this.pendingSyncRequests = /* @__PURE__ */ new Map();
|
|
585
|
+
this.channel.onmessage = this.handleMessage.bind(this);
|
|
586
|
+
this.channel.onmessageerror = this.handleError.bind(this);
|
|
587
|
+
}
|
|
588
|
+
log(...args) {
|
|
589
|
+
if (this.debug) {
|
|
590
|
+
console.log(`[SyncBroadcastChannel][${this.tabId}]`, ...args);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
error(...args) {
|
|
594
|
+
console.error(`[SyncBroadcastChannel][${this.tabId}]`, ...args);
|
|
595
|
+
}
|
|
596
|
+
async handleMessage(event) {
|
|
597
|
+
const message = event.data;
|
|
598
|
+
if (message.senderId === this.tabId) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
if (message.type === "SYNC_REQUEST") {
|
|
602
|
+
if (this.syncHandler) {
|
|
603
|
+
try {
|
|
604
|
+
const state = await this.syncHandler();
|
|
605
|
+
this.postMessage("SYNC_RESPONSE", state, message.senderId);
|
|
606
|
+
} catch (error) {
|
|
607
|
+
this.error("Error handling sync request:", error);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
if (message.type === "SYNC_RESPONSE") {
|
|
613
|
+
const request = this.pendingSyncRequests.get(this.tabId);
|
|
614
|
+
if (request) {
|
|
615
|
+
clearTimeout(request.timeout);
|
|
616
|
+
this.pendingSyncRequests.delete(this.tabId);
|
|
617
|
+
request.resolve(message.payload);
|
|
618
|
+
}
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
for (const handler of this.messageHandlers) {
|
|
622
|
+
try {
|
|
623
|
+
await handler(message);
|
|
624
|
+
} catch (error) {
|
|
625
|
+
this.error("Error in message handler:", error);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
handleError(event) {
|
|
630
|
+
this.error("Channel error:", event);
|
|
631
|
+
}
|
|
632
|
+
postMessage(type, payload, targetId) {
|
|
633
|
+
const message = {
|
|
634
|
+
type,
|
|
635
|
+
payload,
|
|
636
|
+
senderId: this.tabId,
|
|
637
|
+
timestamp: Date.now()
|
|
638
|
+
};
|
|
639
|
+
this.channel.postMessage(message);
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Подписка на сообщения канала
|
|
643
|
+
*/
|
|
644
|
+
subscribe(handler) {
|
|
645
|
+
this.messageHandlers.add(handler);
|
|
646
|
+
return () => this.messageHandlers.delete(handler);
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Установка обработчика запросов на синхронизацию
|
|
650
|
+
*/
|
|
651
|
+
setSyncHandler(handler) {
|
|
652
|
+
this.syncHandler = handler;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Отправка сообщения всем подписчикам
|
|
656
|
+
*/
|
|
657
|
+
broadcast(type, payload) {
|
|
658
|
+
this.postMessage(type, payload);
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Запрос синхронизации данных с других вкладок
|
|
662
|
+
*/
|
|
663
|
+
async requestSync() {
|
|
664
|
+
return new Promise((resolve, reject) => {
|
|
665
|
+
const timeout = setTimeout(() => {
|
|
666
|
+
this.pendingSyncRequests.delete(this.tabId);
|
|
667
|
+
resolve(null);
|
|
668
|
+
}, this.syncTimeoutMs);
|
|
669
|
+
this.pendingSyncRequests.set(this.tabId, { resolve, reject, timeout });
|
|
670
|
+
this.postMessage("SYNC_REQUEST", { type: "sync" });
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Закрытие канала
|
|
675
|
+
*/
|
|
676
|
+
close() {
|
|
677
|
+
for (const [, request] of this.pendingSyncRequests) {
|
|
678
|
+
clearTimeout(request.timeout);
|
|
679
|
+
request.reject(new Error("Channel closed"));
|
|
680
|
+
}
|
|
681
|
+
this.pendingSyncRequests.clear();
|
|
682
|
+
this.messageHandlers.clear();
|
|
683
|
+
this.syncHandler = void 0;
|
|
684
|
+
this.channel.close();
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
// src/core/storage/middlewares/broadcast.middleware.ts
|
|
689
|
+
var broadcastMiddleware = (props) => {
|
|
690
|
+
const { storageName, storageType } = props;
|
|
691
|
+
const channelName = `${storageType}-${storageName}`;
|
|
692
|
+
const channel = new SyncBroadcastChannel(channelName, { debug: true });
|
|
693
|
+
return {
|
|
694
|
+
name: "sync",
|
|
695
|
+
setup: (api) => {
|
|
696
|
+
if (storageType === "memory") {
|
|
697
|
+
channel.setSyncHandler(async () => {
|
|
698
|
+
const state = await api.getState();
|
|
699
|
+
const updates = Object.entries(state).map(([key, value]) => ({
|
|
700
|
+
key,
|
|
701
|
+
value
|
|
702
|
+
}));
|
|
703
|
+
const action = {
|
|
704
|
+
type: "update",
|
|
705
|
+
key: "*",
|
|
706
|
+
value: updates,
|
|
707
|
+
metadata: {
|
|
708
|
+
batchUpdate: true,
|
|
709
|
+
timestamp: Date.now()
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
return action;
|
|
713
|
+
});
|
|
714
|
+
channel.requestSync().then(async (action) => {
|
|
715
|
+
if (action?.type === "update" && Array.isArray(action.value)) {
|
|
716
|
+
try {
|
|
717
|
+
const validUpdates = action.value.every((update) => update && typeof update === "object" && "key" in update && "value" in update);
|
|
718
|
+
if (!validUpdates) {
|
|
719
|
+
console.error("[Sync Response] Invalid updates structure:", action.value);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
await api.storage.doUpdate(action.value);
|
|
723
|
+
action.value.forEach(({ key, value }) => {
|
|
724
|
+
api.storage.notifySubscribers(key, value);
|
|
725
|
+
});
|
|
726
|
+
api.storage.notifySubscribers("*", {
|
|
727
|
+
type: "storage:update" /* STORAGE_UPDATE */,
|
|
728
|
+
value: action.value,
|
|
729
|
+
source: "broadcast"
|
|
730
|
+
});
|
|
731
|
+
} catch (error) {
|
|
732
|
+
console.error("[Sync Response] Error applying updates:", error);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
return channel.subscribe(async (message) => {
|
|
738
|
+
const { type, payload } = message;
|
|
739
|
+
if (storageType === "memory") {
|
|
740
|
+
switch (type) {
|
|
741
|
+
case "set":
|
|
742
|
+
await api.storage.doSet(payload.key, payload.value);
|
|
743
|
+
api.storage.notifySubscribers(payload.key, payload.value);
|
|
744
|
+
break;
|
|
745
|
+
case "update":
|
|
746
|
+
if (Array.isArray(payload.value)) {
|
|
747
|
+
await api.storage.doUpdate(payload.value);
|
|
748
|
+
payload.value.forEach(({ key, value }) => {
|
|
749
|
+
api.storage.notifySubscribers(key, value);
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
break;
|
|
753
|
+
case "delete":
|
|
754
|
+
await api.storage.doDelete(payload.key);
|
|
755
|
+
api.storage.notifySubscribers(payload.key, void 0);
|
|
756
|
+
break;
|
|
757
|
+
case "clear":
|
|
758
|
+
await api.storage.doClear();
|
|
759
|
+
api.storage.notifySubscribers("*", {
|
|
760
|
+
type: "storage:update" /* STORAGE_UPDATE */,
|
|
761
|
+
value: {},
|
|
762
|
+
source: "broadcast"
|
|
763
|
+
});
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
api.storage.notifySubscribers("*", {
|
|
767
|
+
type: "storage:update" /* STORAGE_UPDATE */,
|
|
768
|
+
key: payload?.key,
|
|
769
|
+
value: payload?.value,
|
|
770
|
+
source: "broadcast"
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
},
|
|
775
|
+
reducer: (api) => (next) => async (action) => {
|
|
776
|
+
const result = await next(action);
|
|
777
|
+
if (["set", "delete", "clear", "update"].includes(action.type)) {
|
|
778
|
+
channel.broadcast(action.type, action);
|
|
779
|
+
}
|
|
780
|
+
return result;
|
|
781
|
+
},
|
|
782
|
+
cleanup: () => {
|
|
783
|
+
channel.close();
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
// src/core/storage/utils/storage-key.ts
|
|
789
|
+
var StorageKey = class {
|
|
790
|
+
constructor(value, isRawKey = false) {
|
|
791
|
+
this.value = value;
|
|
792
|
+
this.isRawKey = isRawKey;
|
|
793
|
+
}
|
|
794
|
+
toString() {
|
|
795
|
+
return this.value;
|
|
796
|
+
}
|
|
797
|
+
toJSON() {
|
|
798
|
+
return this.value;
|
|
799
|
+
}
|
|
800
|
+
valueOf() {
|
|
801
|
+
return this.value;
|
|
802
|
+
}
|
|
803
|
+
isUnparseable() {
|
|
804
|
+
return this.isRawKey;
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
// src/core/storage/utils/batch.utils.ts
|
|
809
|
+
var BatchProcessor = class {
|
|
810
|
+
options;
|
|
811
|
+
queues = /* @__PURE__ */ new Map();
|
|
812
|
+
timeouts = /* @__PURE__ */ new Map();
|
|
813
|
+
constructor(options) {
|
|
814
|
+
this.options = {
|
|
815
|
+
batchSize: options.batchSize ?? 10,
|
|
816
|
+
batchDelay: options.batchDelay ?? 300,
|
|
817
|
+
onBatch: options.onBatch ?? (async () => {
|
|
818
|
+
}),
|
|
819
|
+
getSegmentKey: options.getSegmentKey ?? (() => "default"),
|
|
820
|
+
shouldBatch: options.shouldBatch ?? (() => true),
|
|
821
|
+
mergeItems: options.mergeItems ?? ((items) => items)
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
async add(action, baseOperation) {
|
|
825
|
+
if (!this.options.shouldBatch(action)) {
|
|
826
|
+
return baseOperation();
|
|
827
|
+
}
|
|
828
|
+
return new Promise((resolve, reject) => {
|
|
829
|
+
const segment = this.options.getSegmentKey(action);
|
|
830
|
+
this.addToQueue(segment, { action, baseOperation, resolve, reject });
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
addToQueue(segment, queueItem) {
|
|
834
|
+
let queue = this.queues.get(segment);
|
|
835
|
+
if (!queue) {
|
|
836
|
+
queue = [];
|
|
837
|
+
this.queues.set(segment, queue);
|
|
838
|
+
}
|
|
839
|
+
queue.push(queueItem);
|
|
840
|
+
this.clearSegmentTimeout(segment);
|
|
841
|
+
if (queue.length >= this.options.batchSize) {
|
|
842
|
+
this.processBatch(segment);
|
|
843
|
+
} else {
|
|
844
|
+
this.setSegmentTimeout(segment);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
async processBatch(segment) {
|
|
848
|
+
const queue = this.queues.get(segment);
|
|
849
|
+
if (!queue?.length) return;
|
|
850
|
+
this.queues.delete(segment);
|
|
851
|
+
this.clearSegmentTimeout(segment);
|
|
852
|
+
try {
|
|
853
|
+
const mergedActions = this.options.mergeItems(queue.map((item) => item.action));
|
|
854
|
+
for (let i = 0; i < mergedActions.length; i++) {
|
|
855
|
+
const result = await queue[i].baseOperation();
|
|
856
|
+
queue[i].resolve(result);
|
|
857
|
+
}
|
|
858
|
+
} catch (error) {
|
|
859
|
+
queue.forEach(({ reject }) => reject(error));
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
clearSegmentTimeout(segment) {
|
|
863
|
+
const timeout = this.timeouts.get(segment);
|
|
864
|
+
if (timeout) {
|
|
865
|
+
clearTimeout(timeout);
|
|
866
|
+
this.timeouts.delete(segment);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
setSegmentTimeout(segment) {
|
|
870
|
+
const timeout = setTimeout(() => {
|
|
871
|
+
this.processBatch(segment);
|
|
872
|
+
}, this.options.batchDelay);
|
|
873
|
+
this.timeouts.set(segment, timeout);
|
|
874
|
+
}
|
|
875
|
+
async flush() {
|
|
876
|
+
const segments = Array.from(this.queues.keys());
|
|
877
|
+
await Promise.all(segments.map((segment) => this.processBatch(segment)));
|
|
878
|
+
}
|
|
879
|
+
clear() {
|
|
880
|
+
this.queues.clear();
|
|
881
|
+
Array.from(this.timeouts.values()).forEach(clearTimeout);
|
|
882
|
+
this.timeouts.clear();
|
|
883
|
+
}
|
|
884
|
+
getState() {
|
|
885
|
+
const state = {};
|
|
886
|
+
this.queues.forEach((queue, segment) => {
|
|
887
|
+
state[segment] = queue.length;
|
|
888
|
+
});
|
|
889
|
+
return state;
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
// src/core/storage/middlewares/storage-batching.middleware.ts
|
|
894
|
+
var batchingMiddleware = (options = {}) => {
|
|
895
|
+
const batchProcessor = new BatchProcessor({
|
|
896
|
+
batchSize: options.batchSize,
|
|
897
|
+
batchDelay: options.batchDelay,
|
|
898
|
+
shouldBatch: (action) => {
|
|
899
|
+
if (action.type === "get" || action.type === "keys") return false;
|
|
900
|
+
return true;
|
|
901
|
+
},
|
|
902
|
+
getSegmentKey: (action) => action.key || "default",
|
|
903
|
+
mergeItems: (actions) => {
|
|
904
|
+
return actions.reduce((acc, action) => {
|
|
905
|
+
if (action.type === "set") {
|
|
906
|
+
const existingIndex = acc.findIndex((existing) => existing.type === "set" && existing.key === action.key);
|
|
907
|
+
if (existingIndex !== -1) {
|
|
908
|
+
acc[existingIndex] = action;
|
|
909
|
+
} else {
|
|
910
|
+
acc.push(action);
|
|
911
|
+
}
|
|
912
|
+
} else {
|
|
913
|
+
acc.push(action);
|
|
914
|
+
}
|
|
915
|
+
return acc;
|
|
916
|
+
}, []);
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
return {
|
|
920
|
+
name: "batching",
|
|
921
|
+
setup: () => {
|
|
922
|
+
},
|
|
923
|
+
reducer: (api) => (next) => (action) => batchProcessor.add(action, () => next(action))
|
|
924
|
+
};
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
// src/core/storage/middlewares/storage-shallow-compare.middleware.ts
|
|
928
|
+
var shallowCompareMiddleware = (options = {}) => {
|
|
929
|
+
const {
|
|
930
|
+
comparator = (prev, next) => {
|
|
931
|
+
if (prev === next) return true;
|
|
932
|
+
if (typeof prev !== "object" || typeof next !== "object" || prev === null || next === null) {
|
|
933
|
+
return prev === next;
|
|
934
|
+
}
|
|
935
|
+
const keysA = Object.keys(prev);
|
|
936
|
+
const keysB = Object.keys(next);
|
|
937
|
+
if (keysA.length !== keysB.length) return false;
|
|
938
|
+
return keysA.every((key) => Object.prototype.hasOwnProperty.call(next, key) && prev[key] === next[key]);
|
|
939
|
+
},
|
|
940
|
+
segments = []
|
|
941
|
+
} = options;
|
|
942
|
+
const valueCache = /* @__PURE__ */ new Map();
|
|
943
|
+
return {
|
|
944
|
+
name: "shallow-compare",
|
|
945
|
+
setup: (api) => {
|
|
946
|
+
},
|
|
947
|
+
reducer: (api) => (next) => async (action) => {
|
|
948
|
+
if (action.type !== "set" || segments.length && !segments.includes(action.metadata?.segment ?? "default")) {
|
|
949
|
+
return next(action);
|
|
950
|
+
}
|
|
951
|
+
const cacheKey = action.key;
|
|
952
|
+
const prevValue = valueCache.get(cacheKey);
|
|
953
|
+
const nextValue = action.value;
|
|
954
|
+
if (prevValue !== void 0 && comparator(prevValue, nextValue)) {
|
|
955
|
+
console.log("ShallowCompare: \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u0438\u0434\u0435\u043D\u0442\u0438\u0447\u043D\u044B, \u043F\u0440\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u043C \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u044E", { key: cacheKey, value: nextValue });
|
|
956
|
+
return {
|
|
957
|
+
...prevValue,
|
|
958
|
+
__metadata: {
|
|
959
|
+
valueNotChanged: true,
|
|
960
|
+
// Этот флаг будет проверяться в BaseStorage перед notifySubscribers
|
|
961
|
+
originalValue: prevValue
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
const result = await next(action);
|
|
966
|
+
valueCache.set(cacheKey, nextValue);
|
|
967
|
+
return result;
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
// src/core/storage/utils/middleware-module.ts
|
|
973
|
+
var MiddlewareModule = class {
|
|
974
|
+
middlewares = [];
|
|
975
|
+
api;
|
|
976
|
+
initialized = false;
|
|
977
|
+
dispatchFn;
|
|
978
|
+
constructor(storage) {
|
|
979
|
+
this.api = {
|
|
980
|
+
dispatch: async (action) => this.dispatch(action),
|
|
981
|
+
getState: () => storage.getState(),
|
|
982
|
+
storage: {
|
|
983
|
+
doGet: storage.doGet.bind(storage),
|
|
984
|
+
doSet: storage.doSet.bind(storage),
|
|
985
|
+
doUpdate: storage.doUpdate.bind(storage),
|
|
986
|
+
doDelete: storage.doDelete.bind(storage),
|
|
987
|
+
doClear: storage.doClear.bind(storage),
|
|
988
|
+
doKeys: storage.doKeys.bind(storage),
|
|
989
|
+
notifySubscribers: storage.notifySubscribers.bind(storage)
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
async baseOperation(action) {
|
|
994
|
+
const { processed: _, ...metadata } = action.metadata || {};
|
|
995
|
+
const cleanAction = { ...action, metadata };
|
|
996
|
+
switch (cleanAction.type) {
|
|
997
|
+
case "get": {
|
|
998
|
+
return this.api.storage.doGet(cleanAction.key);
|
|
999
|
+
}
|
|
1000
|
+
case "set": {
|
|
1001
|
+
await this.api.storage.doSet(cleanAction.key, cleanAction.value);
|
|
1002
|
+
return this.api.storage.doGet(cleanAction.key);
|
|
1003
|
+
}
|
|
1004
|
+
case "update": {
|
|
1005
|
+
if (Array.isArray(cleanAction.value)) {
|
|
1006
|
+
await this.api.storage.doUpdate(cleanAction.value);
|
|
1007
|
+
return this.api.storage.doGet("");
|
|
1008
|
+
}
|
|
1009
|
+
return cleanAction.value;
|
|
1010
|
+
}
|
|
1011
|
+
case "delete": {
|
|
1012
|
+
return this.api.storage.doDelete(cleanAction.key);
|
|
1013
|
+
}
|
|
1014
|
+
case "clear": {
|
|
1015
|
+
return this.api.storage.doClear();
|
|
1016
|
+
}
|
|
1017
|
+
case "init": {
|
|
1018
|
+
const currentState = await this.api.storage.doGet("");
|
|
1019
|
+
if (Object.keys(currentState || {}).length > 0) {
|
|
1020
|
+
return currentState;
|
|
1021
|
+
}
|
|
1022
|
+
if (cleanAction.value) {
|
|
1023
|
+
await this.api.storage.doSet("", cleanAction.value);
|
|
1024
|
+
return this.api.storage.doGet("");
|
|
1025
|
+
}
|
|
1026
|
+
return currentState;
|
|
1027
|
+
}
|
|
1028
|
+
case "keys": {
|
|
1029
|
+
return this.api.storage.doKeys();
|
|
1030
|
+
}
|
|
1031
|
+
default: {
|
|
1032
|
+
throw new Error(`Unknown action type: ${cleanAction.type}`);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
initializeMiddlewares() {
|
|
1037
|
+
if (this.initialized) return;
|
|
1038
|
+
let chain = this.baseOperation.bind(this);
|
|
1039
|
+
for (const middleware of [...this.middlewares].reverse()) {
|
|
1040
|
+
const nextChain = chain;
|
|
1041
|
+
chain = async (action) => {
|
|
1042
|
+
if (action.metadata?.processed) {
|
|
1043
|
+
return nextChain(action);
|
|
1044
|
+
}
|
|
1045
|
+
const actionWithMeta = {
|
|
1046
|
+
...action,
|
|
1047
|
+
metadata: {
|
|
1048
|
+
...action.metadata,
|
|
1049
|
+
processed: true,
|
|
1050
|
+
timestamp: action.metadata?.timestamp || Date.now()
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
return middleware.reducer(this.api)(nextChain)(actionWithMeta);
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
this.dispatchFn = chain;
|
|
1057
|
+
this.initialized = true;
|
|
1058
|
+
}
|
|
1059
|
+
use(middleware) {
|
|
1060
|
+
if (middleware.setup) {
|
|
1061
|
+
middleware.setup(this.api);
|
|
1062
|
+
}
|
|
1063
|
+
this.middlewares.push(middleware);
|
|
1064
|
+
this.initialized = false;
|
|
1065
|
+
}
|
|
1066
|
+
async dispatch(action) {
|
|
1067
|
+
if (!this.initialized) {
|
|
1068
|
+
this.initializeMiddlewares();
|
|
1069
|
+
}
|
|
1070
|
+
try {
|
|
1071
|
+
return this.dispatchFn(action);
|
|
1072
|
+
} catch (error) {
|
|
1073
|
+
console.error("Error in middleware chain:", error);
|
|
1074
|
+
throw error;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
|
|
1079
|
+
// src/core/storage/adapters/path.utils.ts
|
|
1080
|
+
function parsePath(path) {
|
|
1081
|
+
if (path instanceof StorageKey && path.isUnparseable()) {
|
|
1082
|
+
return [path.toString()];
|
|
1083
|
+
}
|
|
1084
|
+
const pathStr = path.toString();
|
|
1085
|
+
return pathStr.replace(/\[/g, ".").replace(/\]/g, "").split(".").filter(Boolean);
|
|
1086
|
+
}
|
|
1087
|
+
function getValueByPath(obj, path) {
|
|
1088
|
+
const parts = parsePath(path);
|
|
1089
|
+
return parts.reduce((curr, key) => curr === void 0 ? void 0 : curr[key], obj);
|
|
1090
|
+
}
|
|
1091
|
+
function setValueByPath(obj, path, value) {
|
|
1092
|
+
if (path === "") return value;
|
|
1093
|
+
const parts = parsePath(path);
|
|
1094
|
+
if (path instanceof StorageKey && path.isUnparseable()) {
|
|
1095
|
+
obj[path.toString()] = value;
|
|
1096
|
+
return obj;
|
|
1097
|
+
}
|
|
1098
|
+
const lastKey = parts.pop();
|
|
1099
|
+
const target = parts.reduce((curr, key) => {
|
|
1100
|
+
const nextKey = parts[parts.indexOf(key) + 1];
|
|
1101
|
+
const shouldBeArray = !Number.isNaN(Number(nextKey));
|
|
1102
|
+
if (!(key in curr)) {
|
|
1103
|
+
curr[key] = shouldBeArray ? [] : {};
|
|
1104
|
+
}
|
|
1105
|
+
return curr[key];
|
|
1106
|
+
}, obj);
|
|
1107
|
+
target[lastKey] = value;
|
|
1108
|
+
return obj;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// src/core/storage/adapters/base-storage.service.ts
|
|
1112
|
+
var BaseStorage = class _BaseStorage {
|
|
1113
|
+
constructor(config, pluginExecutor, eventEmitter, logger) {
|
|
1114
|
+
this.config = config;
|
|
1115
|
+
this.pluginExecutor = pluginExecutor;
|
|
1116
|
+
this.eventEmitter = eventEmitter;
|
|
1117
|
+
this.logger = logger;
|
|
1118
|
+
this.name = config.name;
|
|
1119
|
+
this.middlewareModule = new MiddlewareModule({
|
|
1120
|
+
getState: this.getState.bind(this),
|
|
1121
|
+
// Предоставляем базовые операции хранилища
|
|
1122
|
+
doGet: this.doGet.bind(this),
|
|
1123
|
+
doSet: this.doSet.bind(this),
|
|
1124
|
+
doUpdate: this.doUpdate.bind(this),
|
|
1125
|
+
doDelete: this.doDelete.bind(this),
|
|
1126
|
+
doClear: this.doClear.bind(this),
|
|
1127
|
+
doKeys: this.doKeys.bind(this),
|
|
1128
|
+
// Предоставляем методы для работы с подписчиками
|
|
1129
|
+
notifySubscribers: this.notifySubscribers.bind(this),
|
|
1130
|
+
// Предоставляем плагины и эмиттер
|
|
1131
|
+
pluginExecutor: this.pluginExecutor,
|
|
1132
|
+
eventEmitter: this.eventEmitter,
|
|
1133
|
+
logger: this.logger
|
|
1134
|
+
});
|
|
1135
|
+
this.initializeMiddlewares();
|
|
1136
|
+
}
|
|
1137
|
+
// Константа для глобальной подписки
|
|
1138
|
+
static GLOBAL_SUBSCRIPTION_KEY = "*";
|
|
1139
|
+
name;
|
|
1140
|
+
selectorPathCache = /* @__PURE__ */ new WeakMap();
|
|
1141
|
+
middlewareModule;
|
|
1142
|
+
initializedMiddlewares = null;
|
|
1143
|
+
subscribers = /* @__PURE__ */ new Map();
|
|
1144
|
+
initializeMiddlewares() {
|
|
1145
|
+
if (this.config.middlewares && !this.initializedMiddlewares) {
|
|
1146
|
+
this.initializedMiddlewares = this.config.middlewares(() => this.getDefaultMiddleware());
|
|
1147
|
+
this.initializedMiddlewares.forEach((middleware) => this.middlewareModule.use(middleware));
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
getDefaultMiddleware() {
|
|
1151
|
+
return {
|
|
1152
|
+
batching: (options = {}) => batchingMiddleware(options),
|
|
1153
|
+
shallowCompare: (options = {}) => shallowCompareMiddleware(options)
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
async initializeWithMiddlewares() {
|
|
1157
|
+
try {
|
|
1158
|
+
const state = await this.getState();
|
|
1159
|
+
const hasExistingState = Object.keys(state).length > 0;
|
|
1160
|
+
if (!hasExistingState && this.config.initialState) {
|
|
1161
|
+
await this.middlewareModule.dispatch({
|
|
1162
|
+
type: "init",
|
|
1163
|
+
value: this.config.initialState
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
} catch (error) {
|
|
1167
|
+
this.logger?.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u0438 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430", { error });
|
|
1168
|
+
throw error;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
async get(key) {
|
|
1172
|
+
try {
|
|
1173
|
+
const metadata = { operation: "get", timestamp: Date.now(), key };
|
|
1174
|
+
const middlewareResult = await this.middlewareModule.dispatch({
|
|
1175
|
+
type: "get",
|
|
1176
|
+
key,
|
|
1177
|
+
metadata
|
|
1178
|
+
});
|
|
1179
|
+
const finalResult = await this.pluginExecutor?.executeAfterGet(key, middlewareResult, metadata) ?? middlewareResult;
|
|
1180
|
+
await this.emitEvent({
|
|
1181
|
+
type: "storage:select" /* STORAGE_SELECT */,
|
|
1182
|
+
payload: { key, value: finalResult }
|
|
1183
|
+
});
|
|
1184
|
+
return finalResult;
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
this.logger?.error("Error getting value", { key, error });
|
|
1187
|
+
throw error;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
async set(key, value) {
|
|
1191
|
+
try {
|
|
1192
|
+
const metadata = { operation: "set", timestamp: Date.now(), key };
|
|
1193
|
+
const processedValue = await this.pluginExecutor?.executeBeforeSet(value, metadata) ?? value;
|
|
1194
|
+
const middlewareResult = await this.middlewareModule.dispatch({
|
|
1195
|
+
type: "set",
|
|
1196
|
+
key,
|
|
1197
|
+
value: processedValue,
|
|
1198
|
+
metadata
|
|
1199
|
+
});
|
|
1200
|
+
const valueNotChanged = middlewareResult?.__metadata?.valueNotChanged === true;
|
|
1201
|
+
let finalResult;
|
|
1202
|
+
if (valueNotChanged && middlewareResult?.__metadata?.originalValue !== void 0) {
|
|
1203
|
+
finalResult = middlewareResult.__metadata.originalValue;
|
|
1204
|
+
} else {
|
|
1205
|
+
finalResult = await this.pluginExecutor?.executeAfterSet(key, middlewareResult, metadata) ?? middlewareResult;
|
|
1206
|
+
}
|
|
1207
|
+
if (!valueNotChanged) {
|
|
1208
|
+
const keyStr = key.toString();
|
|
1209
|
+
const changedPaths = [keyStr];
|
|
1210
|
+
this.notifySubscribers(key, finalResult);
|
|
1211
|
+
this.notifySubscribers(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY, {
|
|
1212
|
+
type: "storage:update" /* STORAGE_UPDATE */,
|
|
1213
|
+
key,
|
|
1214
|
+
value: finalResult,
|
|
1215
|
+
changedPaths
|
|
1216
|
+
});
|
|
1217
|
+
await this.emitEvent({
|
|
1218
|
+
type: "storage:update" /* STORAGE_UPDATE */,
|
|
1219
|
+
payload: {
|
|
1220
|
+
key,
|
|
1221
|
+
value: finalResult,
|
|
1222
|
+
changedPaths
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
} catch (error) {
|
|
1227
|
+
this.logger?.error("Error setting value", { key, error });
|
|
1228
|
+
throw error;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
async update(updater) {
|
|
1232
|
+
try {
|
|
1233
|
+
const metadata = { operation: "update", timestamp: Date.now() };
|
|
1234
|
+
const currentState = await this.getState();
|
|
1235
|
+
const newState = structuredClone(currentState);
|
|
1236
|
+
updater(newState);
|
|
1237
|
+
const changedPaths = this.findChangedPaths(currentState, newState);
|
|
1238
|
+
if (changedPaths.size === 0) {
|
|
1239
|
+
if (this.logger?.debug) {
|
|
1240
|
+
this.logger.debug("No changes detected in update");
|
|
1241
|
+
}
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
if (this.logger?.debug) {
|
|
1245
|
+
this.logger.debug("Changed paths:", { paths: Array.from(changedPaths) });
|
|
1246
|
+
}
|
|
1247
|
+
const changedTopLevelKeys = /* @__PURE__ */ new Set();
|
|
1248
|
+
for (const path of changedPaths) {
|
|
1249
|
+
const topLevelKey = path.split(".")[0];
|
|
1250
|
+
changedTopLevelKeys.add(topLevelKey);
|
|
1251
|
+
}
|
|
1252
|
+
const updates = await Promise.all(
|
|
1253
|
+
Array.from(changedTopLevelKeys).map(async (key) => {
|
|
1254
|
+
const keyMetadata = { ...metadata, key };
|
|
1255
|
+
const processedValue = await this.pluginExecutor?.executeBeforeSet(newState[key], keyMetadata) ?? newState[key];
|
|
1256
|
+
return { key, value: processedValue };
|
|
1257
|
+
})
|
|
1258
|
+
);
|
|
1259
|
+
const result = await this.middlewareModule.dispatch({
|
|
1260
|
+
type: "update",
|
|
1261
|
+
value: updates,
|
|
1262
|
+
metadata: {
|
|
1263
|
+
...metadata,
|
|
1264
|
+
batchUpdate: true,
|
|
1265
|
+
changedPaths: Array.from(changedPaths)
|
|
1266
|
+
}
|
|
1267
|
+
});
|
|
1268
|
+
let updatedValues = {};
|
|
1269
|
+
if (Array.isArray(result)) {
|
|
1270
|
+
result.forEach((update) => {
|
|
1271
|
+
if (update && typeof update === "object" && "key" in update && "value" in update) {
|
|
1272
|
+
updatedValues[update.key] = update.value;
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
} else if (result && typeof result === "object") {
|
|
1276
|
+
updatedValues = { ...result };
|
|
1277
|
+
}
|
|
1278
|
+
const actuallyChangedKeys = Object.keys(updatedValues).filter((key) => !this.isEqual(currentState[key], updatedValues[key]));
|
|
1279
|
+
if (actuallyChangedKeys.length === 0) {
|
|
1280
|
+
if (this.logger?.debug) {
|
|
1281
|
+
this.logger.debug("No actual changes after middleware processing");
|
|
1282
|
+
}
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
const finalUpdates = {};
|
|
1286
|
+
actuallyChangedKeys.forEach((key) => {
|
|
1287
|
+
finalUpdates[key] = updatedValues[key];
|
|
1288
|
+
});
|
|
1289
|
+
if (this.logger?.debug) {
|
|
1290
|
+
this.logger.debug("Notifying subscribers about changes:", { keys: actuallyChangedKeys });
|
|
1291
|
+
}
|
|
1292
|
+
this.notifySubscribers(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY, {
|
|
1293
|
+
type: "storage:update" /* STORAGE_UPDATE */,
|
|
1294
|
+
key: actuallyChangedKeys,
|
|
1295
|
+
value: finalUpdates,
|
|
1296
|
+
changedPaths: Array.from(changedPaths)
|
|
1297
|
+
// Добавляем информацию о всех изменившихся путях
|
|
1298
|
+
});
|
|
1299
|
+
for (const path of changedPaths) {
|
|
1300
|
+
try {
|
|
1301
|
+
const topLevelKey = path.split(".")[0];
|
|
1302
|
+
if (topLevelKey in finalUpdates) {
|
|
1303
|
+
let value;
|
|
1304
|
+
if (path === topLevelKey) {
|
|
1305
|
+
value = finalUpdates[topLevelKey];
|
|
1306
|
+
} else {
|
|
1307
|
+
const restPath = path.substring(topLevelKey.length + 1);
|
|
1308
|
+
value = getValueByPath(finalUpdates[topLevelKey], restPath);
|
|
1309
|
+
}
|
|
1310
|
+
if (value !== void 0) {
|
|
1311
|
+
this.notifySubscribers(path, value);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
} catch (error) {
|
|
1315
|
+
this.logger?.error("Error notifying path subscribers", { path, error });
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
await this.emitEvent({
|
|
1319
|
+
type: "storage:update" /* STORAGE_UPDATE */,
|
|
1320
|
+
payload: {
|
|
1321
|
+
state: finalUpdates,
|
|
1322
|
+
key: actuallyChangedKeys,
|
|
1323
|
+
changedPaths: Array.from(changedPaths)
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
this.logger?.error("Error updating state", { error });
|
|
1328
|
+
throw error;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
async delete(key) {
|
|
1332
|
+
try {
|
|
1333
|
+
const metadata = { operation: "delete", timestamp: Date.now(), key };
|
|
1334
|
+
if (await this.pluginExecutor?.executeBeforeDelete(key, metadata)) {
|
|
1335
|
+
const middlewareResult = await this.middlewareModule.dispatch({
|
|
1336
|
+
type: "delete",
|
|
1337
|
+
key,
|
|
1338
|
+
metadata
|
|
1339
|
+
});
|
|
1340
|
+
await this.pluginExecutor?.executeAfterDelete(key, metadata);
|
|
1341
|
+
const keyStr = key.toString();
|
|
1342
|
+
const changedPaths = [keyStr];
|
|
1343
|
+
this.notifySubscribers(key, void 0);
|
|
1344
|
+
this.notifySubscribers(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY, {
|
|
1345
|
+
type: "storage:update" /* STORAGE_UPDATE */,
|
|
1346
|
+
key,
|
|
1347
|
+
value: void 0,
|
|
1348
|
+
result: middlewareResult,
|
|
1349
|
+
changedPaths
|
|
1350
|
+
});
|
|
1351
|
+
await this.emitEvent({
|
|
1352
|
+
type: "storage:update" /* STORAGE_UPDATE */,
|
|
1353
|
+
payload: {
|
|
1354
|
+
key,
|
|
1355
|
+
value: void 0,
|
|
1356
|
+
result: middlewareResult,
|
|
1357
|
+
changedPaths
|
|
1358
|
+
}
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
this.logger?.error("Error deleting value", { key, error });
|
|
1363
|
+
throw error;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
async clear() {
|
|
1367
|
+
try {
|
|
1368
|
+
this.pluginExecutor?.executeOnClear();
|
|
1369
|
+
await this.middlewareModule.dispatch({
|
|
1370
|
+
type: "clear"
|
|
1371
|
+
});
|
|
1372
|
+
} catch (error) {
|
|
1373
|
+
this.logger?.error("Error clearing storage", { error });
|
|
1374
|
+
throw error;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
async keys() {
|
|
1378
|
+
try {
|
|
1379
|
+
return await this.middlewareModule.dispatch({
|
|
1380
|
+
type: "keys"
|
|
1381
|
+
});
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
this.logger?.error("Error getting keys", { error });
|
|
1384
|
+
throw error;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
async has(key) {
|
|
1388
|
+
try {
|
|
1389
|
+
return await this.doHas(key);
|
|
1390
|
+
} catch (error) {
|
|
1391
|
+
this.logger?.error("Error checking value existence", { key, error });
|
|
1392
|
+
throw error;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
async getState() {
|
|
1396
|
+
try {
|
|
1397
|
+
const value = await this.doGet("");
|
|
1398
|
+
return value || {};
|
|
1399
|
+
} catch (error) {
|
|
1400
|
+
this.logger?.error("Error getting state", { error });
|
|
1401
|
+
throw error;
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
// Вспомогательный метод для подписки на все изменения
|
|
1405
|
+
subscribeToAll(callback) {
|
|
1406
|
+
if (!this.subscribers.has(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY)) {
|
|
1407
|
+
this.subscribers.set(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY, /* @__PURE__ */ new Set());
|
|
1408
|
+
}
|
|
1409
|
+
this.subscribers.get(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY).add(callback);
|
|
1410
|
+
return () => {
|
|
1411
|
+
const subscribers = this.subscribers.get(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY);
|
|
1412
|
+
if (subscribers) {
|
|
1413
|
+
subscribers.delete(callback);
|
|
1414
|
+
if (subscribers.size === 0) {
|
|
1415
|
+
this.subscribers.delete(_BaseStorage.GLOBAL_SUBSCRIPTION_KEY);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
subscribe(keyOrSelector, callback) {
|
|
1421
|
+
if (typeof keyOrSelector === "string") {
|
|
1422
|
+
return this.subscribeByKey(keyOrSelector, callback);
|
|
1423
|
+
}
|
|
1424
|
+
return this.subscribeBySelector(keyOrSelector, callback);
|
|
1425
|
+
}
|
|
1426
|
+
async destroy() {
|
|
1427
|
+
try {
|
|
1428
|
+
await this.clear();
|
|
1429
|
+
await this.doDestroy();
|
|
1430
|
+
if (this.initializedMiddlewares) {
|
|
1431
|
+
await Promise.all(
|
|
1432
|
+
this.initializedMiddlewares.map(async (middleware) => {
|
|
1433
|
+
if ("cleanup" in middleware) {
|
|
1434
|
+
await middleware.cleanup?.();
|
|
1435
|
+
}
|
|
1436
|
+
})
|
|
1437
|
+
);
|
|
1438
|
+
this.initializedMiddlewares = null;
|
|
1439
|
+
}
|
|
1440
|
+
await this.emitEvent({
|
|
1441
|
+
type: "storage:destroy" /* STORAGE_DESTROY */
|
|
1442
|
+
});
|
|
1443
|
+
} catch (error) {
|
|
1444
|
+
this.logger?.error("Error destroying storage", { error });
|
|
1445
|
+
throw error;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
// Вспомогательные методы
|
|
1449
|
+
subscribeByKey(key, callback) {
|
|
1450
|
+
if (!this.subscribers.has(key)) {
|
|
1451
|
+
this.subscribers.set(key, /* @__PURE__ */ new Set());
|
|
1452
|
+
}
|
|
1453
|
+
let initialValueSent = false;
|
|
1454
|
+
this.subscribers.get(key).add(callback);
|
|
1455
|
+
this.get(key).then((value) => {
|
|
1456
|
+
try {
|
|
1457
|
+
if (!initialValueSent) {
|
|
1458
|
+
initialValueSent = true;
|
|
1459
|
+
callback(value);
|
|
1460
|
+
}
|
|
1461
|
+
} catch (error) {
|
|
1462
|
+
this.logger?.error("Error in initial callback", { key, error });
|
|
1463
|
+
}
|
|
1464
|
+
});
|
|
1465
|
+
return () => {
|
|
1466
|
+
const subscribers = this.subscribers.get(key);
|
|
1467
|
+
if (subscribers) {
|
|
1468
|
+
subscribers.delete(callback);
|
|
1469
|
+
if (subscribers.size === 0) {
|
|
1470
|
+
this.subscribers.delete(key);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
createDummyState() {
|
|
1476
|
+
const handler = {
|
|
1477
|
+
get: (target, prop) => {
|
|
1478
|
+
target[prop] = target[prop] || new Proxy({}, handler);
|
|
1479
|
+
return target[prop];
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
return new Proxy({}, handler);
|
|
1483
|
+
}
|
|
1484
|
+
isEqual(a, b) {
|
|
1485
|
+
if (a === b) return true;
|
|
1486
|
+
if (a == null || b == null) return a === b;
|
|
1487
|
+
const typeA = typeof a;
|
|
1488
|
+
const typeB = typeof b;
|
|
1489
|
+
if (typeA !== typeB) return false;
|
|
1490
|
+
if (typeA !== "object") return a === b;
|
|
1491
|
+
if (a instanceof Date && b instanceof Date) {
|
|
1492
|
+
return a.getTime() === b.getTime();
|
|
1493
|
+
}
|
|
1494
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
1495
|
+
if (a.length !== b.length) return false;
|
|
1496
|
+
for (let i = 0; i < a.length; i++) {
|
|
1497
|
+
if (!this.isEqual(a[i], b[i])) return false;
|
|
1498
|
+
}
|
|
1499
|
+
return true;
|
|
1500
|
+
}
|
|
1501
|
+
const keysA = Object.keys(a);
|
|
1502
|
+
const keysB = Object.keys(b);
|
|
1503
|
+
if (keysA.length !== keysB.length) return false;
|
|
1504
|
+
return keysA.every((key) => Object.prototype.hasOwnProperty.call(b, key) && this.isEqual(a[key], b[key]));
|
|
1505
|
+
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Возвращает полный путь, а не только корневой ключ
|
|
1508
|
+
*/
|
|
1509
|
+
extractPath(selector, dummyState) {
|
|
1510
|
+
if (this.selectorPathCache.has(selector)) {
|
|
1511
|
+
return this.selectorPathCache.get(selector);
|
|
1512
|
+
}
|
|
1513
|
+
const accessedPaths = [];
|
|
1514
|
+
const createProxyHandler = (path = "") => ({
|
|
1515
|
+
get: (target, prop) => {
|
|
1516
|
+
if (typeof prop === "symbol") {
|
|
1517
|
+
return Reflect.get(target, prop);
|
|
1518
|
+
}
|
|
1519
|
+
const currentPath = path ? `${path}.${prop}` : prop;
|
|
1520
|
+
accessedPaths.push(currentPath);
|
|
1521
|
+
return new Proxy({}, createProxyHandler(currentPath));
|
|
1522
|
+
},
|
|
1523
|
+
// Обработка опциональной цепочки (?.)
|
|
1524
|
+
has: (target, prop) => {
|
|
1525
|
+
return true;
|
|
1526
|
+
},
|
|
1527
|
+
// Поддержка для Array.prototype.map и других операций над массивами
|
|
1528
|
+
ownKeys: () => [],
|
|
1529
|
+
getOwnPropertyDescriptor: () => ({
|
|
1530
|
+
configurable: true,
|
|
1531
|
+
enumerable: true
|
|
1532
|
+
}),
|
|
1533
|
+
apply: (target, thisArg, args) => {
|
|
1534
|
+
return new Proxy(() => {
|
|
1535
|
+
}, createProxyHandler(path));
|
|
1536
|
+
}
|
|
1537
|
+
});
|
|
1538
|
+
try {
|
|
1539
|
+
selector(new Proxy(dummyState, createProxyHandler()));
|
|
1540
|
+
} catch (error) {
|
|
1541
|
+
}
|
|
1542
|
+
if (accessedPaths.length === 0) return "";
|
|
1543
|
+
accessedPaths.sort((a, b) => b.length - a.length);
|
|
1544
|
+
this.selectorPathCache.set(selector, accessedPaths[0]);
|
|
1545
|
+
return accessedPaths[0];
|
|
1546
|
+
}
|
|
1547
|
+
notifySubscribers(key, value) {
|
|
1548
|
+
const keyStr = key.toString();
|
|
1549
|
+
const exactSubscribers = this.subscribers.get(keyStr);
|
|
1550
|
+
if (exactSubscribers?.size) {
|
|
1551
|
+
const subscribersCopy = new Set(exactSubscribers);
|
|
1552
|
+
subscribersCopy.forEach((callback) => {
|
|
1553
|
+
try {
|
|
1554
|
+
callback(value);
|
|
1555
|
+
} catch (error) {
|
|
1556
|
+
this.logger?.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A\u0435 \u043D\u0430 \u043A\u043E\u043B\u0431\u044D\u043A", { key: keyStr, error });
|
|
1557
|
+
}
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Метод для определения изменившихся путей между двумя объектами
|
|
1563
|
+
*/
|
|
1564
|
+
findChangedPaths(oldObj, newObj, prefix = "", changedPaths = /* @__PURE__ */ new Set(), visited = /* @__PURE__ */ new WeakMap()) {
|
|
1565
|
+
if (oldObj === newObj) return changedPaths;
|
|
1566
|
+
if (typeof oldObj !== "object" || typeof newObj !== "object" || oldObj === null || newObj === null) {
|
|
1567
|
+
if (oldObj !== newObj) {
|
|
1568
|
+
changedPaths.add(prefix || "");
|
|
1569
|
+
}
|
|
1570
|
+
return changedPaths;
|
|
1571
|
+
}
|
|
1572
|
+
if (visited.has(oldObj)) return changedPaths;
|
|
1573
|
+
visited.set(oldObj, true);
|
|
1574
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(oldObj || {}), ...Object.keys(newObj || {})]);
|
|
1575
|
+
for (const key of allKeys) {
|
|
1576
|
+
const oldValue = oldObj[key];
|
|
1577
|
+
const newValue = newObj[key];
|
|
1578
|
+
if (oldValue === newValue) continue;
|
|
1579
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
1580
|
+
if (oldValue && newValue && typeof oldValue === "object" && typeof newValue === "object" && !Array.isArray(oldValue) && !Array.isArray(newValue)) {
|
|
1581
|
+
this.findChangedPaths(oldValue, newValue, path, changedPaths, visited);
|
|
1582
|
+
} else if (Array.isArray(oldValue) && Array.isArray(newValue)) {
|
|
1583
|
+
if (!this.isEqual(oldValue, newValue)) {
|
|
1584
|
+
changedPaths.add(path);
|
|
1585
|
+
}
|
|
1586
|
+
} else if (!this.isEqual(oldValue, newValue)) {
|
|
1587
|
+
changedPaths.add(path);
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
return changedPaths;
|
|
1591
|
+
}
|
|
1592
|
+
subscribeBySelector(pathSelector, callback) {
|
|
1593
|
+
const dummyState = this.createDummyState();
|
|
1594
|
+
const fullPath = this.extractPath(pathSelector, dummyState);
|
|
1595
|
+
if (this.logger?.debug) {
|
|
1596
|
+
this.logger.debug("Subscribing to path:", { path: fullPath });
|
|
1597
|
+
}
|
|
1598
|
+
const wrappedCallback = async (value) => {
|
|
1599
|
+
try {
|
|
1600
|
+
if (value === void 0 || value === null) {
|
|
1601
|
+
const currentState2 = await this.getState();
|
|
1602
|
+
const selectedValue2 = pathSelector(currentState2);
|
|
1603
|
+
callback(selectedValue2);
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
if (typeof value !== "object" || value === null) {
|
|
1607
|
+
callback(value);
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
const currentState = await this.getState();
|
|
1611
|
+
const selectedValue = pathSelector(currentState);
|
|
1612
|
+
callback(selectedValue);
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
this.logger?.error("Error in selector callback", { path: fullPath, error });
|
|
1615
|
+
callback(value);
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
if (!fullPath) {
|
|
1619
|
+
return this.subscribeToAll(() => {
|
|
1620
|
+
this.getState().then((state) => {
|
|
1621
|
+
callback(pathSelector(state));
|
|
1622
|
+
});
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
return this.subscribeByKey(fullPath, wrappedCallback);
|
|
1626
|
+
}
|
|
1627
|
+
async emitEvent(event) {
|
|
1628
|
+
try {
|
|
1629
|
+
await this.eventEmitter?.emit({
|
|
1630
|
+
...event,
|
|
1631
|
+
metadata: {
|
|
1632
|
+
...event.metadata || {},
|
|
1633
|
+
timestamp: Date.now(),
|
|
1634
|
+
storageName: this.name
|
|
1635
|
+
}
|
|
1636
|
+
});
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
this.logger?.error("Error emitting event", { event, error });
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
|
|
1643
|
+
// src/core/storage/adapters/indexed-DB.service.ts
|
|
1644
|
+
var IndexedDBManager = class _IndexedDBManager {
|
|
1645
|
+
constructor(dbName, dbVersion, logger) {
|
|
1646
|
+
this.dbName = dbName;
|
|
1647
|
+
this.logger = logger;
|
|
1648
|
+
this.dbVersion = dbVersion;
|
|
1649
|
+
}
|
|
1650
|
+
static instances = /* @__PURE__ */ new Map();
|
|
1651
|
+
db = null;
|
|
1652
|
+
initPromise = null;
|
|
1653
|
+
storeNames = /* @__PURE__ */ new Set();
|
|
1654
|
+
dbVersion;
|
|
1655
|
+
static getInstance(dbName, dbVersion = 1, logger) {
|
|
1656
|
+
if (!_IndexedDBManager.instances.has(dbName)) {
|
|
1657
|
+
_IndexedDBManager.instances.set(dbName, new _IndexedDBManager(dbName, dbVersion, logger));
|
|
1658
|
+
}
|
|
1659
|
+
const instance = _IndexedDBManager.instances.get(dbName);
|
|
1660
|
+
if (dbVersion > instance.dbVersion) {
|
|
1661
|
+
instance.dbVersion = dbVersion;
|
|
1662
|
+
}
|
|
1663
|
+
return instance;
|
|
1664
|
+
}
|
|
1665
|
+
async initialize() {
|
|
1666
|
+
if (this.db) {
|
|
1667
|
+
return this.db;
|
|
1668
|
+
}
|
|
1669
|
+
if (!this.initPromise) {
|
|
1670
|
+
this.initPromise = this.openDatabase();
|
|
1671
|
+
}
|
|
1672
|
+
return this.initPromise;
|
|
1673
|
+
}
|
|
1674
|
+
async ensureStoreExists(storeName) {
|
|
1675
|
+
await this.initialize();
|
|
1676
|
+
if (this.db.objectStoreNames.contains(storeName)) {
|
|
1677
|
+
this.storeNames.add(storeName);
|
|
1678
|
+
return this.db;
|
|
1679
|
+
}
|
|
1680
|
+
this.logger?.debug(`Store "${storeName}" not found, upgrading database`, {
|
|
1681
|
+
dbName: this.dbName,
|
|
1682
|
+
currentStores: Array.from(this.db.objectStoreNames)
|
|
1683
|
+
});
|
|
1684
|
+
this.db.close();
|
|
1685
|
+
this.db = null;
|
|
1686
|
+
this.dbVersion++;
|
|
1687
|
+
this.initPromise = this.openDatabase([storeName]);
|
|
1688
|
+
const newDb = await this.initPromise;
|
|
1689
|
+
this.storeNames.add(storeName);
|
|
1690
|
+
return newDb;
|
|
1691
|
+
}
|
|
1692
|
+
async openDatabase(newStores = []) {
|
|
1693
|
+
return new Promise((resolve, reject) => {
|
|
1694
|
+
this.logger?.debug(`Opening database "${this.dbName}" with version ${this.dbVersion}`);
|
|
1695
|
+
const request = indexedDB.open(this.dbName, this.dbVersion);
|
|
1696
|
+
request.onerror = () => {
|
|
1697
|
+
this.logger?.error(`Failed to open database "${this.dbName}"`, { error: request.error });
|
|
1698
|
+
reject(request.error);
|
|
1699
|
+
};
|
|
1700
|
+
request.onsuccess = () => {
|
|
1701
|
+
this.db = request.result;
|
|
1702
|
+
for (let i = 0; i < this.db.objectStoreNames.length; i++) {
|
|
1703
|
+
this.storeNames.add(this.db.objectStoreNames[i]);
|
|
1704
|
+
}
|
|
1705
|
+
this.logger?.debug(`Database "${this.dbName}" opened successfully`, {
|
|
1706
|
+
version: this.db.version,
|
|
1707
|
+
stores: Array.from(this.db.objectStoreNames)
|
|
1708
|
+
});
|
|
1709
|
+
resolve(this.db);
|
|
1710
|
+
};
|
|
1711
|
+
request.onupgradeneeded = (event) => {
|
|
1712
|
+
const db = event.target.result;
|
|
1713
|
+
this.logger?.debug(`Upgrading database "${this.dbName}" to version ${this.dbVersion}`);
|
|
1714
|
+
for (const storeName of newStores) {
|
|
1715
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
1716
|
+
this.logger?.debug(`Creating store "${storeName}"`);
|
|
1717
|
+
db.createObjectStore(storeName);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
closeDatabase() {
|
|
1724
|
+
if (this.db) {
|
|
1725
|
+
this.db.close();
|
|
1726
|
+
this.db = null;
|
|
1727
|
+
this.initPromise = null;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
async deleteDatabase() {
|
|
1731
|
+
this.closeDatabase();
|
|
1732
|
+
return new Promise((resolve, reject) => {
|
|
1733
|
+
const request = indexedDB.deleteDatabase(this.dbName);
|
|
1734
|
+
request.onsuccess = () => {
|
|
1735
|
+
this.logger?.debug(`Database "${this.dbName}" deleted successfully`);
|
|
1736
|
+
_IndexedDBManager.instances.delete(this.dbName);
|
|
1737
|
+
this.storeNames.clear();
|
|
1738
|
+
resolve();
|
|
1739
|
+
};
|
|
1740
|
+
request.onerror = () => {
|
|
1741
|
+
this.logger?.error(`Failed to delete database "${this.dbName}"`, { error: request.error });
|
|
1742
|
+
reject(request.error);
|
|
1743
|
+
};
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
async ensureStoresExist(storeNames) {
|
|
1747
|
+
await this.initialize();
|
|
1748
|
+
const missingStores = storeNames.filter((name) => !this.db.objectStoreNames.contains(name));
|
|
1749
|
+
if (missingStores.length === 0) {
|
|
1750
|
+
return this.db;
|
|
1751
|
+
}
|
|
1752
|
+
this.logger?.debug(`\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u044E\u0449\u0438\u0445 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449: ${missingStores.join(", ")}`, {
|
|
1753
|
+
dbName: this.dbName,
|
|
1754
|
+
currentStores: Array.from(this.db.objectStoreNames)
|
|
1755
|
+
});
|
|
1756
|
+
this.db.close();
|
|
1757
|
+
this.db = null;
|
|
1758
|
+
this.dbVersion++;
|
|
1759
|
+
this.initPromise = this.openDatabase(missingStores);
|
|
1760
|
+
return this.initPromise;
|
|
1761
|
+
}
|
|
1762
|
+
// Метод для получения текущей версии
|
|
1763
|
+
getCurrentVersion() {
|
|
1764
|
+
return this.dbVersion;
|
|
1765
|
+
}
|
|
1766
|
+
};
|
|
1767
|
+
var DBVersionManager = class {
|
|
1768
|
+
constructor(dbName, initialVersion = 1, logger) {
|
|
1769
|
+
this.dbName = dbName;
|
|
1770
|
+
this.logger = logger;
|
|
1771
|
+
this.version = initialVersion;
|
|
1772
|
+
}
|
|
1773
|
+
db = null;
|
|
1774
|
+
version;
|
|
1775
|
+
/**
|
|
1776
|
+
* Получает текущую версию базы данных
|
|
1777
|
+
*/
|
|
1778
|
+
getCurrentVersion() {
|
|
1779
|
+
return this.version;
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Открывает базу данных с заданной версией
|
|
1783
|
+
*/
|
|
1784
|
+
openDatabase(version, newStores = []) {
|
|
1785
|
+
return new Promise((resolve, reject) => {
|
|
1786
|
+
this.logger?.debug(`\u041E\u0442\u043A\u0440\u044B\u0442\u0438\u0435 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445 "${this.dbName}" \u0441 \u0432\u0435\u0440\u0441\u0438\u0435\u0439 ${version}`);
|
|
1787
|
+
const request = indexedDB.open(this.dbName, version);
|
|
1788
|
+
request.onerror = () => {
|
|
1789
|
+
this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0438 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445 "${this.dbName}"`, { error: request.error });
|
|
1790
|
+
reject(request.error);
|
|
1791
|
+
};
|
|
1792
|
+
request.onsuccess = () => {
|
|
1793
|
+
this.db = request.result;
|
|
1794
|
+
this.version = this.db.version;
|
|
1795
|
+
this.logger?.debug(`\u0411\u0430\u0437\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 "${this.dbName}" \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043E\u0442\u043A\u0440\u044B\u0442\u0430`, {
|
|
1796
|
+
version: this.db.version,
|
|
1797
|
+
stores: Array.from(this.db.objectStoreNames)
|
|
1798
|
+
});
|
|
1799
|
+
resolve(this.db);
|
|
1800
|
+
};
|
|
1801
|
+
request.onupgradeneeded = (event) => {
|
|
1802
|
+
const db = event.target.result;
|
|
1803
|
+
this.logger?.debug(`\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445 "${this.dbName}" \u0434\u043E \u0432\u0435\u0440\u0441\u0438\u0438 ${version}`);
|
|
1804
|
+
for (const storeName of newStores) {
|
|
1805
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
1806
|
+
this.logger?.debug(`\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430 "${storeName}"`);
|
|
1807
|
+
db.createObjectStore(storeName);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
};
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
/**
|
|
1814
|
+
* Убеждается, что все указанные хранилища существуют в базе данных
|
|
1815
|
+
*/
|
|
1816
|
+
async ensureStoresExist(storeNames) {
|
|
1817
|
+
if (!this.db) {
|
|
1818
|
+
this.db = await this.openDatabase(this.version);
|
|
1819
|
+
}
|
|
1820
|
+
const missingStores = storeNames.filter((name) => !this.db.objectStoreNames.contains(name));
|
|
1821
|
+
if (missingStores.length === 0) {
|
|
1822
|
+
return this.db;
|
|
1823
|
+
}
|
|
1824
|
+
this.logger?.debug(`\u041D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0441\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0435 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430: ${missingStores.join(", ")}`, {
|
|
1825
|
+
dbName: this.dbName,
|
|
1826
|
+
currentVersion: this.version
|
|
1827
|
+
});
|
|
1828
|
+
this.db.close();
|
|
1829
|
+
this.db = null;
|
|
1830
|
+
this.version++;
|
|
1831
|
+
this.db = await this.openDatabase(this.version, missingStores);
|
|
1832
|
+
return this.db;
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* Закрывает соединение с базой данных
|
|
1836
|
+
*/
|
|
1837
|
+
close() {
|
|
1838
|
+
if (this.db) {
|
|
1839
|
+
this.db.close();
|
|
1840
|
+
this.db = null;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
};
|
|
1844
|
+
var IndexedDBStorage = class _IndexedDBStorage extends BaseStorage {
|
|
1845
|
+
DB_NAME;
|
|
1846
|
+
STORE_NAME;
|
|
1847
|
+
DB_VERSION;
|
|
1848
|
+
dbManager;
|
|
1849
|
+
constructor(config, pluginExecutor, eventEmitter, logger) {
|
|
1850
|
+
super(config, pluginExecutor, eventEmitter, logger);
|
|
1851
|
+
const options = config.options;
|
|
1852
|
+
this.DB_NAME = options.dbName || "app_storage";
|
|
1853
|
+
this.STORE_NAME = config.name;
|
|
1854
|
+
this.DB_VERSION = options.dbVersion || 1;
|
|
1855
|
+
this.dbManager = IndexedDBManager.getInstance(this.DB_NAME, this.DB_VERSION, logger);
|
|
1856
|
+
}
|
|
1857
|
+
async initialize() {
|
|
1858
|
+
try {
|
|
1859
|
+
this.logger?.debug(`Initializing IndexedDB storage "${this.STORE_NAME}"`);
|
|
1860
|
+
await this.dbManager.ensureStoreExists(this.STORE_NAME);
|
|
1861
|
+
try {
|
|
1862
|
+
const db = await this.dbManager.initialize();
|
|
1863
|
+
if (!db.objectStoreNames.contains(this.STORE_NAME)) {
|
|
1864
|
+
throw new Error(`Store "${this.STORE_NAME}" not found after initialization`);
|
|
1865
|
+
}
|
|
1866
|
+
} catch (error) {
|
|
1867
|
+
this.logger?.error(`Error verifying store "${this.STORE_NAME}" exists:`, { error });
|
|
1868
|
+
throw error;
|
|
1869
|
+
}
|
|
1870
|
+
this.initializeMiddlewares();
|
|
1871
|
+
try {
|
|
1872
|
+
await this.initializeWithMiddlewares();
|
|
1873
|
+
this.logger?.debug(`IndexedDB storage "${this.STORE_NAME}" initialized successfully`);
|
|
1874
|
+
} catch (error) {
|
|
1875
|
+
this.logger?.error(`Failed to initialize middleware for store "${this.STORE_NAME}"`, { error });
|
|
1876
|
+
}
|
|
1877
|
+
return this;
|
|
1878
|
+
} catch (error) {
|
|
1879
|
+
this.logger?.error(`\u041E\u0448\u0438\u0431\u043A\u0430 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u0438 IndexedDB "${this.name}"`, { error });
|
|
1880
|
+
throw error;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
static async getCurrentDBVersion(dbName) {
|
|
1884
|
+
return new Promise((resolve) => {
|
|
1885
|
+
try {
|
|
1886
|
+
const request = indexedDB.open(dbName);
|
|
1887
|
+
request.onsuccess = () => {
|
|
1888
|
+
const version = request.result.version;
|
|
1889
|
+
request.result.close();
|
|
1890
|
+
resolve(version);
|
|
1891
|
+
};
|
|
1892
|
+
request.onerror = () => {
|
|
1893
|
+
console.warn(`\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u0411\u0414 ${dbName}`, request.error);
|
|
1894
|
+
resolve(0);
|
|
1895
|
+
};
|
|
1896
|
+
} catch (error) {
|
|
1897
|
+
console.warn(`\u0418\u0441\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435 \u043F\u0440\u0438 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u0411\u0414 ${dbName}`, error);
|
|
1898
|
+
resolve(0);
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
static async createStorages(dbName, configs, logger) {
|
|
1903
|
+
const currentVersion = await this.getCurrentDBVersion(dbName);
|
|
1904
|
+
const initialVersion = currentVersion || 1;
|
|
1905
|
+
const dbManager = new DBVersionManager(dbName, initialVersion, logger);
|
|
1906
|
+
const storeNames = Object.values(configs).map((config) => config.name);
|
|
1907
|
+
await dbManager.ensureStoresExist(storeNames);
|
|
1908
|
+
const result = {};
|
|
1909
|
+
for (const [key, config] of Object.entries(configs)) {
|
|
1910
|
+
const storage = new _IndexedDBStorage(
|
|
1911
|
+
{
|
|
1912
|
+
...config,
|
|
1913
|
+
options: {
|
|
1914
|
+
dbName,
|
|
1915
|
+
dbVersion: dbManager.getCurrentVersion()
|
|
1916
|
+
}
|
|
1917
|
+
},
|
|
1918
|
+
config.pluginExecutor,
|
|
1919
|
+
config.eventEmitter,
|
|
1920
|
+
logger
|
|
1921
|
+
);
|
|
1922
|
+
result[key] = await storage.initialize();
|
|
1923
|
+
}
|
|
1924
|
+
return result;
|
|
1925
|
+
}
|
|
1926
|
+
async getTransaction(mode = "readonly") {
|
|
1927
|
+
try {
|
|
1928
|
+
const db = await this.dbManager.ensureStoreExists(this.STORE_NAME);
|
|
1929
|
+
if (!db.objectStoreNames.contains(this.STORE_NAME)) {
|
|
1930
|
+
this.logger?.warn(`Object store "${this.STORE_NAME}" not found, attempting to repair`);
|
|
1931
|
+
db.close();
|
|
1932
|
+
this.dbManager.closeDatabase();
|
|
1933
|
+
const newDb = await this.dbManager.ensureStoreExists(this.STORE_NAME);
|
|
1934
|
+
if (!newDb.objectStoreNames.contains(this.STORE_NAME)) {
|
|
1935
|
+
throw new Error(`Object store "${this.STORE_NAME}" still doesn't exist after repair attempt`);
|
|
1936
|
+
}
|
|
1937
|
+
return newDb.transaction(this.STORE_NAME, mode);
|
|
1938
|
+
}
|
|
1939
|
+
return db.transaction(this.STORE_NAME, mode);
|
|
1940
|
+
} catch (error) {
|
|
1941
|
+
this.logger?.error(`Failed to create transaction for store "${this.STORE_NAME}"`, { error });
|
|
1942
|
+
throw error;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
async getObjectStore(mode = "readonly") {
|
|
1946
|
+
const transaction = await this.getTransaction(mode);
|
|
1947
|
+
return transaction.objectStore(this.STORE_NAME);
|
|
1948
|
+
}
|
|
1949
|
+
async doGet(key) {
|
|
1950
|
+
const store = await this.getObjectStore();
|
|
1951
|
+
if (key === "") {
|
|
1952
|
+
return new Promise((resolve, reject) => {
|
|
1953
|
+
const request = store.getAll();
|
|
1954
|
+
request.onerror = () => reject(request.error);
|
|
1955
|
+
request.onsuccess = () => {
|
|
1956
|
+
const allValues = request.result;
|
|
1957
|
+
const allKeys = store.getAllKeys();
|
|
1958
|
+
allKeys.onsuccess = () => {
|
|
1959
|
+
const state = allKeys.result.reduce(
|
|
1960
|
+
(acc, k, index) => {
|
|
1961
|
+
if (k !== "root") {
|
|
1962
|
+
acc[k] = allValues[index];
|
|
1963
|
+
}
|
|
1964
|
+
return acc;
|
|
1965
|
+
},
|
|
1966
|
+
{}
|
|
1967
|
+
);
|
|
1968
|
+
resolve(state);
|
|
1969
|
+
};
|
|
1970
|
+
allKeys.onerror = () => reject(allKeys.error);
|
|
1971
|
+
};
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
1975
|
+
return new Promise((resolve, reject) => {
|
|
1976
|
+
const request = store.get(key.valueOf());
|
|
1977
|
+
request.onerror = () => reject(request.error);
|
|
1978
|
+
request.onsuccess = () => resolve(request.result);
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
const parts = parsePath(key);
|
|
1982
|
+
if (parts.length > 1) {
|
|
1983
|
+
const rootKey = parts[0];
|
|
1984
|
+
return new Promise((resolve, reject) => {
|
|
1985
|
+
const request = store.get(rootKey);
|
|
1986
|
+
request.onerror = () => reject(request.error);
|
|
1987
|
+
request.onsuccess = () => {
|
|
1988
|
+
const rootValue = request.result;
|
|
1989
|
+
if (!rootValue) {
|
|
1990
|
+
resolve(void 0);
|
|
1991
|
+
return;
|
|
1992
|
+
}
|
|
1993
|
+
const value = getValueByPath(rootValue, parts.slice(1).join("."));
|
|
1994
|
+
resolve(value);
|
|
1995
|
+
};
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
return new Promise((resolve, reject) => {
|
|
1999
|
+
const request = store.get(parts[0]);
|
|
2000
|
+
request.onerror = () => reject(request.error);
|
|
2001
|
+
request.onsuccess = () => resolve(request.result);
|
|
2002
|
+
});
|
|
2003
|
+
}
|
|
2004
|
+
async doSet(key, value) {
|
|
2005
|
+
if (key === "") {
|
|
2006
|
+
const store2 = await this.getObjectStore("readwrite");
|
|
2007
|
+
return new Promise((resolve, reject) => {
|
|
2008
|
+
const tx = store2.transaction;
|
|
2009
|
+
tx.oncomplete = () => {
|
|
2010
|
+
resolve();
|
|
2011
|
+
};
|
|
2012
|
+
tx.onerror = () => {
|
|
2013
|
+
reject(tx.error);
|
|
2014
|
+
};
|
|
2015
|
+
const clearRequest = store2.clear();
|
|
2016
|
+
clearRequest.onsuccess = () => {
|
|
2017
|
+
const entries = Object.entries(value);
|
|
2018
|
+
for (const [entryKey, entryValue] of entries) {
|
|
2019
|
+
store2.put(entryValue, entryKey);
|
|
2020
|
+
}
|
|
2021
|
+
};
|
|
2022
|
+
clearRequest.onerror = () => {
|
|
2023
|
+
reject(clearRequest.error);
|
|
2024
|
+
};
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
2027
|
+
const store = await this.getObjectStore("readwrite");
|
|
2028
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2029
|
+
await this.putValueInStore(store, key.valueOf(), value);
|
|
2030
|
+
return;
|
|
2031
|
+
}
|
|
2032
|
+
const parts = parsePath(key);
|
|
2033
|
+
if (parts.length > 1) {
|
|
2034
|
+
const rootKey = parts[0];
|
|
2035
|
+
return new Promise((resolve, reject) => {
|
|
2036
|
+
const request = store.get(rootKey);
|
|
2037
|
+
request.onerror = () => reject(request.error);
|
|
2038
|
+
request.onsuccess = () => {
|
|
2039
|
+
const rootValue = request.result || {};
|
|
2040
|
+
const updatedValue = setValueByPath(rootValue, parts.slice(1).join("."), value);
|
|
2041
|
+
const putRequest = store.put(updatedValue, rootKey);
|
|
2042
|
+
putRequest.onerror = () => reject(putRequest.error);
|
|
2043
|
+
putRequest.onsuccess = () => resolve();
|
|
2044
|
+
};
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
await this.putValueInStore(store, parts[0], value);
|
|
2048
|
+
}
|
|
2049
|
+
async putValueInStore(store, key, value) {
|
|
2050
|
+
return new Promise((resolve, reject) => {
|
|
2051
|
+
const request = store.put(value, key.valueOf());
|
|
2052
|
+
request.onerror = () => reject(request.error);
|
|
2053
|
+
request.onsuccess = () => resolve();
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
async doUpdate(updates) {
|
|
2057
|
+
const updatesByRoot = /* @__PURE__ */ new Map();
|
|
2058
|
+
const rawUpdates = [];
|
|
2059
|
+
for (const { key, value } of updates) {
|
|
2060
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2061
|
+
rawUpdates.push({ key: key.valueOf(), value });
|
|
2062
|
+
continue;
|
|
2063
|
+
}
|
|
2064
|
+
const parts = parsePath(key);
|
|
2065
|
+
const rootKey = parts[0];
|
|
2066
|
+
const path = parts.slice(1);
|
|
2067
|
+
if (!updatesByRoot.has(rootKey)) {
|
|
2068
|
+
updatesByRoot.set(rootKey, []);
|
|
2069
|
+
}
|
|
2070
|
+
updatesByRoot.get(rootKey).push({ path, value });
|
|
2071
|
+
}
|
|
2072
|
+
try {
|
|
2073
|
+
for (const { key, value } of rawUpdates) {
|
|
2074
|
+
const store = await this.getObjectStore("readwrite");
|
|
2075
|
+
await this.putValueInStore(store, key, value);
|
|
2076
|
+
}
|
|
2077
|
+
for (const [rootKey, pathUpdates] of updatesByRoot) {
|
|
2078
|
+
const rootValue = await this.doGet(rootKey) || {};
|
|
2079
|
+
let updatedValue = { ...rootValue };
|
|
2080
|
+
for (const { path, value } of pathUpdates) {
|
|
2081
|
+
if (path.length === 0) {
|
|
2082
|
+
updatedValue = value;
|
|
2083
|
+
} else {
|
|
2084
|
+
updatedValue = setValueByPath(updatedValue, path.join("."), value);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
const store = await this.getObjectStore("readwrite");
|
|
2088
|
+
await this.putValueInStore(store, rootKey, updatedValue);
|
|
2089
|
+
}
|
|
2090
|
+
} catch (error) {
|
|
2091
|
+
this.logger?.error("Error during update:", { error });
|
|
2092
|
+
throw error;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
async doDelete(key) {
|
|
2096
|
+
const store = await this.getObjectStore("readwrite");
|
|
2097
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2098
|
+
return new Promise((resolve, reject) => {
|
|
2099
|
+
const request = store.delete(key.valueOf());
|
|
2100
|
+
request.onerror = () => reject(request.error);
|
|
2101
|
+
request.onsuccess = () => resolve(true);
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
const parts = parsePath(key);
|
|
2105
|
+
if (parts.length === 1) {
|
|
2106
|
+
return new Promise((resolve, reject) => {
|
|
2107
|
+
const request = store.delete(parts[0]);
|
|
2108
|
+
request.onerror = () => reject(request.error);
|
|
2109
|
+
request.onsuccess = () => resolve(true);
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
const rootKey = parts[0];
|
|
2113
|
+
return new Promise((resolve, reject) => {
|
|
2114
|
+
const getRequest = store.get(rootKey);
|
|
2115
|
+
getRequest.onerror = () => reject(getRequest.error);
|
|
2116
|
+
getRequest.onsuccess = () => {
|
|
2117
|
+
const rootValue = getRequest.result;
|
|
2118
|
+
if (!rootValue) {
|
|
2119
|
+
resolve(false);
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
const parent = getValueByPath(rootValue, parts.slice(0, -1).join("."));
|
|
2123
|
+
const lastKey = parts[parts.length - 1];
|
|
2124
|
+
if (!parent || !(lastKey in parent)) {
|
|
2125
|
+
resolve(false);
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
if (Array.isArray(parent)) {
|
|
2129
|
+
const index = parseInt(lastKey, 10);
|
|
2130
|
+
if (!isNaN(index)) {
|
|
2131
|
+
parent.splice(index, 1);
|
|
2132
|
+
} else {
|
|
2133
|
+
delete parent[lastKey];
|
|
2134
|
+
}
|
|
2135
|
+
} else {
|
|
2136
|
+
delete parent[lastKey];
|
|
2137
|
+
}
|
|
2138
|
+
const putRequest = store.put(rootValue, rootKey);
|
|
2139
|
+
putRequest.onerror = () => reject(putRequest.error);
|
|
2140
|
+
putRequest.onsuccess = () => resolve(true);
|
|
2141
|
+
};
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2144
|
+
async doClear() {
|
|
2145
|
+
const store = await this.getObjectStore("readwrite");
|
|
2146
|
+
return new Promise((resolve, reject) => {
|
|
2147
|
+
const request = store.clear();
|
|
2148
|
+
request.onsuccess = () => resolve();
|
|
2149
|
+
request.onerror = () => reject(request.error);
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
async doKeys() {
|
|
2153
|
+
const store = await this.getObjectStore();
|
|
2154
|
+
const request = store.getAllKeys();
|
|
2155
|
+
return new Promise((resolve, reject) => {
|
|
2156
|
+
request.onsuccess = () => {
|
|
2157
|
+
resolve(request.result);
|
|
2158
|
+
};
|
|
2159
|
+
request.onerror = () => reject(request.error);
|
|
2160
|
+
});
|
|
2161
|
+
}
|
|
2162
|
+
async doHas(key) {
|
|
2163
|
+
const value = await this.doGet(key);
|
|
2164
|
+
return value !== void 0;
|
|
2165
|
+
}
|
|
2166
|
+
async doDestroy() {
|
|
2167
|
+
try {
|
|
2168
|
+
await this.doClear();
|
|
2169
|
+
} catch (error) {
|
|
2170
|
+
this.logger?.error(`Error destroying store "${this.STORE_NAME}"`, { error });
|
|
2171
|
+
throw error;
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
|
|
2176
|
+
// src/core/storage/adapters/local-storage.service.ts
|
|
2177
|
+
var LocalStorage = class extends BaseStorage {
|
|
2178
|
+
constructor(config, pluginExecutor, eventEmitter, logger) {
|
|
2179
|
+
super(config, pluginExecutor, eventEmitter, logger);
|
|
2180
|
+
}
|
|
2181
|
+
async initialize() {
|
|
2182
|
+
try {
|
|
2183
|
+
await this.initializeWithMiddlewares();
|
|
2184
|
+
return this;
|
|
2185
|
+
} catch (error) {
|
|
2186
|
+
this.logger?.error("Error initializing LocalStorage", { error });
|
|
2187
|
+
throw error;
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
async doGet(key) {
|
|
2191
|
+
const storageData = localStorage.getItem(this.name);
|
|
2192
|
+
if (!storageData) return void 0;
|
|
2193
|
+
const state = JSON.parse(storageData);
|
|
2194
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2195
|
+
return state[key.valueOf()];
|
|
2196
|
+
}
|
|
2197
|
+
return getValueByPath(state, key);
|
|
2198
|
+
}
|
|
2199
|
+
async doSet(key, value) {
|
|
2200
|
+
const storageData = localStorage.getItem(this.name);
|
|
2201
|
+
const state = storageData ? JSON.parse(storageData) : {};
|
|
2202
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2203
|
+
state[key.valueOf()] = value;
|
|
2204
|
+
localStorage.setItem(this.name, JSON.stringify(state));
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2207
|
+
const newState = setValueByPath({ ...state }, key, value);
|
|
2208
|
+
localStorage.setItem(this.name, JSON.stringify(newState));
|
|
2209
|
+
}
|
|
2210
|
+
async doDelete(key) {
|
|
2211
|
+
const storageData = localStorage.getItem(this.name);
|
|
2212
|
+
if (!storageData) return false;
|
|
2213
|
+
const state = JSON.parse(storageData);
|
|
2214
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2215
|
+
const rawKey = key.valueOf();
|
|
2216
|
+
if (!(rawKey in state)) return false;
|
|
2217
|
+
delete state[rawKey];
|
|
2218
|
+
localStorage.setItem(this.name, JSON.stringify(state));
|
|
2219
|
+
return true;
|
|
2220
|
+
}
|
|
2221
|
+
const pathParts = parsePath(key);
|
|
2222
|
+
const parentPath = pathParts.slice(0, -1).join(".");
|
|
2223
|
+
const lastKey = pathParts[pathParts.length - 1];
|
|
2224
|
+
const parent = parentPath ? getValueByPath(state, parentPath) : state;
|
|
2225
|
+
if (!parent || !(lastKey in parent)) return false;
|
|
2226
|
+
delete parent[lastKey];
|
|
2227
|
+
localStorage.setItem(this.name, JSON.stringify(state));
|
|
2228
|
+
return true;
|
|
2229
|
+
}
|
|
2230
|
+
async doUpdate(updates) {
|
|
2231
|
+
const storageData = localStorage.getItem(this.name);
|
|
2232
|
+
const state = storageData ? JSON.parse(storageData) : {};
|
|
2233
|
+
for (const { key, value } of updates) {
|
|
2234
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2235
|
+
state[key.valueOf()] = value;
|
|
2236
|
+
} else {
|
|
2237
|
+
setValueByPath(state, key, value);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
localStorage.setItem(this.name, JSON.stringify(state));
|
|
2241
|
+
}
|
|
2242
|
+
async doClear() {
|
|
2243
|
+
localStorage.removeItem(this.name);
|
|
2244
|
+
}
|
|
2245
|
+
async doKeys() {
|
|
2246
|
+
const storageData = localStorage.getItem(this.name);
|
|
2247
|
+
if (!storageData) return [];
|
|
2248
|
+
const state = JSON.parse(storageData);
|
|
2249
|
+
return this.getAllKeys(state);
|
|
2250
|
+
}
|
|
2251
|
+
async doHas(key) {
|
|
2252
|
+
const value = await this.doGet(key);
|
|
2253
|
+
return value !== void 0;
|
|
2254
|
+
}
|
|
2255
|
+
getAllKeys(obj) {
|
|
2256
|
+
return Object.keys(obj);
|
|
2257
|
+
}
|
|
2258
|
+
async doDestroy() {
|
|
2259
|
+
await this.doClear();
|
|
2260
|
+
}
|
|
2261
|
+
};
|
|
2262
|
+
|
|
2263
|
+
// src/core/storage/adapters/memory-storage.service.ts
|
|
2264
|
+
var MemoryStorage = class extends BaseStorage {
|
|
2265
|
+
storage = /* @__PURE__ */ new Map();
|
|
2266
|
+
constructor(config, pluginExecutor, eventEmitter, logger) {
|
|
2267
|
+
super(config, pluginExecutor, eventEmitter, logger);
|
|
2268
|
+
}
|
|
2269
|
+
async initialize() {
|
|
2270
|
+
try {
|
|
2271
|
+
this.initializeMiddlewares();
|
|
2272
|
+
await this.initializeWithMiddlewares();
|
|
2273
|
+
return this;
|
|
2274
|
+
} catch (error) {
|
|
2275
|
+
this.logger?.error("Error initializing MemoryStorage", { error });
|
|
2276
|
+
throw error;
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
async doGet(key) {
|
|
2280
|
+
const state = this.storage.get(this.name);
|
|
2281
|
+
if (!state) return void 0;
|
|
2282
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2283
|
+
return state[key.valueOf()];
|
|
2284
|
+
}
|
|
2285
|
+
return getValueByPath(state, key);
|
|
2286
|
+
}
|
|
2287
|
+
async doSet(key, value) {
|
|
2288
|
+
const state = this.storage.get(this.name) || {};
|
|
2289
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2290
|
+
state[key.valueOf()] = value;
|
|
2291
|
+
this.storage.set(this.name, state);
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
const newState = setValueByPath({ ...state }, key, value);
|
|
2295
|
+
this.storage.set(this.name, newState);
|
|
2296
|
+
}
|
|
2297
|
+
async doDelete(key) {
|
|
2298
|
+
const state = this.storage.get(this.name);
|
|
2299
|
+
if (!state) return false;
|
|
2300
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2301
|
+
const rawKey = key.valueOf();
|
|
2302
|
+
if (!(rawKey in state)) return false;
|
|
2303
|
+
delete state[rawKey];
|
|
2304
|
+
this.storage.set(this.name, state);
|
|
2305
|
+
return true;
|
|
2306
|
+
}
|
|
2307
|
+
const pathParts = parsePath(key);
|
|
2308
|
+
const parentPath = pathParts.slice(0, -1).join(".");
|
|
2309
|
+
const lastKey = pathParts[pathParts.length - 1];
|
|
2310
|
+
const parent = parentPath ? getValueByPath(state, parentPath) : state;
|
|
2311
|
+
if (!parent || !(lastKey in parent)) return false;
|
|
2312
|
+
delete parent[lastKey];
|
|
2313
|
+
this.storage.set(this.name, state);
|
|
2314
|
+
return true;
|
|
2315
|
+
}
|
|
2316
|
+
async doUpdate(updates) {
|
|
2317
|
+
const currentState = this.storage.get(this.name) || {};
|
|
2318
|
+
const newState = { ...currentState };
|
|
2319
|
+
for (const { key, value } of updates) {
|
|
2320
|
+
if (key instanceof StorageKey && key.isUnparseable()) {
|
|
2321
|
+
newState[key.valueOf()] = value;
|
|
2322
|
+
} else {
|
|
2323
|
+
setValueByPath(newState, key, value);
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
this.storage.set(this.name, newState);
|
|
2327
|
+
}
|
|
2328
|
+
async doClear() {
|
|
2329
|
+
this.storage.delete(this.name);
|
|
2330
|
+
}
|
|
2331
|
+
async doKeys() {
|
|
2332
|
+
const state = this.storage.get(this.name);
|
|
2333
|
+
if (!state) return [];
|
|
2334
|
+
return this.getAllKeys(state);
|
|
2335
|
+
}
|
|
2336
|
+
async doHas(key) {
|
|
2337
|
+
const value = await this.doGet(key);
|
|
2338
|
+
return value !== void 0;
|
|
2339
|
+
}
|
|
2340
|
+
async doDestroy() {
|
|
2341
|
+
this.storage.delete(this.name);
|
|
2342
|
+
}
|
|
2343
|
+
getAllKeys(obj) {
|
|
2344
|
+
return Object.keys(obj);
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
export {
|
|
2348
|
+
IndexedDBStorage,
|
|
2349
|
+
LocalStorage,
|
|
2350
|
+
MemoryStorage,
|
|
2351
|
+
SelectorModule,
|
|
2352
|
+
StorageEvents,
|
|
2353
|
+
StoragePluginModule,
|
|
2354
|
+
broadcastMiddleware
|
|
2355
|
+
};
|
|
2356
|
+
//# sourceMappingURL=core.js.map
|