@hiliosai/sdk 0.1.0 → 0.1.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/INTEGRATION_SERVICE.md +394 -0
- package/package.json +5 -3
- package/src/configs/constants.ts +122 -0
- package/src/configs/index.ts +1 -0
- package/src/configs/moleculer/bulkhead.ts +8 -0
- package/src/configs/moleculer/channels.ts +102 -0
- package/src/configs/moleculer/circuit-breaker.ts +17 -0
- package/src/configs/moleculer/index.ts +85 -0
- package/src/configs/moleculer/logger.ts +17 -0
- package/src/configs/moleculer/metrics.ts +20 -0
- package/src/configs/moleculer/registry.ts +7 -0
- package/src/configs/moleculer/retry-policy.ts +17 -0
- package/src/configs/moleculer/tracing.ts +6 -0
- package/src/configs/moleculer/tracking.ts +6 -0
- package/src/datasources/base.datasource.ts +79 -0
- package/src/datasources/index.ts +1 -0
- package/src/middlewares/context-helpers.middleware.ts +4 -1
- package/src/middlewares/datasource.middleware.ts +3 -1
- package/src/middlewares/index.ts +0 -1
- package/src/mixins/datasource.mixin.ts +118 -0
- package/src/mixins/index.ts +1 -0
- package/src/service/define-integration.ts +177 -61
- package/src/service/define-service.ts +10 -30
- package/src/service/example-user/user.service.ts +2 -10
- package/src/types/context.ts +7 -4
- package/src/types/message.ts +1 -0
- package/src/types/service.ts +3 -4
- package/src/examples/cache-usage.example.ts +0 -293
- package/src/middlewares/cache.middleware.ts +0 -278
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example of how to use the cache middleware in a Moleculer service
|
|
3
|
-
* This file demonstrates various caching patterns and best practices
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {AppContext} from '../types';
|
|
7
|
-
import {CacheMiddleware} from '../middlewares/cache.middleware';
|
|
8
|
-
import {REDIS_URL} from '../env';
|
|
9
|
-
|
|
10
|
-
// Initialize cache on application start
|
|
11
|
-
export function initializeCache() {
|
|
12
|
-
CacheMiddleware.init({
|
|
13
|
-
redisUrl: REDIS_URL, // Falls back to in-memory if not set
|
|
14
|
-
ttl: 10 * 60 * 1000, // 10 minutes default TTL
|
|
15
|
-
namespace: 'app', // Namespace for cache keys
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Example 1: Simple get/set pattern
|
|
21
|
-
*/
|
|
22
|
-
export async function getUserWithCache(ctx: AppContext, userId: string) {
|
|
23
|
-
const cacheKey = `user:${userId}`;
|
|
24
|
-
|
|
25
|
-
// Try to get from cache
|
|
26
|
-
const cached = await ctx.cache.get(cacheKey);
|
|
27
|
-
if (cached) {
|
|
28
|
-
return cached;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Cache miss - fetch from database
|
|
32
|
-
const user = await fetchUserFromDatabase(userId);
|
|
33
|
-
|
|
34
|
-
// Store in cache for 5 minutes
|
|
35
|
-
await ctx.cache.set(cacheKey, user, 5 * 60 * 1000);
|
|
36
|
-
|
|
37
|
-
return user;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Example 2: Using the wrap pattern (recommended)
|
|
42
|
-
* Automatically handles get/set logic
|
|
43
|
-
*/
|
|
44
|
-
export async function getUserWithWrap(ctx: AppContext, userId: string) {
|
|
45
|
-
return ctx.cache.wrap(
|
|
46
|
-
`user:${userId}`,
|
|
47
|
-
async () => {
|
|
48
|
-
// This function only runs if cache miss
|
|
49
|
-
return fetchUserFromDatabase(userId);
|
|
50
|
-
},
|
|
51
|
-
5 * 60 * 1000 // TTL
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Example 3: Caching complex calculations
|
|
57
|
-
*/
|
|
58
|
-
export async function getUserPermissions(ctx: AppContext, userId: string) {
|
|
59
|
-
return ctx.cache.wrap(
|
|
60
|
-
`permissions:${userId}`,
|
|
61
|
-
async () => {
|
|
62
|
-
// Complex permission calculation
|
|
63
|
-
const user = await fetchUserFromDatabase(userId);
|
|
64
|
-
const roles = await fetchUserRoles(userId);
|
|
65
|
-
const permissions = calculatePermissions(user, roles);
|
|
66
|
-
return permissions;
|
|
67
|
-
},
|
|
68
|
-
10 * 60 * 1000 // Cache for 10 minutes
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Example 4: Batch operations
|
|
74
|
-
*/
|
|
75
|
-
export async function getUsersBatch(ctx: AppContext, userIds: string[]) {
|
|
76
|
-
// Generate cache keys
|
|
77
|
-
const cacheKeys = userIds.map((id) => `user:${id}`);
|
|
78
|
-
|
|
79
|
-
// Get all from cache at once
|
|
80
|
-
const cachedUsers = await ctx.cache.mget(...cacheKeys);
|
|
81
|
-
|
|
82
|
-
// Find missing users
|
|
83
|
-
const result: Array<Record<string, unknown>> = [];
|
|
84
|
-
const missingIds: string[] = [];
|
|
85
|
-
|
|
86
|
-
cachedUsers.forEach((user, index) => {
|
|
87
|
-
if (user !== undefined && user !== null) {
|
|
88
|
-
result[index] = user as Record<string, unknown>;
|
|
89
|
-
} else {
|
|
90
|
-
const userId = userIds[index];
|
|
91
|
-
if (userId !== undefined) {
|
|
92
|
-
missingIds.push(userId);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Fetch missing users from database
|
|
98
|
-
if (missingIds.length > 0) {
|
|
99
|
-
const missingUsers = await fetchUsersFromDatabase(missingIds);
|
|
100
|
-
|
|
101
|
-
// Prepare batch cache update
|
|
102
|
-
const cacheEntries: Record<string, unknown> = {};
|
|
103
|
-
missingUsers.forEach((user) => {
|
|
104
|
-
cacheEntries[`user:${user.id}`] = user;
|
|
105
|
-
// Add to result
|
|
106
|
-
const index = userIds.indexOf(user.id);
|
|
107
|
-
if (index !== -1) {
|
|
108
|
-
result[index] = user;
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Set all at once
|
|
113
|
-
await ctx.cache.mset(cacheEntries, 5 * 60 * 1000);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return result;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Example 5: Cache invalidation after update
|
|
121
|
-
*/
|
|
122
|
-
export async function updateUser(
|
|
123
|
-
ctx: AppContext,
|
|
124
|
-
userId: string,
|
|
125
|
-
updates: Record<string, unknown>
|
|
126
|
-
) {
|
|
127
|
-
// Update in database
|
|
128
|
-
const updatedUser = await updateUserInDatabase(userId, updates);
|
|
129
|
-
|
|
130
|
-
// Invalidate all related caches
|
|
131
|
-
await ctx.cache.mdel(
|
|
132
|
-
`user:${userId}`,
|
|
133
|
-
`permissions:${userId}`,
|
|
134
|
-
`profile:${userId}`
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
// Optionally, pre-populate cache with new data
|
|
138
|
-
await ctx.cache.set(`user:${userId}`, updatedUser, 5 * 60 * 1000);
|
|
139
|
-
|
|
140
|
-
return updatedUser;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Example 6: Check if key exists
|
|
145
|
-
*/
|
|
146
|
-
export async function isUserCached(ctx: AppContext, userId: string) {
|
|
147
|
-
return ctx.cache.has(`user:${userId}`);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Example 7: Clear entire cache namespace
|
|
152
|
-
*/
|
|
153
|
-
export async function clearAllCache(ctx: AppContext) {
|
|
154
|
-
await ctx.cache.clear();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Example 8: Cache with dynamic TTL based on data
|
|
159
|
-
*/
|
|
160
|
-
interface CacheableData {
|
|
161
|
-
type: 'static' | 'volatile' | 'normal';
|
|
162
|
-
[key: string]: unknown;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export async function cacheWithDynamicTTL(
|
|
166
|
-
ctx: AppContext,
|
|
167
|
-
key: string,
|
|
168
|
-
data: CacheableData
|
|
169
|
-
) {
|
|
170
|
-
// Determine TTL based on data characteristics
|
|
171
|
-
let ttl = 5 * 60 * 1000; // 5 minutes default
|
|
172
|
-
|
|
173
|
-
if (data.type === 'static') {
|
|
174
|
-
ttl = 60 * 60 * 1000; // 1 hour for static data
|
|
175
|
-
} else if (data.type === 'volatile') {
|
|
176
|
-
ttl = 60 * 1000; // 1 minute for volatile data
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
await ctx.cache.set(key, data, ttl);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Example 9: Cache status check
|
|
184
|
-
*/
|
|
185
|
-
export async function checkCacheHealth(ctx: AppContext) {
|
|
186
|
-
const testKey = 'health:check';
|
|
187
|
-
const testValue = {timestamp: Date.now(), random: Math.random()};
|
|
188
|
-
|
|
189
|
-
try {
|
|
190
|
-
// Test set
|
|
191
|
-
const setResult = await ctx.cache.set(testKey, testValue, 1000);
|
|
192
|
-
|
|
193
|
-
// Test get
|
|
194
|
-
const getResult = await ctx.cache.get(testKey);
|
|
195
|
-
|
|
196
|
-
// Test has
|
|
197
|
-
const hasResult = await ctx.cache.has(testKey);
|
|
198
|
-
|
|
199
|
-
// Test delete
|
|
200
|
-
const deleteResult = await ctx.cache.delete(testKey);
|
|
201
|
-
|
|
202
|
-
return {
|
|
203
|
-
healthy: true,
|
|
204
|
-
type: REDIS_URL ? 'Redis' : 'In-Memory',
|
|
205
|
-
operations: {
|
|
206
|
-
set: setResult,
|
|
207
|
-
get: JSON.stringify(getResult) === JSON.stringify(testValue),
|
|
208
|
-
has: hasResult,
|
|
209
|
-
delete: deleteResult,
|
|
210
|
-
},
|
|
211
|
-
};
|
|
212
|
-
} catch (error) {
|
|
213
|
-
return {
|
|
214
|
-
healthy: false,
|
|
215
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Mock database functions for example purposes
|
|
221
|
-
async function fetchUserFromDatabase(userId: string) {
|
|
222
|
-
return {
|
|
223
|
-
id: userId,
|
|
224
|
-
name: `User ${userId}`,
|
|
225
|
-
email: `user${userId}@example.com`,
|
|
226
|
-
createdAt: new Date(),
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
async function fetchUsersFromDatabase(userIds: string[]) {
|
|
231
|
-
return userIds.map((id) => ({
|
|
232
|
-
id,
|
|
233
|
-
name: `User ${id}`,
|
|
234
|
-
email: `user${id}@example.com`,
|
|
235
|
-
createdAt: new Date(),
|
|
236
|
-
}));
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
240
|
-
async function fetchUserRoles(_userId: string) {
|
|
241
|
-
return ['user', 'member'];
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
interface User {
|
|
245
|
-
id: string;
|
|
246
|
-
name: string;
|
|
247
|
-
email: string;
|
|
248
|
-
createdAt: Date;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function calculatePermissions(_user: User, roles: string[]) {
|
|
252
|
-
return {
|
|
253
|
-
canRead: true,
|
|
254
|
-
canWrite: roles.includes('admin'),
|
|
255
|
-
canDelete: roles.includes('admin'),
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
async function updateUserInDatabase(
|
|
260
|
-
userId: string,
|
|
261
|
-
updates: Record<string, unknown>
|
|
262
|
-
) {
|
|
263
|
-
return {
|
|
264
|
-
id: userId,
|
|
265
|
-
...updates,
|
|
266
|
-
updatedAt: new Date(),
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Usage in a Moleculer service:
|
|
272
|
-
*
|
|
273
|
-
* export default {
|
|
274
|
-
* name: 'users',
|
|
275
|
-
* middlewares: [CacheMiddleware],
|
|
276
|
-
*
|
|
277
|
-
* started() {
|
|
278
|
-
* initializeCache();
|
|
279
|
-
* },
|
|
280
|
-
*
|
|
281
|
-
* stopped() {
|
|
282
|
-
* return CacheMiddleware.stopped();
|
|
283
|
-
* },
|
|
284
|
-
*
|
|
285
|
-
* actions: {
|
|
286
|
-
* get: {
|
|
287
|
-
* async handler(ctx) {
|
|
288
|
-
* return getUserWithWrap(ctx, ctx.params.id);
|
|
289
|
-
* }
|
|
290
|
-
* }
|
|
291
|
-
* }
|
|
292
|
-
* }
|
|
293
|
-
*/
|
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
import KeyvRedis from '@keyv/redis';
|
|
2
|
-
import Keyv from 'keyv';
|
|
3
|
-
import {REDIS_URL} from '../env';
|
|
4
|
-
import type {AppContext} from '../types';
|
|
5
|
-
|
|
6
|
-
export interface CacheOptions {
|
|
7
|
-
redisUrl?: string;
|
|
8
|
-
ttl?: number; // Default TTL in milliseconds
|
|
9
|
-
namespace?: string;
|
|
10
|
-
serialize?: (value: unknown) => string;
|
|
11
|
-
deserialize?: (value: string) => unknown;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface CacheHelpers {
|
|
15
|
-
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
16
|
-
set<T = unknown>(key: string, value: T, ttl?: number): Promise<boolean>;
|
|
17
|
-
delete(key: string): Promise<boolean>;
|
|
18
|
-
clear(): Promise<void>;
|
|
19
|
-
has(key: string): Promise<boolean>;
|
|
20
|
-
mget<T = unknown>(...keys: string[]): Promise<Array<T | undefined>>;
|
|
21
|
-
mset(entries: Record<string, unknown>, ttl?: number): Promise<boolean>;
|
|
22
|
-
mdel(...keys: string[]): Promise<boolean>;
|
|
23
|
-
wrap<T = unknown>(
|
|
24
|
-
key: string,
|
|
25
|
-
factory: () => Promise<T> | T,
|
|
26
|
-
ttl?: number
|
|
27
|
-
): Promise<T>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export class CacheService {
|
|
31
|
-
private keyv: Keyv;
|
|
32
|
-
private defaultTtl: number;
|
|
33
|
-
|
|
34
|
-
constructor(options: CacheOptions = {}) {
|
|
35
|
-
const {
|
|
36
|
-
redisUrl = REDIS_URL,
|
|
37
|
-
ttl = 5 * 60 * 1000, // 5 minutes default
|
|
38
|
-
namespace = 'cache',
|
|
39
|
-
} = options;
|
|
40
|
-
|
|
41
|
-
this.defaultTtl = ttl;
|
|
42
|
-
|
|
43
|
-
// Configure store based on Redis availability
|
|
44
|
-
const store = redisUrl ? new KeyvRedis(redisUrl) : new Map(); // In-memory fallback
|
|
45
|
-
|
|
46
|
-
this.keyv = new Keyv({
|
|
47
|
-
store,
|
|
48
|
-
namespace,
|
|
49
|
-
ttl,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Error handling
|
|
53
|
-
this.keyv.on('error', () => {
|
|
54
|
-
// Silently handle errors - could be logged to a logger if available
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Get a value from cache
|
|
60
|
-
*/
|
|
61
|
-
async get<T = unknown>(key: string): Promise<T | undefined> {
|
|
62
|
-
try {
|
|
63
|
-
return await this.keyv.get<T>(key);
|
|
64
|
-
} catch {
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Set a value in cache
|
|
71
|
-
*/
|
|
72
|
-
async set<T = unknown>(
|
|
73
|
-
key: string,
|
|
74
|
-
value: T,
|
|
75
|
-
ttl?: number
|
|
76
|
-
): Promise<boolean> {
|
|
77
|
-
try {
|
|
78
|
-
return await this.keyv.set(key, value, ttl);
|
|
79
|
-
} catch {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Delete a key from cache
|
|
86
|
-
*/
|
|
87
|
-
async delete(key: string): Promise<boolean> {
|
|
88
|
-
try {
|
|
89
|
-
return await this.keyv.delete(key);
|
|
90
|
-
} catch {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Clear all keys with the namespace
|
|
97
|
-
*/
|
|
98
|
-
async clear(): Promise<void> {
|
|
99
|
-
try {
|
|
100
|
-
await this.keyv.clear();
|
|
101
|
-
} catch {
|
|
102
|
-
// Silently handle errors
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Check if a key exists
|
|
108
|
-
*/
|
|
109
|
-
async has(key: string): Promise<boolean> {
|
|
110
|
-
try {
|
|
111
|
-
return await this.keyv.has(key);
|
|
112
|
-
} catch {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Get multiple values at once
|
|
119
|
-
*/
|
|
120
|
-
async mget<T = unknown>(...keys: string[]): Promise<Array<T | undefined>> {
|
|
121
|
-
try {
|
|
122
|
-
const promises = keys.map((key) => this.get<T>(key));
|
|
123
|
-
return await Promise.all(promises);
|
|
124
|
-
} catch {
|
|
125
|
-
return keys.map(() => undefined);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Set multiple values at once
|
|
131
|
-
*/
|
|
132
|
-
async mset(entries: Record<string, unknown>, ttl?: number): Promise<boolean> {
|
|
133
|
-
try {
|
|
134
|
-
const promises = Object.entries(entries).map(([key, value]) =>
|
|
135
|
-
this.set(key, value, ttl)
|
|
136
|
-
);
|
|
137
|
-
const results = await Promise.all(promises);
|
|
138
|
-
return results.every(Boolean);
|
|
139
|
-
} catch {
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Delete multiple keys at once
|
|
146
|
-
*/
|
|
147
|
-
async mdel(...keys: string[]): Promise<boolean> {
|
|
148
|
-
try {
|
|
149
|
-
const promises = keys.map((key) => this.delete(key));
|
|
150
|
-
const results = await Promise.all(promises);
|
|
151
|
-
return results.every(Boolean);
|
|
152
|
-
} catch {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Wrap pattern: Get from cache or calculate and cache
|
|
159
|
-
*/
|
|
160
|
-
async wrap<T = unknown>(
|
|
161
|
-
key: string,
|
|
162
|
-
factory: () => Promise<T> | T,
|
|
163
|
-
ttl?: number
|
|
164
|
-
): Promise<T> {
|
|
165
|
-
try {
|
|
166
|
-
// Try to get from cache
|
|
167
|
-
const cached = await this.get<T>(key);
|
|
168
|
-
if (cached !== undefined) {
|
|
169
|
-
return cached;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Calculate value
|
|
173
|
-
const value = await factory();
|
|
174
|
-
|
|
175
|
-
// Cache the value
|
|
176
|
-
await this.set(key, value, ttl ?? this.defaultTtl);
|
|
177
|
-
|
|
178
|
-
return value;
|
|
179
|
-
} catch {
|
|
180
|
-
// In case of error, still try to return the factory value
|
|
181
|
-
return await factory();
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Disconnect the cache (for cleanup)
|
|
187
|
-
*/
|
|
188
|
-
async disconnect(): Promise<void> {
|
|
189
|
-
try {
|
|
190
|
-
await this.keyv.disconnect();
|
|
191
|
-
} catch {
|
|
192
|
-
// Silently handle errors
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Create cache middleware with specific options for a service
|
|
199
|
-
*/
|
|
200
|
-
export function createCacheMiddleware(options: CacheOptions = {}) {
|
|
201
|
-
let cacheInstance: CacheService | null = null;
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
/**
|
|
205
|
-
* Initialize cache on service start
|
|
206
|
-
*/
|
|
207
|
-
started() {
|
|
208
|
-
cacheInstance = new CacheService(options);
|
|
209
|
-
},
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Add cache helpers to context
|
|
213
|
-
*/
|
|
214
|
-
localAction(handler: (...args: unknown[]) => unknown) {
|
|
215
|
-
return function CacheMiddlewareWrapper(this: unknown, ctx: AppContext) {
|
|
216
|
-
cacheInstance ??= new CacheService(options);
|
|
217
|
-
|
|
218
|
-
const instance = cacheInstance;
|
|
219
|
-
|
|
220
|
-
// Create scoped cache helpers
|
|
221
|
-
const cache: CacheHelpers = {
|
|
222
|
-
get: <T = unknown>(key: string) => instance.get<T>(key),
|
|
223
|
-
|
|
224
|
-
set: <T = unknown>(key: string, value: T, ttl?: number) =>
|
|
225
|
-
instance.set(key, value, ttl),
|
|
226
|
-
|
|
227
|
-
delete: (key: string) => instance.delete(key),
|
|
228
|
-
|
|
229
|
-
clear: () => instance.clear(),
|
|
230
|
-
|
|
231
|
-
has: (key: string) => instance.has(key),
|
|
232
|
-
|
|
233
|
-
mget: <T = unknown>(...keys: string[]) => instance.mget<T>(...keys),
|
|
234
|
-
|
|
235
|
-
mset: (entries: Record<string, unknown>, ttl?: number) =>
|
|
236
|
-
instance.mset(entries, ttl),
|
|
237
|
-
|
|
238
|
-
mdel: (...keys: string[]) => instance.mdel(...keys),
|
|
239
|
-
|
|
240
|
-
wrap: <T = unknown>(
|
|
241
|
-
key: string,
|
|
242
|
-
factory: () => Promise<T> | T,
|
|
243
|
-
ttl?: number
|
|
244
|
-
) => instance.wrap<T>(key, factory, ttl),
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
// Add cache to context
|
|
248
|
-
ctx.cache = cache;
|
|
249
|
-
|
|
250
|
-
// Call the original handler
|
|
251
|
-
return handler.call(this, ctx);
|
|
252
|
-
};
|
|
253
|
-
},
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Cleanup on service stop
|
|
257
|
-
*/
|
|
258
|
-
stopped() {
|
|
259
|
-
if (cacheInstance) {
|
|
260
|
-
return cacheInstance.disconnect();
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Default cache middleware (backwards compatibility)
|
|
267
|
-
export const CacheMiddleware = createCacheMiddleware();
|
|
268
|
-
|
|
269
|
-
// Singleton instance for direct usage
|
|
270
|
-
let globalCacheInstance: CacheService | null = null;
|
|
271
|
-
|
|
272
|
-
export const getCacheInstance = (options?: CacheOptions) => {
|
|
273
|
-
if (options) {
|
|
274
|
-
return new CacheService(options);
|
|
275
|
-
}
|
|
276
|
-
globalCacheInstance ??= new CacheService();
|
|
277
|
-
return globalCacheInstance;
|
|
278
|
-
};
|