@engjts/nexus 0.1.8 → 0.1.9
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/BENCHMARK_REPORT.md +0 -343
- package/documentation/01-getting-started.md +0 -240
- package/documentation/02-context.md +0 -335
- package/documentation/03-routing.md +0 -397
- package/documentation/04-middleware.md +0 -483
- package/documentation/05-validation.md +0 -514
- package/documentation/06-error-handling.md +0 -465
- package/documentation/07-performance.md +0 -364
- package/documentation/08-adapters.md +0 -470
- package/documentation/09-api-reference.md +0 -548
- package/documentation/10-examples.md +0 -582
- package/documentation/11-deployment.md +0 -477
- package/documentation/12-sentry.md +0 -620
- package/documentation/13-sentry-data-storage.md +0 -996
- package/documentation/14-sentry-data-reference.md +0 -457
- package/documentation/15-sentry-summary.md +0 -409
- package/documentation/16-alerts-system.md +0 -745
- package/documentation/17-alert-adapters.md +0 -696
- package/documentation/18-alerts-implementation-summary.md +0 -385
- package/documentation/19-class-based-routing.md +0 -840
- package/documentation/20-websocket-realtime.md +0 -813
- package/documentation/21-cache-system.md +0 -510
- package/documentation/22-job-queue.md +0 -772
- package/documentation/23-sentry-plugin.md +0 -551
- package/documentation/24-testing-utilities.md +0 -1287
- package/documentation/25-api-versioning.md +0 -533
- package/documentation/26-context-store.md +0 -607
- package/documentation/27-dependency-injection.md +0 -329
- package/documentation/28-lifecycle-hooks.md +0 -521
- package/documentation/29-package-structure.md +0 -196
- package/documentation/30-plugin-system.md +0 -414
- package/documentation/31-jwt-authentication.md +0 -597
- package/documentation/32-cli.md +0 -268
- package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
- package/documentation/ALERTS-INDEX.md +0 -330
- package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
- package/documentation/README.md +0 -178
- package/documentation/index.html +0 -34
- package/modern_framework_paper.md +0 -1870
- package/public/css/style.css +0 -87
- package/public/index.html +0 -34
- package/public/js/app.js +0 -27
- package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
- package/src/advanced/cache/MultiTierCache.ts +0 -194
- package/src/advanced/cache/RedisCacheStore.ts +0 -341
- package/src/advanced/cache/index.ts +0 -5
- package/src/advanced/cache/types.ts +0 -40
- package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
- package/src/advanced/graphql/index.ts +0 -22
- package/src/advanced/graphql/server.ts +0 -252
- package/src/advanced/graphql/types.ts +0 -42
- package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
- package/src/advanced/jobs/JobQueue.ts +0 -556
- package/src/advanced/jobs/RedisQueueStore.ts +0 -367
- package/src/advanced/jobs/index.ts +0 -5
- package/src/advanced/jobs/types.ts +0 -70
- package/src/advanced/observability/APMManager.ts +0 -163
- package/src/advanced/observability/AlertManager.ts +0 -109
- package/src/advanced/observability/MetricRegistry.ts +0 -151
- package/src/advanced/observability/ObservabilityCenter.ts +0 -304
- package/src/advanced/observability/StructuredLogger.ts +0 -154
- package/src/advanced/observability/TracingManager.ts +0 -117
- package/src/advanced/observability/adapters.ts +0 -304
- package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
- package/src/advanced/observability/index.ts +0 -11
- package/src/advanced/observability/types.ts +0 -174
- package/src/advanced/playground/extractPathParams.ts +0 -6
- package/src/advanced/playground/generateFieldExample.ts +0 -31
- package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
- package/src/advanced/playground/generateSummary.ts +0 -19
- package/src/advanced/playground/getTagFromPath.ts +0 -9
- package/src/advanced/playground/index.ts +0 -8
- package/src/advanced/playground/playground.ts +0 -250
- package/src/advanced/playground/types.ts +0 -49
- package/src/advanced/playground/zodToExample.ts +0 -16
- package/src/advanced/playground/zodToParams.ts +0 -15
- package/src/advanced/postman/buildAuth.ts +0 -31
- package/src/advanced/postman/buildBody.ts +0 -15
- package/src/advanced/postman/buildQueryParams.ts +0 -27
- package/src/advanced/postman/buildRequestItem.ts +0 -36
- package/src/advanced/postman/buildResponses.ts +0 -11
- package/src/advanced/postman/buildUrl.ts +0 -33
- package/src/advanced/postman/capitalize.ts +0 -4
- package/src/advanced/postman/generateCollection.ts +0 -59
- package/src/advanced/postman/generateEnvironment.ts +0 -34
- package/src/advanced/postman/generateExampleFromZod.ts +0 -21
- package/src/advanced/postman/generateFieldExample.ts +0 -45
- package/src/advanced/postman/generateName.ts +0 -20
- package/src/advanced/postman/generateUUID.ts +0 -11
- package/src/advanced/postman/getTagFromPath.ts +0 -10
- package/src/advanced/postman/index.ts +0 -28
- package/src/advanced/postman/postman.ts +0 -156
- package/src/advanced/postman/slugify.ts +0 -7
- package/src/advanced/postman/types.ts +0 -140
- package/src/advanced/realtime/index.ts +0 -18
- package/src/advanced/realtime/websocket.ts +0 -231
- package/src/advanced/sentry/index.ts +0 -1236
- package/src/advanced/sentry/types.ts +0 -355
- package/src/advanced/static/generateDirectoryListing.ts +0 -47
- package/src/advanced/static/generateETag.ts +0 -7
- package/src/advanced/static/getMimeType.ts +0 -9
- package/src/advanced/static/index.ts +0 -32
- package/src/advanced/static/isSafePath.ts +0 -13
- package/src/advanced/static/publicDir.ts +0 -21
- package/src/advanced/static/serveStatic.ts +0 -225
- package/src/advanced/static/spa.ts +0 -24
- package/src/advanced/static/types.ts +0 -159
- package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
- package/src/advanced/swagger/buildOperation.ts +0 -61
- package/src/advanced/swagger/buildParameters.ts +0 -61
- package/src/advanced/swagger/buildRequestBody.ts +0 -21
- package/src/advanced/swagger/buildResponses.ts +0 -54
- package/src/advanced/swagger/capitalize.ts +0 -5
- package/src/advanced/swagger/convertPath.ts +0 -9
- package/src/advanced/swagger/createSwagger.ts +0 -12
- package/src/advanced/swagger/generateOperationId.ts +0 -21
- package/src/advanced/swagger/generateSpec.ts +0 -105
- package/src/advanced/swagger/generateSummary.ts +0 -24
- package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
- package/src/advanced/swagger/generateThemeCss.ts +0 -53
- package/src/advanced/swagger/index.ts +0 -25
- package/src/advanced/swagger/swagger.ts +0 -237
- package/src/advanced/swagger/types.ts +0 -206
- package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
- package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
- package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
- package/src/advanced/testing/factory.ts +0 -509
- package/src/advanced/testing/harness.ts +0 -612
- package/src/advanced/testing/index.ts +0 -430
- package/src/advanced/testing/load-test.ts +0 -618
- package/src/advanced/testing/mock-server.ts +0 -498
- package/src/advanced/testing/mock.ts +0 -670
- package/src/cli/bin.ts +0 -9
- package/src/cli/cli.ts +0 -158
- package/src/cli/commands/add.ts +0 -178
- package/src/cli/commands/build.ts +0 -73
- package/src/cli/commands/create.ts +0 -166
- package/src/cli/commands/dev.ts +0 -85
- package/src/cli/commands/generate.ts +0 -99
- package/src/cli/commands/help.ts +0 -95
- package/src/cli/commands/init.ts +0 -91
- package/src/cli/commands/version.ts +0 -38
- package/src/cli/index.ts +0 -6
- package/src/cli/templates/generators.ts +0 -359
- package/src/cli/templates/index.ts +0 -680
- package/src/cli/utils/exec.ts +0 -52
- package/src/cli/utils/file-system.ts +0 -78
- package/src/cli/utils/logger.ts +0 -111
- package/src/core/adapter.ts +0 -88
- package/src/core/application.ts +0 -1453
- package/src/core/context-pool.ts +0 -79
- package/src/core/context.ts +0 -856
- package/src/core/index.ts +0 -94
- package/src/core/middleware.ts +0 -272
- package/src/core/performance/buffer-pool.ts +0 -108
- package/src/core/performance/middleware-optimizer.ts +0 -162
- package/src/core/plugin/PluginManager.ts +0 -435
- package/src/core/plugin/builder.ts +0 -358
- package/src/core/plugin/index.ts +0 -50
- package/src/core/plugin/types.ts +0 -214
- package/src/core/router/file-router.ts +0 -623
- package/src/core/router/index.ts +0 -260
- package/src/core/router/radix-tree.ts +0 -242
- package/src/core/serializer.ts +0 -397
- package/src/core/store/index.ts +0 -30
- package/src/core/store/registry.ts +0 -178
- package/src/core/store/request-store.ts +0 -240
- package/src/core/store/types.ts +0 -233
- package/src/core/types.ts +0 -616
- package/src/database/adapter.ts +0 -35
- package/src/database/adapters/index.ts +0 -1
- package/src/database/adapters/mysql.ts +0 -669
- package/src/database/database.ts +0 -70
- package/src/database/dialect.ts +0 -388
- package/src/database/index.ts +0 -12
- package/src/database/migrations.ts +0 -86
- package/src/database/optimizer.ts +0 -125
- package/src/database/query-builder.ts +0 -404
- package/src/database/realtime.ts +0 -53
- package/src/database/schema.ts +0 -71
- package/src/database/transactions.ts +0 -56
- package/src/database/types.ts +0 -87
- package/src/deployment/cluster.ts +0 -471
- package/src/deployment/config.ts +0 -454
- package/src/deployment/docker.ts +0 -599
- package/src/deployment/graceful-shutdown.ts +0 -373
- package/src/deployment/index.ts +0 -56
- package/src/index.ts +0 -281
- package/src/security/adapter.ts +0 -318
- package/src/security/auth/JWTPlugin.ts +0 -234
- package/src/security/auth/JWTProvider.ts +0 -316
- package/src/security/auth/adapter.ts +0 -12
- package/src/security/auth/jwt.ts +0 -234
- package/src/security/auth/middleware.ts +0 -188
- package/src/security/csrf.ts +0 -220
- package/src/security/headers.ts +0 -108
- package/src/security/index.ts +0 -60
- package/src/security/rate-limit/adapter.ts +0 -7
- package/src/security/rate-limit/memory.ts +0 -108
- package/src/security/rate-limit/middleware.ts +0 -181
- package/src/security/sanitization.ts +0 -75
- package/src/security/types.ts +0 -240
- package/src/security/utils.ts +0 -52
- package/tsconfig.json +0 -39
package/public/css/style.css
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/* Nexus Framework - Sample Styles */
|
|
2
|
-
|
|
3
|
-
* {
|
|
4
|
-
box-sizing: border-box;
|
|
5
|
-
margin: 0;
|
|
6
|
-
padding: 0;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
body {
|
|
10
|
-
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
11
|
-
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
12
|
-
color: #fff;
|
|
13
|
-
min-height: 100vh;
|
|
14
|
-
display: flex;
|
|
15
|
-
align-items: center;
|
|
16
|
-
justify-content: center;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.container {
|
|
20
|
-
max-width: 800px;
|
|
21
|
-
padding: 40px;
|
|
22
|
-
text-align: center;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
h1 {
|
|
26
|
-
font-size: 3rem;
|
|
27
|
-
margin-bottom: 20px;
|
|
28
|
-
background: linear-gradient(90deg, #00d9ff, #00ff88);
|
|
29
|
-
-webkit-background-clip: text;
|
|
30
|
-
-webkit-text-fill-color: transparent;
|
|
31
|
-
background-clip: text;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
p {
|
|
35
|
-
font-size: 1.2rem;
|
|
36
|
-
color: #a0a0a0;
|
|
37
|
-
margin-bottom: 40px;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.features {
|
|
41
|
-
display: grid;
|
|
42
|
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
43
|
-
gap: 20px;
|
|
44
|
-
margin-top: 40px;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.feature {
|
|
48
|
-
background: rgba(255, 255, 255, 0.05);
|
|
49
|
-
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
50
|
-
border-radius: 12px;
|
|
51
|
-
padding: 30px;
|
|
52
|
-
transition: transform 0.2s, box-shadow 0.2s;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.feature:hover {
|
|
56
|
-
transform: translateY(-5px);
|
|
57
|
-
box-shadow: 0 10px 40px rgba(0, 217, 255, 0.2);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.feature h3 {
|
|
61
|
-
font-size: 1.2rem;
|
|
62
|
-
margin-bottom: 10px;
|
|
63
|
-
color: #00d9ff;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.feature p {
|
|
67
|
-
font-size: 0.9rem;
|
|
68
|
-
margin-bottom: 0;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.feature a {
|
|
72
|
-
color: #00ff88;
|
|
73
|
-
text-decoration: none;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.feature a:hover {
|
|
77
|
-
text-decoration: underline;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
code {
|
|
81
|
-
background: rgba(0, 217, 255, 0.1);
|
|
82
|
-
padding: 2px 8px;
|
|
83
|
-
border-radius: 4px;
|
|
84
|
-
font-family: 'Monaco', 'Menlo', monospace;
|
|
85
|
-
font-size: 0.85em;
|
|
86
|
-
color: #00d9ff;
|
|
87
|
-
}
|
package/public/index.html
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Nexus Framework</title>
|
|
7
|
-
<link rel="stylesheet" href="/css/style.css">
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div class="container">
|
|
11
|
-
<h1>🚀 Nexus Framework</h1>
|
|
12
|
-
<p>Static files are working!</p>
|
|
13
|
-
|
|
14
|
-
<div class="features">
|
|
15
|
-
<div class="feature">
|
|
16
|
-
<h3>📁 Static Files</h3>
|
|
17
|
-
<p>This page is served from <code>./public/index.html</code></p>
|
|
18
|
-
</div>
|
|
19
|
-
|
|
20
|
-
<div class="feature">
|
|
21
|
-
<h3>📚 API Docs</h3>
|
|
22
|
-
<p><a href="/docs">View Swagger Documentation</a></p>
|
|
23
|
-
</div>
|
|
24
|
-
|
|
25
|
-
<div class="feature">
|
|
26
|
-
<h3>🎮 Playground</h3>
|
|
27
|
-
<p><a href="/playground">Try the API Playground</a></p>
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
|
-
<script src="/js/app.js"></script>
|
|
33
|
-
</body>
|
|
34
|
-
</html>
|
package/public/js/app.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// Nexus Framework - Sample JavaScript
|
|
2
|
-
|
|
3
|
-
console.log('🚀 Nexus Framework - Static files loaded!');
|
|
4
|
-
|
|
5
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
6
|
-
console.log('Page ready!');
|
|
7
|
-
|
|
8
|
-
// Add some interactivity
|
|
9
|
-
const features = document.querySelectorAll('.feature');
|
|
10
|
-
features.forEach((feature, index) => {
|
|
11
|
-
feature.style.animationDelay = `${index * 0.1}s`;
|
|
12
|
-
feature.classList.add('fade-in');
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
// Add fade-in animation
|
|
17
|
-
const style = document.createElement('style');
|
|
18
|
-
style.textContent = `
|
|
19
|
-
@keyframes fadeIn {
|
|
20
|
-
from { opacity: 0; transform: translateY(20px); }
|
|
21
|
-
to { opacity: 1; transform: translateY(0); }
|
|
22
|
-
}
|
|
23
|
-
.fade-in {
|
|
24
|
-
animation: fadeIn 0.5s ease forwards;
|
|
25
|
-
}
|
|
26
|
-
`;
|
|
27
|
-
document.head.appendChild(style);
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
2
|
-
import { CacheStore, CacheEntry } from './types';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Simple in-memory cache store with TTL support
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export class InMemoryCacheStore<Value = unknown> implements CacheStore<Value> {
|
|
9
|
-
readonly name: string;
|
|
10
|
-
private store: Map<string, CacheEntry<Value>> = new Map();
|
|
11
|
-
private emitter = new EventEmitter();
|
|
12
|
-
private maxSize: number;
|
|
13
|
-
|
|
14
|
-
constructor(name: string, maxSize: number = 10000) {
|
|
15
|
-
this.name = name;
|
|
16
|
-
this.maxSize = maxSize;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async get(key: string): Promise<CacheEntry<Value> | undefined> {
|
|
20
|
-
const entry = this.store.get(key);
|
|
21
|
-
if (!entry) {
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (entry.expiresAt && entry.expiresAt < Date.now()) {
|
|
26
|
-
await this.delete(key);
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return entry;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async set(key: string, entry: CacheEntry<Value>): Promise<void> {
|
|
34
|
-
if (this.store.size >= this.maxSize) {
|
|
35
|
-
const oldestKey = this.store.keys().next().value;
|
|
36
|
-
if (oldestKey) {
|
|
37
|
-
this.store.delete(oldestKey);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
this.store.set(key, entry);
|
|
42
|
-
this.emitter.emit('set', key, entry);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async delete(key: string): Promise<void> {
|
|
46
|
-
if (this.store.has(key)) {
|
|
47
|
-
this.store.delete(key);
|
|
48
|
-
this.emitter.emit('delete', key);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async clear(): Promise<void> {
|
|
53
|
-
this.store.clear();
|
|
54
|
-
this.emitter.emit('clear');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async keys(pattern?: RegExp): Promise<string[]> {
|
|
58
|
-
const allKeys = Array.from(this.store.keys());
|
|
59
|
-
if (!pattern) {
|
|
60
|
-
return allKeys;
|
|
61
|
-
}
|
|
62
|
-
return allKeys.filter(key => pattern.test(key));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
on(event: 'set' | 'delete' | 'clear', listener: (...args: any[]) => void) {
|
|
66
|
-
this.emitter.on(event, listener);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { CacheTierConfig, TagIndexEntry, CacheSetOptions, CacheEntry, CacheWrapOptions, MemoizeOptions } from './types';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Multi-tier cache orchestrator
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export class MultiTierCache<Value = unknown> {
|
|
8
|
-
private tiers: CacheTierConfig<Value>[];
|
|
9
|
-
private tagIndex: Map<string, TagIndexEntry> = new Map();
|
|
10
|
-
private defaultTTL: number;
|
|
11
|
-
|
|
12
|
-
constructor(tiers: CacheTierConfig<Value>[], defaultTTL: number = 60000) {
|
|
13
|
-
if (tiers.length === 0) {
|
|
14
|
-
throw new Error('MultiTierCache requires at least one tier');
|
|
15
|
-
}
|
|
16
|
-
this.tiers = tiers;
|
|
17
|
-
this.defaultTTL = defaultTTL;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Retrieve a value from the tiers starting from the fastest one
|
|
22
|
-
*/
|
|
23
|
-
async get(key: string): Promise<Value | undefined> {
|
|
24
|
-
for (let i = 0; i < this.tiers.length; i++) {
|
|
25
|
-
const tier = this.tiers[i];
|
|
26
|
-
const entry = await tier.store.get(key);
|
|
27
|
-
if (entry) {
|
|
28
|
-
if (i > 0) {
|
|
29
|
-
// hydrate higher tiers for faster access next time
|
|
30
|
-
await this.promoteToHigherTiers(key, entry, i);
|
|
31
|
-
}
|
|
32
|
-
return entry.value;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Set a cache value across tiers
|
|
41
|
-
*/
|
|
42
|
-
async set(key: string, value: Value, options: CacheSetOptions<Value> = {}): Promise<void> {
|
|
43
|
-
const expiresAt = this.calculateExpiry(options.ttl);
|
|
44
|
-
const entry: CacheEntry<Value> = {
|
|
45
|
-
value,
|
|
46
|
-
expiresAt,
|
|
47
|
-
tags: options.tags,
|
|
48
|
-
meta: options.meta
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
await Promise.all(this.tiers.map(async (tier) => {
|
|
52
|
-
const ttl = tier.ttl ?? options.ttl ?? this.defaultTTL;
|
|
53
|
-
const tierEntry = {
|
|
54
|
-
...entry,
|
|
55
|
-
expiresAt: this.calculateExpiry(ttl)
|
|
56
|
-
};
|
|
57
|
-
await tier.store.set(key, tierEntry);
|
|
58
|
-
}));
|
|
59
|
-
|
|
60
|
-
if (options.tags?.length) {
|
|
61
|
-
this.indexTags(key, options.tags, expiresAt);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Delete a cache key from all tiers
|
|
67
|
-
*/
|
|
68
|
-
async delete(key: string): Promise<void> {
|
|
69
|
-
await Promise.all(this.tiers.map(tier => tier.store.delete(key)));
|
|
70
|
-
for (const entry of this.tagIndex.values()) {
|
|
71
|
-
entry.keys.delete(key);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Delete keys matching a wildcard pattern
|
|
77
|
-
*/
|
|
78
|
-
async deletePattern(pattern: string): Promise<void> {
|
|
79
|
-
const regex = new RegExp(
|
|
80
|
-
'^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$'
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
for (const tier of this.tiers) {
|
|
84
|
-
const keys = tier.store.keys ? await tier.store.keys(regex) : [];
|
|
85
|
-
await Promise.all(keys.map(key => tier.store.delete(key)));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
for (const entry of this.tagIndex.values()) {
|
|
89
|
-
for (const key of Array.from(entry.keys)) {
|
|
90
|
-
if (regex.test(key)) {
|
|
91
|
-
entry.keys.delete(key);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Invalidate cache entries by tags
|
|
99
|
-
*/
|
|
100
|
-
async invalidateTags(tags: string[]): Promise<void> {
|
|
101
|
-
const keysToDelete = new Set<string>();
|
|
102
|
-
|
|
103
|
-
for (const tag of tags) {
|
|
104
|
-
const entry = this.tagIndex.get(tag);
|
|
105
|
-
if (!entry) continue;
|
|
106
|
-
|
|
107
|
-
for (const key of entry.keys) {
|
|
108
|
-
keysToDelete.add(key);
|
|
109
|
-
}
|
|
110
|
-
this.tagIndex.delete(tag);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
await Promise.all(Array.from(keysToDelete).map(key => this.delete(key)));
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Wrap helper for cache-aside pattern
|
|
118
|
-
*/
|
|
119
|
-
async wrap(
|
|
120
|
-
key: string,
|
|
121
|
-
resolver: () => Promise<Value>,
|
|
122
|
-
options: CacheWrapOptions<Value> = {}
|
|
123
|
-
): Promise<Value> {
|
|
124
|
-
if (!options.refresh) {
|
|
125
|
-
const cached = await this.get(key);
|
|
126
|
-
if (cached !== undefined) {
|
|
127
|
-
return cached;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const value = await resolver();
|
|
132
|
-
await this.set(key, value, options);
|
|
133
|
-
return value;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Memoize async functions
|
|
138
|
-
*/
|
|
139
|
-
memoize<Func extends (...args: any[]) => Promise<Value>>(
|
|
140
|
-
fn: Func,
|
|
141
|
-
options: MemoizeOptions<Value> = {}
|
|
142
|
-
): (...args: Parameters<Func>) => Promise<Value> {
|
|
143
|
-
const resolver = options.keyResolver || ((...args: any[]) => JSON.stringify(args));
|
|
144
|
-
|
|
145
|
-
return async (...args: Parameters<Func>): Promise<Value> => {
|
|
146
|
-
const key = resolver(...args);
|
|
147
|
-
return this.wrap(key, () => fn(...args), options);
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Promote cache entry to higher tiers after a miss
|
|
153
|
-
*/
|
|
154
|
-
private async promoteToHigherTiers(
|
|
155
|
-
key: string,
|
|
156
|
-
entry: CacheEntry<Value>,
|
|
157
|
-
fromIndex: number
|
|
158
|
-
): Promise<void> {
|
|
159
|
-
const tiersToHydrate = this.tiers.slice(0, fromIndex);
|
|
160
|
-
|
|
161
|
-
await Promise.all(
|
|
162
|
-
tiersToHydrate.map(tier => tier.store.set(key, entry))
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
private calculateExpiry(ttl?: number): number | undefined {
|
|
167
|
-
if (!ttl) {
|
|
168
|
-
return undefined;
|
|
169
|
-
}
|
|
170
|
-
return Date.now() + ttl;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private indexTags(key: string, tags: string[], expiresAt?: number) {
|
|
174
|
-
for (const tag of tags) {
|
|
175
|
-
const entry = this.tagIndex.get(tag) || { keys: new Set<string>(), expiresAt };
|
|
176
|
-
entry.keys.add(key);
|
|
177
|
-
if (expiresAt) {
|
|
178
|
-
entry.expiresAt = expiresAt;
|
|
179
|
-
}
|
|
180
|
-
this.tagIndex.set(tag, entry);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Export current metrics for observability or admin UI
|
|
186
|
-
*/
|
|
187
|
-
getStats() {
|
|
188
|
-
return {
|
|
189
|
-
tiers: this.tiers.map(tier => ({ name: tier.store.name })),
|
|
190
|
-
tags: Array.from(this.tagIndex.keys()),
|
|
191
|
-
defaultTTL: this.defaultTTL
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
}
|