complexqa_frontend_core 1.18.4 → 1.18.6
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 +1 -1
- package/publish/api/api_abstract_class.js +1 -0
- package/publish/api/api_abstract_reference_class.js +22 -2
- package/publish/api/cache/api_search_cache.js +110 -0
- package/publish/api/cache/index.js +9 -0
- package/publish/api/cache/memory_cache_store.js +64 -0
- package/publish/types/core/0_familyGeneralElement.js +1 -1
- package/publish/types/core/1_familyContext.js +0 -4
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,110 @@
|
|
|
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 {object} apiInstance
|
|
72
|
+
* @param {string} methodName
|
|
73
|
+
* @param {object|false|undefined} payload
|
|
74
|
+
* @param {Function} searchFn
|
|
75
|
+
* @param {object} [config]
|
|
76
|
+
* @param {boolean} [config.useCache]
|
|
77
|
+
* @param {number} [config.ttlMs]
|
|
78
|
+
* @returns {Promise<*>}
|
|
79
|
+
*/
|
|
80
|
+
export async function resolveCachedSearch(
|
|
81
|
+
apiInstance,
|
|
82
|
+
methodName,
|
|
83
|
+
payload,
|
|
84
|
+
searchFn,
|
|
85
|
+
config = {},
|
|
86
|
+
)
|
|
87
|
+
{
|
|
88
|
+
const {
|
|
89
|
+
useCache = false,
|
|
90
|
+
ttlMs = DEFAULT_SEARCH_CACHE_TTL_MS,
|
|
91
|
+
} = config;
|
|
92
|
+
|
|
93
|
+
if (!useCache || !isDefaultSearchPayload(payload))
|
|
94
|
+
{
|
|
95
|
+
return searchFn(payload);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const cacheKey = buildSearchCacheKey(apiInstance, methodName);
|
|
99
|
+
const cached = cacheStore.get(cacheKey);
|
|
100
|
+
|
|
101
|
+
if (cached !== undefined)
|
|
102
|
+
{
|
|
103
|
+
return Promise.resolve(clone_object(cached));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const result = await searchFn(payload);
|
|
107
|
+
cacheStore.set(cacheKey, clone_object(result), ttlMs);
|
|
108
|
+
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -203,7 +203,7 @@ export class familyGeneralElement
|
|
|
203
203
|
|
|
204
204
|
|
|
205
205
|
/**
|
|
206
|
-
* Метаданные reference из целевого типа (api, primary_key, search
|
|
206
|
+
* Метаданные reference из целевого типа (api, primary_key, search).
|
|
207
207
|
*
|
|
208
208
|
* @param {string} attribute
|
|
209
209
|
* @returns {object|false}
|
|
@@ -41,12 +41,9 @@ export class familyContext extends familyGeneralElement
|
|
|
41
41
|
operator : 'LIKE',
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
static reference_scope_filters = [ 'project_id' ];
|
|
45
|
-
|
|
46
44
|
reference_api_namespace = familyContext.reference_api_namespace;
|
|
47
45
|
reference_label_key = familyContext.reference_label_key;
|
|
48
46
|
reference_search = familyContext.reference_search;
|
|
49
|
-
reference_scope_filters = familyContext.reference_scope_filters;
|
|
50
47
|
|
|
51
48
|
available_enum_values = familyContext.get_base_available_enum_values();
|
|
52
49
|
|
|
@@ -114,7 +111,6 @@ export class familyContext extends familyGeneralElement
|
|
|
114
111
|
primary_key : this.primary_key,
|
|
115
112
|
label_key : this.reference_label_key,
|
|
116
113
|
search : { ...this.reference_search },
|
|
117
|
-
scope_filters : [ ...this.reference_scope_filters ],
|
|
118
114
|
};
|
|
119
115
|
}
|
|
120
116
|
|