koatty_cacheable 1.6.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +54 -24
- package/dist/README.md +54 -24
- package/dist/index.d.ts +9 -7
- package/dist/index.js +152 -91
- package/dist/index.mjs +152 -91
- package/dist/package.json +6 -3
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [2.0.1](https://github.com/thinkkoa/koatty_cacheable/compare/v1.6.1...v2.0.1) (2025-11-02)
|
|
6
|
+
|
|
7
|
+
## [2.0.0](https://github.com/thinkkoa/koatty_cacheable/compare/v1.6.1...v2.0.0) (2025-11-02)
|
|
8
|
+
|
|
5
9
|
### [1.6.1](https://github.com/thinkkoa/koatty_cacheable/compare/v1.6.0...v1.6.1) (2025-06-09)
|
|
6
10
|
|
|
7
11
|
## [1.6.0](https://github.com/thinkkoa/koatty_cacheable/compare/v1.5.0...v1.6.0) (2024-11-07)
|
package/README.md
CHANGED
|
@@ -20,33 +20,61 @@ Koatty框架的 CacheAble, CacheEvict 缓存装饰器支持库,提供方法级
|
|
|
20
20
|
npm install koatty_cacheable
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## 快速开始
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
### 1. 生成插件模板
|
|
26
|
+
|
|
27
|
+
使用 Koatty CLI 生成插件模板:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
kt plugin Cacheable
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
创建 `src/plugin/Cacheable.ts`:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { Plugin, IPlugin, App } from "koatty";
|
|
37
|
+
import { KoattyCached } from "koatty_cacheable";
|
|
38
|
+
|
|
39
|
+
@Plugin()
|
|
40
|
+
export class Cacheable implements IPlugin {
|
|
41
|
+
run(options: any, app: App) {
|
|
42
|
+
return KoattyCached(options, app);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. 配置插件
|
|
48
|
+
|
|
49
|
+
更新 `src/config/plugin.ts`:
|
|
26
50
|
|
|
27
51
|
```typescript
|
|
28
52
|
export default {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ... 其他配置
|
|
53
|
+
list: ["Cacheable"], // 插件加载顺序
|
|
54
|
+
config: {
|
|
55
|
+
Cacheable: {
|
|
56
|
+
type: "memory", // 缓存类型: "redis" 或 "memory",默认为 "memory"
|
|
57
|
+
db: 0,
|
|
58
|
+
timeout: 30,
|
|
59
|
+
// Redis 配置 (当 type 为 "redis" 时)
|
|
60
|
+
// key_prefix: "koatty",
|
|
61
|
+
// host: '127.0.0.1',
|
|
62
|
+
// port: 6379,
|
|
63
|
+
// name: "",
|
|
64
|
+
// username: "",
|
|
65
|
+
// password: "",
|
|
66
|
+
// pool_size: 10,
|
|
67
|
+
// conn_timeout: 30
|
|
68
|
+
}
|
|
69
|
+
}
|
|
47
70
|
};
|
|
48
71
|
```
|
|
49
72
|
|
|
73
|
+
**注意事项**:
|
|
74
|
+
- 插件会在应用启动时自动初始化缓存
|
|
75
|
+
- 必须在插件配置中提供正确的缓存配置
|
|
76
|
+
- 如果缓存未正确初始化,装饰器方法会直接执行而不进行缓存(优雅降级)
|
|
77
|
+
|
|
50
78
|
## 使用方法
|
|
51
79
|
|
|
52
80
|
### 基本用法
|
|
@@ -178,10 +206,12 @@ export class ProductService {
|
|
|
178
206
|
|
|
179
207
|
## 注意事项
|
|
180
208
|
|
|
181
|
-
1.
|
|
182
|
-
2.
|
|
183
|
-
3.
|
|
184
|
-
4.
|
|
209
|
+
1. **初始化顺序**: 必须先调用 `KoattyCached()` 初始化缓存,然后再使用装饰器。建议在应用启动时(如 `init()` 方法中)进行初始化
|
|
210
|
+
2. 装饰器只能用于 `SERVICE` 和 `COMPONENT` 类型的类
|
|
211
|
+
3. 被装饰的方法必须是异步方法(返回 Promise)
|
|
212
|
+
4. 缓存的数据会自动进行 JSON 序列化/反序列化
|
|
213
|
+
5. 如果缓存服务不可用,方法会正常执行,不会抛出错误(优雅降级)
|
|
214
|
+
6. 缓存键长度超过 128 字符时会自动使用 murmur hash 进行压缩
|
|
185
215
|
|
|
186
216
|
## 许可证
|
|
187
217
|
|
package/dist/README.md
CHANGED
|
@@ -20,33 +20,61 @@ Koatty框架的 CacheAble, CacheEvict 缓存装饰器支持库,提供方法级
|
|
|
20
20
|
npm install koatty_cacheable
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## 快速开始
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
### 1. 生成插件模板
|
|
26
|
+
|
|
27
|
+
使用 Koatty CLI 生成插件模板:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
kt plugin Cacheable
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
创建 `src/plugin/Cacheable.ts`:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { Plugin, IPlugin, App } from "koatty";
|
|
37
|
+
import { KoattyCached } from "koatty_cacheable";
|
|
38
|
+
|
|
39
|
+
@Plugin()
|
|
40
|
+
export class Cacheable implements IPlugin {
|
|
41
|
+
run(options: any, app: App) {
|
|
42
|
+
return KoattyCached(options, app);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. 配置插件
|
|
48
|
+
|
|
49
|
+
更新 `src/config/plugin.ts`:
|
|
26
50
|
|
|
27
51
|
```typescript
|
|
28
52
|
export default {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ... 其他配置
|
|
53
|
+
list: ["Cacheable"], // 插件加载顺序
|
|
54
|
+
config: {
|
|
55
|
+
Cacheable: {
|
|
56
|
+
type: "memory", // 缓存类型: "redis" 或 "memory",默认为 "memory"
|
|
57
|
+
db: 0,
|
|
58
|
+
timeout: 30,
|
|
59
|
+
// Redis 配置 (当 type 为 "redis" 时)
|
|
60
|
+
// key_prefix: "koatty",
|
|
61
|
+
// host: '127.0.0.1',
|
|
62
|
+
// port: 6379,
|
|
63
|
+
// name: "",
|
|
64
|
+
// username: "",
|
|
65
|
+
// password: "",
|
|
66
|
+
// pool_size: 10,
|
|
67
|
+
// conn_timeout: 30
|
|
68
|
+
}
|
|
69
|
+
}
|
|
47
70
|
};
|
|
48
71
|
```
|
|
49
72
|
|
|
73
|
+
**注意事项**:
|
|
74
|
+
- 插件会在应用启动时自动初始化缓存
|
|
75
|
+
- 必须在插件配置中提供正确的缓存配置
|
|
76
|
+
- 如果缓存未正确初始化,装饰器方法会直接执行而不进行缓存(优雅降级)
|
|
77
|
+
|
|
50
78
|
## 使用方法
|
|
51
79
|
|
|
52
80
|
### 基本用法
|
|
@@ -178,10 +206,12 @@ export class ProductService {
|
|
|
178
206
|
|
|
179
207
|
## 注意事项
|
|
180
208
|
|
|
181
|
-
1.
|
|
182
|
-
2.
|
|
183
|
-
3.
|
|
184
|
-
4.
|
|
209
|
+
1. **初始化顺序**: 必须先调用 `KoattyCached()` 初始化缓存,然后再使用装饰器。建议在应用启动时(如 `init()` 方法中)进行初始化
|
|
210
|
+
2. 装饰器只能用于 `SERVICE` 和 `COMPONENT` 类型的类
|
|
211
|
+
3. 被装饰的方法必须是异步方法(返回 Promise)
|
|
212
|
+
4. 缓存的数据会自动进行 JSON 序列化/反序列化
|
|
213
|
+
5. 如果缓存服务不可用,方法会正常执行,不会抛出错误(优雅降级)
|
|
214
|
+
6. 缓存键长度超过 128 字符时会自动使用 murmur hash 进行压缩
|
|
185
215
|
|
|
186
216
|
## 许可证
|
|
187
217
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @Author: richen
|
|
3
|
-
* @Date: 2025-
|
|
3
|
+
* @Date: 2025-11-02 09:20:03
|
|
4
4
|
* @License: BSD (3-Clause)
|
|
5
5
|
* @Copyright (c) - <richenlin(at)gmail.com>
|
|
6
6
|
* @HomePage: https://koatty.org/
|
|
7
7
|
*/
|
|
8
|
-
import { Application } from 'koatty_container';
|
|
9
8
|
import { CacheStore } from 'koatty_store';
|
|
9
|
+
import { Koatty } from 'koatty_core';
|
|
10
|
+
import { StoreOptions } from 'koatty_store';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Decorate this method to support caching.
|
|
@@ -62,6 +63,7 @@ export declare function CacheEvict(cacheName: string, opt?: CacheEvictOpt): (tar
|
|
|
62
63
|
export declare interface CacheEvictOpt {
|
|
63
64
|
params?: string[];
|
|
64
65
|
delayedDoubleDeletion?: boolean;
|
|
66
|
+
delayTime?: number;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
/**
|
|
@@ -73,15 +75,15 @@ export declare function CloseCacheStore(): Promise<void>;
|
|
|
73
75
|
* get instances of storeCache
|
|
74
76
|
*
|
|
75
77
|
* @export
|
|
76
|
-
* @param {
|
|
78
|
+
* @param {StoreOptions} options
|
|
77
79
|
* @returns {*} {CacheStore}
|
|
78
80
|
*/
|
|
79
|
-
export declare function GetCacheStore(
|
|
81
|
+
export declare function GetCacheStore(options?: StoreOptions): Promise<CacheStore>;
|
|
80
82
|
|
|
81
83
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
+
* @param options - The options for the cached options
|
|
85
|
+
* @param app - The Koatty application instance
|
|
84
86
|
*/
|
|
85
|
-
export declare function
|
|
87
|
+
export declare function KoattyCached(options: StoreOptions, app: Koatty): Promise<void>;
|
|
86
88
|
|
|
87
89
|
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,102 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @Author: richen
|
|
3
|
-
* @Date: 2025-
|
|
3
|
+
* @Date: 2025-11-02 09:19:59
|
|
4
4
|
* @License: BSD (3-Clause)
|
|
5
5
|
* @Copyright (c) - <richenlin(at)gmail.com>
|
|
6
6
|
* @HomePage: https://koatty.org/
|
|
7
7
|
*/
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
|
-
var koatty_container = require('koatty_container');
|
|
11
10
|
var koatty_lib = require('koatty_lib');
|
|
12
11
|
var koatty_logger = require('koatty_logger');
|
|
13
12
|
var koatty_store = require('koatty_store');
|
|
13
|
+
var koatty_container = require('koatty_container');
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* @Description:
|
|
17
|
+
* @Usage:
|
|
18
|
+
* @Author: richen
|
|
19
|
+
* @Date: 2024-11-07 16:00:02
|
|
20
|
+
* @LastEditTime: 2024-11-07 16:00:05
|
|
21
|
+
* @License: BSD (3-Clause)
|
|
22
|
+
* @Copyright (c): <richenlin(at)gmail.com>
|
|
23
|
+
*/
|
|
24
|
+
// storeCache
|
|
25
|
+
const storeCache = {
|
|
26
|
+
store: null
|
|
27
|
+
};
|
|
28
|
+
// Promise to track initialization in progress
|
|
29
|
+
let initPromise = null;
|
|
30
|
+
/**
|
|
31
|
+
* get instances of storeCache
|
|
32
|
+
*
|
|
33
|
+
* @export
|
|
34
|
+
* @param {StoreOptions} options
|
|
35
|
+
* @returns {*} {CacheStore}
|
|
36
|
+
*/
|
|
37
|
+
async function GetCacheStore(options) {
|
|
38
|
+
// Return existing store if available
|
|
39
|
+
if (storeCache.store && storeCache.store.getConnection) {
|
|
40
|
+
return storeCache.store;
|
|
41
|
+
}
|
|
42
|
+
// If initialization is in progress, wait for it
|
|
43
|
+
if (initPromise) {
|
|
44
|
+
return initPromise;
|
|
45
|
+
}
|
|
46
|
+
if (koatty_lib.Helper.isEmpty(options)) {
|
|
47
|
+
if (!storeCache.store) {
|
|
48
|
+
koatty_logger.DefaultLogger.Warn(`CacheStore not initialized. Please call KoattyCached() first with proper options in your application startup.`);
|
|
49
|
+
}
|
|
50
|
+
return storeCache.store || null;
|
|
51
|
+
}
|
|
52
|
+
// Start initialization and track it
|
|
53
|
+
initPromise = (async () => {
|
|
54
|
+
try {
|
|
55
|
+
storeCache.store = koatty_store.CacheStore.getInstance(options);
|
|
56
|
+
if (!koatty_lib.Helper.isFunction(storeCache.store.getConnection)) {
|
|
57
|
+
throw Error(`CacheStore connection failed. `);
|
|
58
|
+
}
|
|
59
|
+
await storeCache.store.client.getConnection();
|
|
60
|
+
return storeCache.store;
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
// Clear init promise after completion
|
|
64
|
+
initPromise = null;
|
|
65
|
+
}
|
|
66
|
+
})();
|
|
67
|
+
return initPromise;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Close cache store connection for cleanup (mainly for testing)
|
|
71
|
+
*/
|
|
72
|
+
async function CloseCacheStore() {
|
|
73
|
+
if (storeCache.store) {
|
|
74
|
+
try {
|
|
75
|
+
if (storeCache.store.client) {
|
|
76
|
+
const client = storeCache.store.client;
|
|
77
|
+
if (typeof client.quit === 'function') {
|
|
78
|
+
await client.quit();
|
|
79
|
+
}
|
|
80
|
+
else if (typeof client.close === 'function') {
|
|
81
|
+
await client.close();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Ignore cleanup errors
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Clear the CacheStore singleton instance
|
|
90
|
+
try {
|
|
91
|
+
await koatty_store.CacheStore.clearAllInstances();
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Ignore cleanup errors
|
|
95
|
+
}
|
|
96
|
+
// Always clear the cache
|
|
97
|
+
storeCache.store = null;
|
|
98
|
+
initPromise = null;
|
|
99
|
+
}
|
|
14
100
|
|
|
15
101
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
16
102
|
/*
|
|
@@ -35,8 +121,11 @@ function getArgs(func) {
|
|
|
35
121
|
if (args && args.length > 1) {
|
|
36
122
|
// Split parameters into array and clean them
|
|
37
123
|
return args[1].split(",").map(function (a) {
|
|
38
|
-
// Remove
|
|
39
|
-
|
|
124
|
+
// Remove multi-line comments /* ... */ and single-line comments //
|
|
125
|
+
const param = a.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "").trim();
|
|
126
|
+
// Extract parameter name (before : or = or end of string)
|
|
127
|
+
const match = param.match(/^(\w+)/);
|
|
128
|
+
return match ? match[1] : "";
|
|
40
129
|
}).filter(function (ae) {
|
|
41
130
|
// Filter out empty strings
|
|
42
131
|
return ae;
|
|
@@ -95,84 +184,6 @@ async function asyncDelayedExecution(fn, ms) {
|
|
|
95
184
|
return fn();
|
|
96
185
|
}
|
|
97
186
|
|
|
98
|
-
/*
|
|
99
|
-
* @Description:
|
|
100
|
-
* @Usage:
|
|
101
|
-
* @Author: richen
|
|
102
|
-
* @Date: 2024-11-07 16:00:02
|
|
103
|
-
* @LastEditTime: 2024-11-07 16:00:05
|
|
104
|
-
* @License: BSD (3-Clause)
|
|
105
|
-
* @Copyright (c): <richenlin(at)gmail.com>
|
|
106
|
-
*/
|
|
107
|
-
// storeCache
|
|
108
|
-
const storeCache = {
|
|
109
|
-
store: null
|
|
110
|
-
};
|
|
111
|
-
/**
|
|
112
|
-
* get instances of storeCache
|
|
113
|
-
*
|
|
114
|
-
* @export
|
|
115
|
-
* @param {Application} app
|
|
116
|
-
* @returns {*} {CacheStore}
|
|
117
|
-
*/
|
|
118
|
-
async function GetCacheStore(app) {
|
|
119
|
-
if (storeCache.store && storeCache.store.getConnection) {
|
|
120
|
-
return storeCache.store;
|
|
121
|
-
}
|
|
122
|
-
let opt = {
|
|
123
|
-
type: "memory",
|
|
124
|
-
db: 0,
|
|
125
|
-
timeout: 30
|
|
126
|
-
};
|
|
127
|
-
if (app && koatty_lib.Helper.isFunction(app.config)) {
|
|
128
|
-
opt = app.config("CacheStore") || app.config("CacheStore", "db");
|
|
129
|
-
if (koatty_lib.Helper.isEmpty(opt)) {
|
|
130
|
-
koatty_logger.DefaultLogger.Warn(`Missing CacheStore server configuration. Please write a configuration item with the key name 'CacheStore' in the db.ts file.`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
storeCache.store = koatty_store.CacheStore.getInstance(opt);
|
|
134
|
-
if (!koatty_lib.Helper.isFunction(storeCache.store.getConnection)) {
|
|
135
|
-
throw Error(`CacheStore connection failed. `);
|
|
136
|
-
}
|
|
137
|
-
await storeCache.store.client.getConnection();
|
|
138
|
-
return storeCache.store;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* initiation CacheStore connection and client.
|
|
142
|
-
*
|
|
143
|
-
*/
|
|
144
|
-
async function InitCacheStore() {
|
|
145
|
-
if (storeCache.store) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
const app = koatty_container.IOCContainer.getApp();
|
|
149
|
-
app?.once("appReady", async () => {
|
|
150
|
-
await GetCacheStore(app);
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Close cache store connection for cleanup (mainly for testing)
|
|
155
|
-
*/
|
|
156
|
-
async function CloseCacheStore() {
|
|
157
|
-
if (storeCache.store && storeCache.store.client) {
|
|
158
|
-
try {
|
|
159
|
-
const client = storeCache.store.client;
|
|
160
|
-
if (typeof client.quit === 'function') {
|
|
161
|
-
await client.quit();
|
|
162
|
-
}
|
|
163
|
-
else if (typeof client.close === 'function') {
|
|
164
|
-
await client.close();
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
// Ignore cleanup errors
|
|
169
|
-
}
|
|
170
|
-
finally {
|
|
171
|
-
storeCache.store = null;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
187
|
/*
|
|
177
188
|
* @Author: richen
|
|
178
189
|
* @Date: 2020-07-06 19:53:43
|
|
@@ -213,12 +224,22 @@ function CacheAble(cacheName, opt = {
|
|
|
213
224
|
const funcParams = getArgs(target[methodName]);
|
|
214
225
|
// Get the defined parameter location
|
|
215
226
|
const paramIndexes = getParamIndex(funcParams, mergedOpt.params || []);
|
|
227
|
+
// Validate parameters
|
|
228
|
+
const invalidParams = [];
|
|
229
|
+
(mergedOpt.params || []).forEach((param, index) => {
|
|
230
|
+
if (paramIndexes[index] === -1) {
|
|
231
|
+
invalidParams.push(param);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
if (invalidParams.length > 0) {
|
|
235
|
+
koatty_logger.DefaultLogger.Warn(`CacheAble: Parameter(s) [${invalidParams.join(", ")}] not found in method ${String(methodName)}. These parameters will be ignored.`);
|
|
236
|
+
}
|
|
216
237
|
descriptor = {
|
|
217
238
|
configurable,
|
|
218
239
|
enumerable,
|
|
219
240
|
writable: true,
|
|
220
241
|
async value(...props) {
|
|
221
|
-
const store = await GetCacheStore(
|
|
242
|
+
const store = await GetCacheStore().catch((e) => {
|
|
222
243
|
koatty_logger.DefaultLogger.error("Get cache store instance failed." + e.message);
|
|
223
244
|
return null;
|
|
224
245
|
});
|
|
@@ -228,7 +249,17 @@ function CacheAble(cacheName, opt = {
|
|
|
228
249
|
koatty_logger.DefaultLogger.error("Cache get error:" + e.message);
|
|
229
250
|
});
|
|
230
251
|
if (!koatty_lib.Helper.isEmpty(res)) {
|
|
231
|
-
|
|
252
|
+
try {
|
|
253
|
+
return JSON.parse(res);
|
|
254
|
+
}
|
|
255
|
+
catch (e) {
|
|
256
|
+
const error = e;
|
|
257
|
+
koatty_logger.DefaultLogger.error("Cache JSON parse error:" + error.message);
|
|
258
|
+
// 如果解析失败,删除损坏的缓存,重新执行方法
|
|
259
|
+
store.del(key).catch((err) => {
|
|
260
|
+
koatty_logger.DefaultLogger.error("Cache del error after parse failure:" + err.message);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
232
263
|
}
|
|
233
264
|
const result = await value.apply(this, props);
|
|
234
265
|
// async refresh store
|
|
@@ -243,8 +274,6 @@ function CacheAble(cacheName, opt = {
|
|
|
243
274
|
}
|
|
244
275
|
}
|
|
245
276
|
};
|
|
246
|
-
// bind app_ready hook event
|
|
247
|
-
InitCacheStore();
|
|
248
277
|
return descriptor;
|
|
249
278
|
};
|
|
250
279
|
}
|
|
@@ -278,12 +307,22 @@ function CacheEvict(cacheName, opt = {
|
|
|
278
307
|
const funcParams = getArgs(target[methodName]);
|
|
279
308
|
// Get the defined parameter location
|
|
280
309
|
const paramIndexes = getParamIndex(funcParams, opt.params || []);
|
|
310
|
+
// Validate parameters
|
|
311
|
+
const invalidParams = [];
|
|
312
|
+
(opt.params || []).forEach((param, index) => {
|
|
313
|
+
if (paramIndexes[index] === -1) {
|
|
314
|
+
invalidParams.push(param);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
if (invalidParams.length > 0) {
|
|
318
|
+
koatty_logger.DefaultLogger.Warn(`CacheEvict: Parameter(s) [${invalidParams.join(", ")}] not found in method ${String(methodName)}. These parameters will be ignored.`);
|
|
319
|
+
}
|
|
281
320
|
descriptor = {
|
|
282
321
|
configurable,
|
|
283
322
|
enumerable,
|
|
284
323
|
writable: true,
|
|
285
324
|
async value(...props) {
|
|
286
|
-
const store = await GetCacheStore(
|
|
325
|
+
const store = await GetCacheStore().catch((e) => {
|
|
287
326
|
koatty_logger.DefaultLogger.error("Get cache store instance failed." + e.message);
|
|
288
327
|
return null;
|
|
289
328
|
});
|
|
@@ -294,7 +333,7 @@ function CacheEvict(cacheName, opt = {
|
|
|
294
333
|
koatty_logger.DefaultLogger.error("Cache delete error:" + e.message);
|
|
295
334
|
});
|
|
296
335
|
if (opt.delayedDoubleDeletion) {
|
|
297
|
-
const delayTime = 5000;
|
|
336
|
+
const delayTime = opt.delayTime || 5000;
|
|
298
337
|
asyncDelayedExecution(() => {
|
|
299
338
|
store.del(key).catch((e) => {
|
|
300
339
|
koatty_logger.DefaultLogger.error("Cache double delete error:" + e.message);
|
|
@@ -309,14 +348,36 @@ function CacheEvict(cacheName, opt = {
|
|
|
309
348
|
}
|
|
310
349
|
}
|
|
311
350
|
};
|
|
312
|
-
// bind app_ready hook event
|
|
313
|
-
InitCacheStore();
|
|
314
351
|
return descriptor;
|
|
315
352
|
};
|
|
316
353
|
}
|
|
317
354
|
|
|
355
|
+
/**
|
|
356
|
+
* defaultOptions
|
|
357
|
+
*/
|
|
358
|
+
const defaultOptions = {
|
|
359
|
+
type: "memory",
|
|
360
|
+
db: 0,
|
|
361
|
+
timeout: 30
|
|
362
|
+
};
|
|
363
|
+
/**
|
|
364
|
+
* @param options - The options for the cached options
|
|
365
|
+
* @param app - The Koatty application instance
|
|
366
|
+
*/
|
|
367
|
+
async function KoattyCached(options, app) {
|
|
368
|
+
options = { ...defaultOptions, ...options };
|
|
369
|
+
app.once("appReady", async function () {
|
|
370
|
+
// 初始化缓存存储
|
|
371
|
+
await GetCacheStore(options);
|
|
372
|
+
});
|
|
373
|
+
app.on("appStop", async function () {
|
|
374
|
+
// 关闭缓存存储
|
|
375
|
+
await CloseCacheStore();
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
318
379
|
exports.CacheAble = CacheAble;
|
|
319
380
|
exports.CacheEvict = CacheEvict;
|
|
320
381
|
exports.CloseCacheStore = CloseCacheStore;
|
|
321
382
|
exports.GetCacheStore = GetCacheStore;
|
|
322
|
-
exports.
|
|
383
|
+
exports.KoattyCached = KoattyCached;
|
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,100 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @Author: richen
|
|
3
|
-
* @Date: 2025-
|
|
3
|
+
* @Date: 2025-11-02 09:19:59
|
|
4
4
|
* @License: BSD (3-Clause)
|
|
5
5
|
* @Copyright (c) - <richenlin(at)gmail.com>
|
|
6
6
|
* @HomePage: https://koatty.org/
|
|
7
7
|
*/
|
|
8
|
-
import { IOCContainer } from 'koatty_container';
|
|
9
8
|
import { Helper } from 'koatty_lib';
|
|
10
9
|
import { DefaultLogger } from 'koatty_logger';
|
|
11
10
|
import { CacheStore } from 'koatty_store';
|
|
11
|
+
import { IOCContainer } from 'koatty_container';
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* @Description:
|
|
15
|
+
* @Usage:
|
|
16
|
+
* @Author: richen
|
|
17
|
+
* @Date: 2024-11-07 16:00:02
|
|
18
|
+
* @LastEditTime: 2024-11-07 16:00:05
|
|
19
|
+
* @License: BSD (3-Clause)
|
|
20
|
+
* @Copyright (c): <richenlin(at)gmail.com>
|
|
21
|
+
*/
|
|
22
|
+
// storeCache
|
|
23
|
+
const storeCache = {
|
|
24
|
+
store: null
|
|
25
|
+
};
|
|
26
|
+
// Promise to track initialization in progress
|
|
27
|
+
let initPromise = null;
|
|
28
|
+
/**
|
|
29
|
+
* get instances of storeCache
|
|
30
|
+
*
|
|
31
|
+
* @export
|
|
32
|
+
* @param {StoreOptions} options
|
|
33
|
+
* @returns {*} {CacheStore}
|
|
34
|
+
*/
|
|
35
|
+
async function GetCacheStore(options) {
|
|
36
|
+
// Return existing store if available
|
|
37
|
+
if (storeCache.store && storeCache.store.getConnection) {
|
|
38
|
+
return storeCache.store;
|
|
39
|
+
}
|
|
40
|
+
// If initialization is in progress, wait for it
|
|
41
|
+
if (initPromise) {
|
|
42
|
+
return initPromise;
|
|
43
|
+
}
|
|
44
|
+
if (Helper.isEmpty(options)) {
|
|
45
|
+
if (!storeCache.store) {
|
|
46
|
+
DefaultLogger.Warn(`CacheStore not initialized. Please call KoattyCached() first with proper options in your application startup.`);
|
|
47
|
+
}
|
|
48
|
+
return storeCache.store || null;
|
|
49
|
+
}
|
|
50
|
+
// Start initialization and track it
|
|
51
|
+
initPromise = (async () => {
|
|
52
|
+
try {
|
|
53
|
+
storeCache.store = CacheStore.getInstance(options);
|
|
54
|
+
if (!Helper.isFunction(storeCache.store.getConnection)) {
|
|
55
|
+
throw Error(`CacheStore connection failed. `);
|
|
56
|
+
}
|
|
57
|
+
await storeCache.store.client.getConnection();
|
|
58
|
+
return storeCache.store;
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
// Clear init promise after completion
|
|
62
|
+
initPromise = null;
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
return initPromise;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Close cache store connection for cleanup (mainly for testing)
|
|
69
|
+
*/
|
|
70
|
+
async function CloseCacheStore() {
|
|
71
|
+
if (storeCache.store) {
|
|
72
|
+
try {
|
|
73
|
+
if (storeCache.store.client) {
|
|
74
|
+
const client = storeCache.store.client;
|
|
75
|
+
if (typeof client.quit === 'function') {
|
|
76
|
+
await client.quit();
|
|
77
|
+
}
|
|
78
|
+
else if (typeof client.close === 'function') {
|
|
79
|
+
await client.close();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Ignore cleanup errors
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Clear the CacheStore singleton instance
|
|
88
|
+
try {
|
|
89
|
+
await CacheStore.clearAllInstances();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Ignore cleanup errors
|
|
93
|
+
}
|
|
94
|
+
// Always clear the cache
|
|
95
|
+
storeCache.store = null;
|
|
96
|
+
initPromise = null;
|
|
97
|
+
}
|
|
12
98
|
|
|
13
99
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
14
100
|
/*
|
|
@@ -33,8 +119,11 @@ function getArgs(func) {
|
|
|
33
119
|
if (args && args.length > 1) {
|
|
34
120
|
// Split parameters into array and clean them
|
|
35
121
|
return args[1].split(",").map(function (a) {
|
|
36
|
-
// Remove
|
|
37
|
-
|
|
122
|
+
// Remove multi-line comments /* ... */ and single-line comments //
|
|
123
|
+
const param = a.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "").trim();
|
|
124
|
+
// Extract parameter name (before : or = or end of string)
|
|
125
|
+
const match = param.match(/^(\w+)/);
|
|
126
|
+
return match ? match[1] : "";
|
|
38
127
|
}).filter(function (ae) {
|
|
39
128
|
// Filter out empty strings
|
|
40
129
|
return ae;
|
|
@@ -93,84 +182,6 @@ async function asyncDelayedExecution(fn, ms) {
|
|
|
93
182
|
return fn();
|
|
94
183
|
}
|
|
95
184
|
|
|
96
|
-
/*
|
|
97
|
-
* @Description:
|
|
98
|
-
* @Usage:
|
|
99
|
-
* @Author: richen
|
|
100
|
-
* @Date: 2024-11-07 16:00:02
|
|
101
|
-
* @LastEditTime: 2024-11-07 16:00:05
|
|
102
|
-
* @License: BSD (3-Clause)
|
|
103
|
-
* @Copyright (c): <richenlin(at)gmail.com>
|
|
104
|
-
*/
|
|
105
|
-
// storeCache
|
|
106
|
-
const storeCache = {
|
|
107
|
-
store: null
|
|
108
|
-
};
|
|
109
|
-
/**
|
|
110
|
-
* get instances of storeCache
|
|
111
|
-
*
|
|
112
|
-
* @export
|
|
113
|
-
* @param {Application} app
|
|
114
|
-
* @returns {*} {CacheStore}
|
|
115
|
-
*/
|
|
116
|
-
async function GetCacheStore(app) {
|
|
117
|
-
if (storeCache.store && storeCache.store.getConnection) {
|
|
118
|
-
return storeCache.store;
|
|
119
|
-
}
|
|
120
|
-
let opt = {
|
|
121
|
-
type: "memory",
|
|
122
|
-
db: 0,
|
|
123
|
-
timeout: 30
|
|
124
|
-
};
|
|
125
|
-
if (app && Helper.isFunction(app.config)) {
|
|
126
|
-
opt = app.config("CacheStore") || app.config("CacheStore", "db");
|
|
127
|
-
if (Helper.isEmpty(opt)) {
|
|
128
|
-
DefaultLogger.Warn(`Missing CacheStore server configuration. Please write a configuration item with the key name 'CacheStore' in the db.ts file.`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
storeCache.store = CacheStore.getInstance(opt);
|
|
132
|
-
if (!Helper.isFunction(storeCache.store.getConnection)) {
|
|
133
|
-
throw Error(`CacheStore connection failed. `);
|
|
134
|
-
}
|
|
135
|
-
await storeCache.store.client.getConnection();
|
|
136
|
-
return storeCache.store;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* initiation CacheStore connection and client.
|
|
140
|
-
*
|
|
141
|
-
*/
|
|
142
|
-
async function InitCacheStore() {
|
|
143
|
-
if (storeCache.store) {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const app = IOCContainer.getApp();
|
|
147
|
-
app?.once("appReady", async () => {
|
|
148
|
-
await GetCacheStore(app);
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Close cache store connection for cleanup (mainly for testing)
|
|
153
|
-
*/
|
|
154
|
-
async function CloseCacheStore() {
|
|
155
|
-
if (storeCache.store && storeCache.store.client) {
|
|
156
|
-
try {
|
|
157
|
-
const client = storeCache.store.client;
|
|
158
|
-
if (typeof client.quit === 'function') {
|
|
159
|
-
await client.quit();
|
|
160
|
-
}
|
|
161
|
-
else if (typeof client.close === 'function') {
|
|
162
|
-
await client.close();
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
// Ignore cleanup errors
|
|
167
|
-
}
|
|
168
|
-
finally {
|
|
169
|
-
storeCache.store = null;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
185
|
/*
|
|
175
186
|
* @Author: richen
|
|
176
187
|
* @Date: 2020-07-06 19:53:43
|
|
@@ -211,12 +222,22 @@ function CacheAble(cacheName, opt = {
|
|
|
211
222
|
const funcParams = getArgs(target[methodName]);
|
|
212
223
|
// Get the defined parameter location
|
|
213
224
|
const paramIndexes = getParamIndex(funcParams, mergedOpt.params || []);
|
|
225
|
+
// Validate parameters
|
|
226
|
+
const invalidParams = [];
|
|
227
|
+
(mergedOpt.params || []).forEach((param, index) => {
|
|
228
|
+
if (paramIndexes[index] === -1) {
|
|
229
|
+
invalidParams.push(param);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
if (invalidParams.length > 0) {
|
|
233
|
+
DefaultLogger.Warn(`CacheAble: Parameter(s) [${invalidParams.join(", ")}] not found in method ${String(methodName)}. These parameters will be ignored.`);
|
|
234
|
+
}
|
|
214
235
|
descriptor = {
|
|
215
236
|
configurable,
|
|
216
237
|
enumerable,
|
|
217
238
|
writable: true,
|
|
218
239
|
async value(...props) {
|
|
219
|
-
const store = await GetCacheStore(
|
|
240
|
+
const store = await GetCacheStore().catch((e) => {
|
|
220
241
|
DefaultLogger.error("Get cache store instance failed." + e.message);
|
|
221
242
|
return null;
|
|
222
243
|
});
|
|
@@ -226,7 +247,17 @@ function CacheAble(cacheName, opt = {
|
|
|
226
247
|
DefaultLogger.error("Cache get error:" + e.message);
|
|
227
248
|
});
|
|
228
249
|
if (!Helper.isEmpty(res)) {
|
|
229
|
-
|
|
250
|
+
try {
|
|
251
|
+
return JSON.parse(res);
|
|
252
|
+
}
|
|
253
|
+
catch (e) {
|
|
254
|
+
const error = e;
|
|
255
|
+
DefaultLogger.error("Cache JSON parse error:" + error.message);
|
|
256
|
+
// 如果解析失败,删除损坏的缓存,重新执行方法
|
|
257
|
+
store.del(key).catch((err) => {
|
|
258
|
+
DefaultLogger.error("Cache del error after parse failure:" + err.message);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
230
261
|
}
|
|
231
262
|
const result = await value.apply(this, props);
|
|
232
263
|
// async refresh store
|
|
@@ -241,8 +272,6 @@ function CacheAble(cacheName, opt = {
|
|
|
241
272
|
}
|
|
242
273
|
}
|
|
243
274
|
};
|
|
244
|
-
// bind app_ready hook event
|
|
245
|
-
InitCacheStore();
|
|
246
275
|
return descriptor;
|
|
247
276
|
};
|
|
248
277
|
}
|
|
@@ -276,12 +305,22 @@ function CacheEvict(cacheName, opt = {
|
|
|
276
305
|
const funcParams = getArgs(target[methodName]);
|
|
277
306
|
// Get the defined parameter location
|
|
278
307
|
const paramIndexes = getParamIndex(funcParams, opt.params || []);
|
|
308
|
+
// Validate parameters
|
|
309
|
+
const invalidParams = [];
|
|
310
|
+
(opt.params || []).forEach((param, index) => {
|
|
311
|
+
if (paramIndexes[index] === -1) {
|
|
312
|
+
invalidParams.push(param);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
if (invalidParams.length > 0) {
|
|
316
|
+
DefaultLogger.Warn(`CacheEvict: Parameter(s) [${invalidParams.join(", ")}] not found in method ${String(methodName)}. These parameters will be ignored.`);
|
|
317
|
+
}
|
|
279
318
|
descriptor = {
|
|
280
319
|
configurable,
|
|
281
320
|
enumerable,
|
|
282
321
|
writable: true,
|
|
283
322
|
async value(...props) {
|
|
284
|
-
const store = await GetCacheStore(
|
|
323
|
+
const store = await GetCacheStore().catch((e) => {
|
|
285
324
|
DefaultLogger.error("Get cache store instance failed." + e.message);
|
|
286
325
|
return null;
|
|
287
326
|
});
|
|
@@ -292,7 +331,7 @@ function CacheEvict(cacheName, opt = {
|
|
|
292
331
|
DefaultLogger.error("Cache delete error:" + e.message);
|
|
293
332
|
});
|
|
294
333
|
if (opt.delayedDoubleDeletion) {
|
|
295
|
-
const delayTime = 5000;
|
|
334
|
+
const delayTime = opt.delayTime || 5000;
|
|
296
335
|
asyncDelayedExecution(() => {
|
|
297
336
|
store.del(key).catch((e) => {
|
|
298
337
|
DefaultLogger.error("Cache double delete error:" + e.message);
|
|
@@ -307,10 +346,32 @@ function CacheEvict(cacheName, opt = {
|
|
|
307
346
|
}
|
|
308
347
|
}
|
|
309
348
|
};
|
|
310
|
-
// bind app_ready hook event
|
|
311
|
-
InitCacheStore();
|
|
312
349
|
return descriptor;
|
|
313
350
|
};
|
|
314
351
|
}
|
|
315
352
|
|
|
316
|
-
|
|
353
|
+
/**
|
|
354
|
+
* defaultOptions
|
|
355
|
+
*/
|
|
356
|
+
const defaultOptions = {
|
|
357
|
+
type: "memory",
|
|
358
|
+
db: 0,
|
|
359
|
+
timeout: 30
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* @param options - The options for the cached options
|
|
363
|
+
* @param app - The Koatty application instance
|
|
364
|
+
*/
|
|
365
|
+
async function KoattyCached(options, app) {
|
|
366
|
+
options = { ...defaultOptions, ...options };
|
|
367
|
+
app.once("appReady", async function () {
|
|
368
|
+
// 初始化缓存存储
|
|
369
|
+
await GetCacheStore(options);
|
|
370
|
+
});
|
|
371
|
+
app.on("appStop", async function () {
|
|
372
|
+
// 关闭缓存存储
|
|
373
|
+
await CloseCacheStore();
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export { CacheAble, CacheEvict, CloseCacheStore, GetCacheStore, KoattyCached };
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koatty_cacheable",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Cacheable for koatty.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "npm run build:js && npm run build:dts && npm run build:doc && npm run build:cp",
|
|
@@ -87,13 +87,16 @@
|
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"koatty_container": "^1.x.x",
|
|
90
|
+
"koatty_core": "2.x.x",
|
|
90
91
|
"koatty_lib": "^1.x.x",
|
|
91
92
|
"koatty_logger": "^2.x.x",
|
|
92
|
-
"koatty_store": "^1.
|
|
93
|
+
"koatty_store": "^1.x.x"
|
|
93
94
|
},
|
|
94
95
|
"peerDependencies": {
|
|
95
96
|
"koatty_container": "^1.x.x",
|
|
97
|
+
"koatty_core": "2.x.x",
|
|
96
98
|
"koatty_lib": "^1.x.x",
|
|
97
|
-
"koatty_logger": "^2.x.x"
|
|
99
|
+
"koatty_logger": "^2.x.x",
|
|
100
|
+
"koatty_store": "^1.x.x"
|
|
98
101
|
}
|
|
99
102
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koatty_cacheable",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Cacheable for koatty.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "npm run build:js && npm run build:dts && npm run build:doc && npm run build:cp",
|
|
@@ -87,13 +87,16 @@
|
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"koatty_container": "^1.x.x",
|
|
90
|
+
"koatty_core": "2.x.x",
|
|
90
91
|
"koatty_lib": "^1.x.x",
|
|
91
92
|
"koatty_logger": "^2.x.x",
|
|
92
|
-
"koatty_store": "^1.
|
|
93
|
+
"koatty_store": "^1.x.x"
|
|
93
94
|
},
|
|
94
95
|
"peerDependencies": {
|
|
95
96
|
"koatty_container": "^1.x.x",
|
|
97
|
+
"koatty_core": "2.x.x",
|
|
96
98
|
"koatty_lib": "^1.x.x",
|
|
97
|
-
"koatty_logger": "^2.x.x"
|
|
99
|
+
"koatty_logger": "^2.x.x",
|
|
100
|
+
"koatty_store": "^1.x.x"
|
|
98
101
|
}
|
|
99
102
|
}
|