@ehildt/nestjs-config-factory 1.0.0 → 1.1.0
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/README.md
CHANGED
|
@@ -18,9 +18,9 @@ A NestJS configuration library providing decorators and modules for caching retu
|
|
|
18
18
|
|
|
19
19
|
# StatusBadges
|
|
20
20
|
|
|
21
|
-

|
|
22
|
+

|
|
23
|
+

|
|
24
24
|
|
|
25
25
|
</div>
|
|
26
26
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import Joi from 'joi';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
interface CacheConfig<T = unknown> {
|
|
4
|
+
schema?: Joi.Schema<T>;
|
|
5
|
+
ttl?: number | false;
|
|
6
|
+
}
|
|
7
|
+
declare function CacheReturnValue<T = unknown>(config?: Joi.Schema<T> | CacheConfig<T> | false): MethodDecorator & PropertyDecorator;
|
|
4
8
|
|
|
5
|
-
export { CacheReturnValue };
|
|
9
|
+
export { type CacheConfig, CacheReturnValue };
|
|
@@ -12,24 +12,69 @@ var ValidateReturnValueError = class extends Error {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
// src/cache-return-value/cache-return-value.decorator.ts
|
|
15
|
-
|
|
15
|
+
var DEFAULT_TTL_MS = 5 * 60 * 1e3;
|
|
16
|
+
function isJoiSchema(value) {
|
|
17
|
+
return typeof value === "object" && value !== null && "type" in value && typeof value.validate === "function";
|
|
18
|
+
}
|
|
19
|
+
function parseConfig(config) {
|
|
20
|
+
if (config === false) return { ttl: false };
|
|
21
|
+
if (config === void 0) return { ttl: DEFAULT_TTL_MS };
|
|
22
|
+
if (typeof config === "number") return { ttl: config };
|
|
23
|
+
if (isJoiSchema(config)) return { schema: config, ttl: DEFAULT_TTL_MS };
|
|
24
|
+
return {
|
|
25
|
+
schema: config.schema,
|
|
26
|
+
ttl: config.ttl ?? DEFAULT_TTL_MS
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function validateValue(value, schema) {
|
|
30
|
+
if (schema) {
|
|
31
|
+
const { error, value: validated } = schema.validate(value, {
|
|
32
|
+
abortEarly: false
|
|
33
|
+
});
|
|
34
|
+
if (error) throw new ValidateReturnValueError(`Schema violation`, error.details);
|
|
35
|
+
return validated;
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
function isExpired(entry, ttl) {
|
|
40
|
+
if (ttl === false) return false;
|
|
41
|
+
return Date.now() - entry.timestamp > ttl;
|
|
42
|
+
}
|
|
43
|
+
function CacheReturnValue(config) {
|
|
16
44
|
return function(_target, propertyKey, descriptor) {
|
|
45
|
+
const { schema, ttl } = parseConfig(config);
|
|
46
|
+
const setCache = (instance, key, value, refreshPromise) => {
|
|
47
|
+
let instanceCache = INSTANCE_CACHE.get(instance);
|
|
48
|
+
if (!instanceCache) {
|
|
49
|
+
instanceCache = /* @__PURE__ */ new Map();
|
|
50
|
+
INSTANCE_CACHE.set(instance, instanceCache);
|
|
51
|
+
}
|
|
52
|
+
instanceCache.set(key, { value, timestamp: Date.now(), refreshPromise });
|
|
53
|
+
};
|
|
17
54
|
if (descriptor?.get) {
|
|
18
55
|
const originalGet = descriptor.get;
|
|
19
56
|
descriptor.get = function() {
|
|
20
57
|
const cache = getCacheFor(this);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (error) throw new ValidateReturnValueError(`Schema violation`, error.details);
|
|
28
|
-
value = validated;
|
|
29
|
-
}
|
|
30
|
-
cache.set(String(propertyKey), value);
|
|
58
|
+
const key = String(propertyKey);
|
|
59
|
+
const entry = cache.get(key);
|
|
60
|
+
if (!entry) {
|
|
61
|
+
const value = validateValue(originalGet.call(this), schema);
|
|
62
|
+
setCache(this, key, value);
|
|
63
|
+
return value;
|
|
31
64
|
}
|
|
32
|
-
|
|
65
|
+
if (!isExpired(entry, ttl)) return entry.value;
|
|
66
|
+
const staleValue = entry.value;
|
|
67
|
+
if (entry.refreshPromise) return staleValue;
|
|
68
|
+
const refreshPromise = (async () => {
|
|
69
|
+
return validateValue(await originalGet.call(this), schema);
|
|
70
|
+
})();
|
|
71
|
+
setCache(this, key, staleValue, refreshPromise);
|
|
72
|
+
refreshPromise.then((freshValue) => {
|
|
73
|
+
setCache(this, key, freshValue);
|
|
74
|
+
}).catch(() => {
|
|
75
|
+
setCache(this, key, staleValue);
|
|
76
|
+
});
|
|
77
|
+
return staleValue;
|
|
33
78
|
};
|
|
34
79
|
}
|
|
35
80
|
if (descriptor?.value instanceof Function) {
|
|
@@ -37,18 +82,25 @@ function CacheReturnValue(schema) {
|
|
|
37
82
|
descriptor.value = function(...args) {
|
|
38
83
|
const cache = getCacheFor(this);
|
|
39
84
|
const key = hashPayload(`${String(propertyKey)}:${JSON.stringify(args)}`);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
});
|
|
46
|
-
if (error) throw new ValidateReturnValueError(`Schema violation`, error.details);
|
|
47
|
-
result = validated;
|
|
48
|
-
}
|
|
49
|
-
cache.set(key, result);
|
|
85
|
+
const entry = cache.get(key);
|
|
86
|
+
if (!entry) {
|
|
87
|
+
const result = validateValue(originalMethod.apply(this, args), schema);
|
|
88
|
+
setCache(this, key, result);
|
|
89
|
+
return result;
|
|
50
90
|
}
|
|
51
|
-
return
|
|
91
|
+
if (!isExpired(entry, ttl)) return entry.value;
|
|
92
|
+
const staleValue = entry.value;
|
|
93
|
+
if (entry.refreshPromise) return staleValue;
|
|
94
|
+
const refreshPromise = (async () => {
|
|
95
|
+
return validateValue(await originalMethod.apply(this, args), schema);
|
|
96
|
+
})();
|
|
97
|
+
setCache(this, key, staleValue, refreshPromise);
|
|
98
|
+
refreshPromise.then((freshValue) => {
|
|
99
|
+
setCache(this, key, freshValue);
|
|
100
|
+
}).catch(() => {
|
|
101
|
+
setCache(this, key, staleValue);
|
|
102
|
+
});
|
|
103
|
+
return staleValue;
|
|
52
104
|
};
|
|
53
105
|
}
|
|
54
106
|
};
|
|
@@ -9,6 +9,20 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
9
9
|
return result;
|
|
10
10
|
};
|
|
11
11
|
var ConfigFactoryModule = class {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a dynamic module with the specified configuration providers.
|
|
14
|
+
* All providers are automatically exported for use by other modules.
|
|
15
|
+
*
|
|
16
|
+
* @param options - Configuration options
|
|
17
|
+
* @param options.global - Whether to register as a global module
|
|
18
|
+
* @param options.providers - Array of configuration provider classes
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ConfigFactoryModule.forRoot({
|
|
22
|
+
* global: true,
|
|
23
|
+
* providers: [DatabaseConfigService, AppConfigService],
|
|
24
|
+
* });
|
|
25
|
+
*/
|
|
12
26
|
static forRoot({ global = false, providers = [] } = {}) {
|
|
13
27
|
if (!providers.length)
|
|
14
28
|
console.warn("[ConfigFactoryModule] No providers registered.");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ehildt/nestjs-config-factory",
|
|
3
3
|
"description": "A dynamic NestJS module factory designed to register, manage, and expose custom configuration providers throughout the application.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|