complexqa_frontend_core 1.18.5 → 1.18.7
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/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiAbstractClass } from "./api_abstract_class.js";
|
|
2
|
+
import { resolveCachedSearch } from "./cache/api_search_cache.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
*
|
|
@@ -9,6 +10,9 @@ export class ApiAbstractReferenceClass extends ApiAbstractClass
|
|
|
9
10
|
|
|
10
11
|
api_prefix = 'api';
|
|
11
12
|
|
|
13
|
+
/** @type {boolean} */
|
|
14
|
+
search_cache_enabled = true;
|
|
15
|
+
|
|
12
16
|
/**
|
|
13
17
|
*
|
|
14
18
|
* @version v.1.0 (03/06/2026)
|
|
@@ -16,7 +20,7 @@ export class ApiAbstractReferenceClass extends ApiAbstractClass
|
|
|
16
20
|
*/
|
|
17
21
|
constructor(options)
|
|
18
22
|
{
|
|
19
|
-
super();
|
|
23
|
+
super(options);
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
|
|
@@ -31,6 +35,22 @@ export class ApiAbstractReferenceClass extends ApiAbstractClass
|
|
|
31
35
|
* @param {?object|undefined} payload.response
|
|
32
36
|
*/
|
|
33
37
|
async search(payload = {})
|
|
38
|
+
{
|
|
39
|
+
return resolveCachedSearch(
|
|
40
|
+
this,
|
|
41
|
+
'search',
|
|
42
|
+
payload,
|
|
43
|
+
(p) => this.executeSearch(p),
|
|
44
|
+
{ useCache: this.search_cache_enabled },
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {object|typeFOR} payload
|
|
51
|
+
* @returns {Promise<{response: *, payload: *}>}
|
|
52
|
+
*/
|
|
53
|
+
async executeSearch(payload = {})
|
|
34
54
|
{
|
|
35
55
|
if (!this.module_prefix)
|
|
36
56
|
{
|
|
@@ -50,7 +70,7 @@ export class ApiAbstractReferenceClass extends ApiAbstractClass
|
|
|
50
70
|
|
|
51
71
|
return {
|
|
52
72
|
response: response,
|
|
53
|
-
payload : payload
|
|
73
|
+
payload : payload,
|
|
54
74
|
};
|
|
55
75
|
}
|
|
56
76
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { clone_object } from "../../utils/utils.js";
|
|
2
|
+
import { MemoryCacheStore } from "./memory_cache_store.js";
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_SEARCH_CACHE_TTL_MS = 4 * 60 * 60 * 1000;
|
|
5
|
+
|
|
6
|
+
let cacheStore = new MemoryCacheStore();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* DI: подмена хранилища кеша (например, persistent/store с TTL).
|
|
10
|
+
*
|
|
11
|
+
* @param {MemoryCacheStore} store
|
|
12
|
+
*/
|
|
13
|
+
export function setApiSearchCacheStore(store)
|
|
14
|
+
{
|
|
15
|
+
cacheStore = store;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @returns {MemoryCacheStore}
|
|
21
|
+
*/
|
|
22
|
+
export function getApiSearchCacheStore()
|
|
23
|
+
{
|
|
24
|
+
return cacheStore;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {object} apiInstance
|
|
30
|
+
* @param {string} methodName
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
export function buildSearchCacheKey(apiInstance, methodName = 'search')
|
|
34
|
+
{
|
|
35
|
+
return `${ apiInstance?.constructor?.name || 'Api' }.${ methodName }`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Дефолтный запрос: без фильтров и без options.
|
|
41
|
+
*
|
|
42
|
+
* @param {object|false|undefined} payload
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
export function isDefaultSearchPayload(payload)
|
|
46
|
+
{
|
|
47
|
+
const filters = payload?.filters ?? payload?.filter ?? [];
|
|
48
|
+
|
|
49
|
+
if (!Array.isArray(filters) || filters.length > 0)
|
|
50
|
+
{
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const options = payload?.options;
|
|
55
|
+
|
|
56
|
+
if (options === false || options == null)
|
|
57
|
+
{
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (typeof options === 'object' && Object.keys(options).length === 0)
|
|
62
|
+
{
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {{ response: Promise<*>|*, payload: * }} result
|
|
72
|
+
* @returns {Promise<{ response: *, payload: * }>}
|
|
73
|
+
*/
|
|
74
|
+
async function materializeSearchResult(result)
|
|
75
|
+
{
|
|
76
|
+
return {
|
|
77
|
+
response: await result.response,
|
|
78
|
+
payload : result.payload,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {{ response: *, payload: * }} result
|
|
85
|
+
* @returns {{ response: Promise<*>, payload: * }}
|
|
86
|
+
*/
|
|
87
|
+
function wrapSearchResult(result)
|
|
88
|
+
{
|
|
89
|
+
return {
|
|
90
|
+
response: Promise.resolve(result.response),
|
|
91
|
+
payload : result.payload,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {object} apiInstance
|
|
98
|
+
* @param {string} methodName
|
|
99
|
+
* @param {object|false|undefined} payload
|
|
100
|
+
* @param {Function} searchFn
|
|
101
|
+
* @param {object} [config]
|
|
102
|
+
* @param {boolean} [config.useCache]
|
|
103
|
+
* @param {number} [config.ttlMs]
|
|
104
|
+
* @returns {Promise<*>}
|
|
105
|
+
*/
|
|
106
|
+
export async function resolveCachedSearch(
|
|
107
|
+
apiInstance,
|
|
108
|
+
methodName,
|
|
109
|
+
payload,
|
|
110
|
+
searchFn,
|
|
111
|
+
config = {},
|
|
112
|
+
)
|
|
113
|
+
{
|
|
114
|
+
const {
|
|
115
|
+
useCache = false,
|
|
116
|
+
ttlMs = DEFAULT_SEARCH_CACHE_TTL_MS,
|
|
117
|
+
} = config;
|
|
118
|
+
|
|
119
|
+
if (!useCache || !isDefaultSearchPayload(payload))
|
|
120
|
+
{
|
|
121
|
+
return searchFn(payload);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const cacheKey = buildSearchCacheKey(apiInstance, methodName);
|
|
125
|
+
const cached = cacheStore.get(cacheKey);
|
|
126
|
+
|
|
127
|
+
if (cached !== undefined)
|
|
128
|
+
{
|
|
129
|
+
return wrapSearchResult(clone_object(cached));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const result = await searchFn(payload);
|
|
133
|
+
const materialized = await materializeSearchResult(result);
|
|
134
|
+
|
|
135
|
+
cacheStore.set(cacheKey, clone_object(materialized), ttlMs);
|
|
136
|
+
|
|
137
|
+
return wrapSearchResult(materialized);
|
|
138
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Простое in-memory key-value хранилище с TTL.
|
|
3
|
+
*
|
|
4
|
+
* @version v.1.0 (13/06/2026)
|
|
5
|
+
*/
|
|
6
|
+
export class MemoryCacheStore
|
|
7
|
+
{
|
|
8
|
+
constructor()
|
|
9
|
+
{
|
|
10
|
+
this._entries = new Map();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} key
|
|
16
|
+
* @returns {*|undefined}
|
|
17
|
+
*/
|
|
18
|
+
get(key)
|
|
19
|
+
{
|
|
20
|
+
const entry = this._entries.get(key);
|
|
21
|
+
|
|
22
|
+
if (!entry)
|
|
23
|
+
{
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (entry.expiresAt <= Date.now())
|
|
28
|
+
{
|
|
29
|
+
this._entries.delete(key);
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return entry.value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {string} key
|
|
39
|
+
* @param {*} value
|
|
40
|
+
* @param {number} ttlMs
|
|
41
|
+
*/
|
|
42
|
+
set(key, value, ttlMs)
|
|
43
|
+
{
|
|
44
|
+
this._entries.set(key, {
|
|
45
|
+
value,
|
|
46
|
+
expiresAt: Date.now() + ttlMs,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {string} key
|
|
53
|
+
*/
|
|
54
|
+
delete(key)
|
|
55
|
+
{
|
|
56
|
+
this._entries.delete(key);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
clear()
|
|
61
|
+
{
|
|
62
|
+
this._entries.clear();
|
|
63
|
+
}
|
|
64
|
+
}
|