@kya-os/mcp-i 0.1.0 → 1.2.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 +406 -71
- package/dist/149.js +1 -0
- package/dist/189.js +1 -0
- package/dist/261.js +1 -0
- package/dist/28.js +1 -0
- package/dist/295.js +1 -0
- package/dist/460.js +1 -0
- package/dist/570.js +1 -0
- package/dist/634.js +1 -0
- package/dist/647.js +1 -0
- package/dist/67.js +1 -0
- package/dist/739.js +1 -0
- package/dist/742.js +1 -0
- package/dist/904.js +1 -0
- package/dist/938.js +1 -0
- package/dist/auth/api-key.d.ts +16 -0
- package/dist/auth/api-key.js +82 -0
- package/dist/auth/jwt.d.ts +43 -0
- package/dist/auth/jwt.js +51 -0
- package/dist/auth/oauth/factory.d.ts +12 -0
- package/dist/auth/oauth/factory.js +36 -0
- package/dist/auth/oauth/index.d.ts +5 -0
- package/dist/auth/oauth/index.js +27 -0
- package/dist/auth/oauth/providers/proxy-provider.d.ts +13 -0
- package/dist/auth/oauth/providers/proxy-provider.js +159 -0
- package/dist/auth/oauth/router.d.ts +4 -0
- package/dist/auth/oauth/router.js +294 -0
- package/dist/auth/oauth/storage/memory-storage.d.ts +12 -0
- package/dist/auth/oauth/storage/memory-storage.js +40 -0
- package/dist/auth/oauth/types.d.ts +112 -0
- package/dist/auth/oauth/types.js +2 -0
- package/dist/cache/__tests__/cloudflare-kv-nonce-cache.test.d.ts +4 -0
- package/dist/cache/__tests__/cloudflare-kv-nonce-cache.test.js +176 -0
- package/dist/cache/__tests__/concurrency.test.d.ts +5 -0
- package/dist/cache/__tests__/concurrency.test.js +300 -0
- package/dist/cache/__tests__/dynamodb-nonce-cache.test.d.ts +4 -0
- package/dist/cache/__tests__/dynamodb-nonce-cache.test.js +176 -0
- package/dist/cache/__tests__/memory-nonce-cache.test.d.ts +4 -0
- package/dist/cache/__tests__/memory-nonce-cache.test.js +132 -0
- package/dist/cache/__tests__/nonce-cache-factory-simple.test.d.ts +4 -0
- package/dist/cache/__tests__/nonce-cache-factory-simple.test.js +133 -0
- package/dist/cache/__tests__/nonce-cache-factory.test.d.ts +4 -0
- package/dist/cache/__tests__/nonce-cache-factory.test.js +252 -0
- package/dist/cache/__tests__/redis-nonce-cache.test.d.ts +4 -0
- package/dist/cache/__tests__/redis-nonce-cache.test.js +95 -0
- package/dist/cache/cloudflare-kv-nonce-cache.d.ts +14 -0
- package/dist/cache/cloudflare-kv-nonce-cache.js +93 -0
- package/dist/cache/dynamodb-nonce-cache.d.ts +15 -0
- package/dist/cache/dynamodb-nonce-cache.js +92 -0
- package/dist/cache/index.d.ts +16 -0
- package/dist/cache/index.js +32 -0
- package/dist/cache/memory-nonce-cache.d.ts +44 -0
- package/dist/cache/memory-nonce-cache.js +105 -0
- package/dist/cache/nonce-cache-factory.d.ts +20 -0
- package/dist/cache/nonce-cache-factory.js +208 -0
- package/dist/cache/redis-nonce-cache.d.ts +14 -0
- package/dist/cache/redis-nonce-cache.js +53 -0
- package/dist/compiler/compiler-context.d.ts +23 -0
- package/dist/compiler/compiler-context.js +24 -0
- package/dist/compiler/config/constants.d.ts +41 -0
- package/dist/compiler/config/constants.js +45 -0
- package/dist/compiler/config/index.d.ts +252 -0
- package/dist/compiler/config/index.js +15 -0
- package/dist/compiler/config/injection.d.ts +26 -0
- package/dist/compiler/config/injection.js +58 -0
- package/dist/compiler/config/schemas/experimental/index.d.ts +91 -0
- package/dist/compiler/config/schemas/experimental/index.js +16 -0
- package/dist/compiler/config/schemas/experimental/oauth.d.ts +74 -0
- package/dist/compiler/config/schemas/experimental/oauth.js +25 -0
- package/dist/compiler/config/schemas/index.d.ts +6 -0
- package/dist/compiler/config/schemas/index.js +17 -0
- package/dist/compiler/config/schemas/paths.d.ts +9 -0
- package/dist/compiler/config/schemas/paths.js +12 -0
- package/dist/compiler/config/schemas/transport/http.d.ts +82 -0
- package/dist/compiler/config/schemas/transport/http.js +33 -0
- package/dist/compiler/config/schemas/transport/stdio.d.ts +9 -0
- package/dist/compiler/config/schemas/transport/stdio.js +15 -0
- package/dist/compiler/config/schemas/webpack.d.ts +3 -0
- package/dist/compiler/config/schemas/webpack.js +15 -0
- package/dist/compiler/config/types.d.ts +1 -0
- package/dist/compiler/config/types.js +2 -0
- package/dist/compiler/config/utils.d.ts +20 -0
- package/dist/compiler/config/utils.js +36 -0
- package/dist/compiler/generate-env-code.d.ts +1 -0
- package/dist/compiler/generate-env-code.js +8 -0
- package/dist/compiler/generate-import-code.d.ts +1 -0
- package/dist/compiler/generate-import-code.js +24 -0
- package/dist/compiler/get-webpack-config/get-entries.d.ts +3 -0
- package/dist/compiler/get-webpack-config/get-entries.js +29 -0
- package/dist/compiler/get-webpack-config/get-externals.d.ts +7 -0
- package/dist/compiler/get-webpack-config/get-externals.js +88 -0
- package/dist/compiler/get-webpack-config/get-injected-variables.d.ts +8 -0
- package/dist/compiler/get-webpack-config/get-injected-variables.js +25 -0
- package/dist/compiler/get-webpack-config/index.d.ts +4 -0
- package/dist/compiler/get-webpack-config/index.js +101 -0
- package/dist/compiler/get-webpack-config/plugins.d.ts +8 -0
- package/dist/compiler/get-webpack-config/plugins.js +132 -0
- package/dist/compiler/get-webpack-config/resolve-tsconfig-paths.d.ts +9 -0
- package/dist/compiler/get-webpack-config/resolve-tsconfig-paths.js +40 -0
- package/dist/compiler/index.d.ts +6 -0
- package/dist/compiler/index.js +194 -0
- package/dist/compiler/on-first-build.d.ts +3 -0
- package/dist/compiler/on-first-build.js +58 -0
- package/dist/compiler/parse-xmcp-config.d.ts +9 -0
- package/dist/compiler/parse-xmcp-config.js +155 -0
- package/dist/compiler/start-http-server.d.ts +1 -0
- package/dist/compiler/start-http-server.js +34 -0
- package/dist/index.d.ts +12 -54
- package/dist/index.js +22 -190
- package/dist/index.js.LICENSE.txt +49 -0
- package/dist/runtime/__tests__/audit.test.d.ts +4 -0
- package/dist/runtime/__tests__/audit.test.js +328 -0
- package/dist/runtime/__tests__/identity.test.d.ts +4 -0
- package/dist/runtime/__tests__/identity.test.js +164 -0
- package/dist/runtime/__tests__/mcpi-runtime.test.d.ts +4 -0
- package/dist/runtime/__tests__/mcpi-runtime.test.js +372 -0
- package/dist/runtime/__tests__/proof.test.d.ts +4 -0
- package/dist/runtime/__tests__/proof.test.js +302 -0
- package/dist/runtime/__tests__/session.test.d.ts +4 -0
- package/dist/runtime/__tests__/session.test.js +254 -0
- package/dist/runtime/__tests__/well-known.test.d.ts +4 -0
- package/dist/runtime/__tests__/well-known.test.js +312 -0
- package/dist/runtime/adapter-express.js +2 -0
- package/dist/runtime/adapter-express.js.LICENSE.txt +252 -0
- package/dist/runtime/adapter-nextjs.js +2 -0
- package/dist/runtime/adapter-nextjs.js.LICENSE.txt +53 -0
- package/dist/runtime/adapters/express/index.d.ts +2 -0
- package/dist/runtime/adapters/express/index.js +48 -0
- package/dist/runtime/adapters/nextjs/index.d.ts +8 -0
- package/dist/runtime/adapters/nextjs/index.js +18 -0
- package/dist/runtime/audit.d.ts +93 -0
- package/dist/runtime/audit.js +212 -0
- package/dist/runtime/debug.d.ts +118 -0
- package/dist/runtime/debug.js +612 -0
- package/dist/runtime/delegation-hooks.d.ts +85 -0
- package/dist/runtime/delegation-hooks.js +116 -0
- package/dist/runtime/demo.d.ts +71 -0
- package/dist/runtime/demo.js +135 -0
- package/dist/runtime/headers.d.ts +1 -0
- package/dist/runtime/headers.js +9 -0
- package/dist/runtime/http.js +2 -0
- package/dist/runtime/http.js.LICENSE.txt +252 -0
- package/dist/runtime/identity.d.ts +105 -0
- package/dist/runtime/identity.js +232 -0
- package/dist/runtime/index.d.ts +16 -0
- package/dist/runtime/index.js +56 -0
- package/dist/runtime/mcpi-runtime.d.ts +164 -0
- package/dist/runtime/mcpi-runtime.js +352 -0
- package/dist/runtime/proof.d.ts +87 -0
- package/dist/runtime/proof.js +223 -0
- package/dist/runtime/session.d.ts +88 -0
- package/dist/runtime/session.js +216 -0
- package/dist/runtime/stdio.js +2 -0
- package/dist/runtime/stdio.js.LICENSE.txt +1 -0
- package/dist/runtime/templates/home.d.ts +2 -0
- package/dist/runtime/templates/home.js +50 -0
- package/dist/runtime/transports/http/base-streamable-http.d.ts +25 -0
- package/dist/runtime/transports/http/base-streamable-http.js +16 -0
- package/dist/runtime/transports/http/http-context.d.ts +9 -0
- package/dist/runtime/transports/http/http-context.js +8 -0
- package/dist/runtime/transports/http/index.d.ts +1 -0
- package/dist/runtime/transports/http/index.js +55 -0
- package/dist/runtime/transports/http/setup-cors.d.ts +4 -0
- package/dist/runtime/transports/http/setup-cors.js +24 -0
- package/dist/runtime/transports/http/stateless-streamable-http.d.ts +39 -0
- package/dist/runtime/transports/http/stateless-streamable-http.js +331 -0
- package/dist/runtime/transports/stdio/index.d.ts +1 -0
- package/dist/runtime/transports/stdio/index.js +51 -0
- package/dist/runtime/utils/server.d.ts +42 -0
- package/dist/runtime/utils/server.js +39 -0
- package/dist/runtime/utils/tools.d.ts +8 -0
- package/dist/runtime/utils/tools.js +115 -0
- package/dist/runtime/verifier-middleware.d.ts +76 -0
- package/dist/runtime/verifier-middleware.js +322 -0
- package/dist/runtime/well-known.d.ts +151 -0
- package/dist/runtime/well-known.js +258 -0
- package/dist/storage/config.d.ts +28 -0
- package/dist/storage/config.js +79 -0
- package/dist/storage/delegation.d.ts +59 -0
- package/dist/storage/delegation.js +130 -0
- package/dist/storage/merkle-verifier.d.ts +84 -0
- package/dist/storage/merkle-verifier.js +261 -0
- package/dist/test/__tests__/nonce-cache-integration.test.d.ts +1 -0
- package/dist/test/__tests__/nonce-cache-integration.test.js +116 -0
- package/dist/test/__tests__/nonce-cache.test.d.ts +1 -0
- package/dist/test/__tests__/nonce-cache.test.js +122 -0
- package/dist/test/__tests__/runtime-integration.test.d.ts +4 -0
- package/dist/test/__tests__/runtime-integration.test.js +192 -0
- package/dist/test/__tests__/test-infrastructure.test.d.ts +4 -0
- package/dist/test/__tests__/test-infrastructure.test.js +178 -0
- package/dist/test/deterministic-keys.d.ts +31 -0
- package/dist/test/deterministic-keys.js +108 -0
- package/dist/test/examples/test-usage-example.d.ts +140 -0
- package/dist/test/examples/test-usage-example.js +175 -0
- package/dist/test/index.d.ts +11 -0
- package/dist/test/index.js +27 -0
- package/dist/test/local-verification.d.ts +28 -0
- package/dist/test/local-verification.js +342 -0
- package/dist/test/mock-identity-provider.d.ts +96 -0
- package/dist/test/mock-identity-provider.js +243 -0
- package/dist/test/runtime-integration.d.ts +63 -0
- package/dist/test/runtime-integration.js +140 -0
- package/dist/test/test-environment.d.ts +26 -0
- package/dist/test/test-environment.js +50 -0
- package/dist/types/declarations.d.ts +1 -0
- package/dist/types/declarations.js +6 -0
- package/dist/types/middleware.d.ts +2 -0
- package/dist/types/middleware.js +2 -0
- package/dist/types/tool.d.ts +80 -0
- package/dist/types/tool.js +2 -0
- package/dist/utils/cli-icons.d.ts +3 -0
- package/dist/utils/cli-icons.js +7 -0
- package/dist/utils/constants.d.ts +6 -0
- package/dist/utils/constants.js +13 -0
- package/dist/utils/context.d.ts +33 -0
- package/dist/utils/context.js +58 -0
- package/dist/utils/file-watcher.d.ts +19 -0
- package/dist/utils/file-watcher.js +49 -0
- package/dist/utils/fs-utils.d.ts +2 -0
- package/dist/utils/fs-utils.js +22 -0
- package/dist/utils/path-validation.d.ts +3 -0
- package/dist/utils/path-validation.js +56 -0
- package/dist/utils/spawn-process.d.ts +9 -0
- package/dist/utils/spawn-process.js +50 -0
- package/dist/utils/subscribable.d.ts +12 -0
- package/dist/utils/subscribable.js +44 -0
- package/package.json +99 -21
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for Memory Nonce Cache
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const memory_nonce_cache_js_1 = require("../memory-nonce-cache.js");
|
|
8
|
+
(0, vitest_1.describe)("MemoryNonceCache", () => {
|
|
9
|
+
let cache;
|
|
10
|
+
(0, vitest_1.beforeEach)(() => {
|
|
11
|
+
cache = new memory_nonce_cache_js_1.MemoryNonceCache(100); // Short cleanup interval for testing
|
|
12
|
+
});
|
|
13
|
+
(0, vitest_1.afterEach)(() => {
|
|
14
|
+
cache.destroy();
|
|
15
|
+
});
|
|
16
|
+
(0, vitest_1.describe)("Basic Operations", () => {
|
|
17
|
+
(0, vitest_1.it)("should add and check nonce existence", async () => {
|
|
18
|
+
const nonce = "test-nonce-123";
|
|
19
|
+
// Initially should not exist
|
|
20
|
+
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
21
|
+
// Add nonce
|
|
22
|
+
await cache.add(nonce, 60); // 60 seconds TTL
|
|
23
|
+
// Should now exist
|
|
24
|
+
(0, vitest_1.expect)(await cache.has(nonce)).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
(0, vitest_1.it)("should prevent duplicate nonce addition", async () => {
|
|
27
|
+
const nonce = "duplicate-nonce";
|
|
28
|
+
// Add nonce first time
|
|
29
|
+
await cache.add(nonce, 60);
|
|
30
|
+
// Adding same nonce should throw
|
|
31
|
+
await (0, vitest_1.expect)(cache.add(nonce, 60)).rejects.toThrow("Nonce duplicate-nonce already exists");
|
|
32
|
+
});
|
|
33
|
+
(0, vitest_1.it)("should handle TTL expiration", async () => {
|
|
34
|
+
const nonce = "expiring-nonce";
|
|
35
|
+
// Add nonce with very short TTL
|
|
36
|
+
await cache.add(nonce, 0.1); // 0.1 seconds
|
|
37
|
+
// Should exist initially
|
|
38
|
+
(0, vitest_1.expect)(await cache.has(nonce)).toBe(true);
|
|
39
|
+
// Wait for expiration
|
|
40
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
41
|
+
// Should be expired
|
|
42
|
+
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
(0, vitest_1.describe)("Cleanup", () => {
|
|
46
|
+
(0, vitest_1.it)("should clean up expired entries", async () => {
|
|
47
|
+
const nonce1 = "nonce-1";
|
|
48
|
+
const nonce2 = "nonce-2";
|
|
49
|
+
// Add nonces with different TTLs
|
|
50
|
+
await cache.add(nonce1, 0.1); // Short TTL
|
|
51
|
+
await cache.add(nonce2, 60); // Long TTL
|
|
52
|
+
// Both should exist initially
|
|
53
|
+
(0, vitest_1.expect)(await cache.has(nonce1)).toBe(true);
|
|
54
|
+
(0, vitest_1.expect)(await cache.has(nonce2)).toBe(true);
|
|
55
|
+
// Wait for first nonce to expire
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
57
|
+
// Manual cleanup
|
|
58
|
+
await cache.cleanup();
|
|
59
|
+
// First should be gone, second should remain
|
|
60
|
+
(0, vitest_1.expect)(await cache.has(nonce1)).toBe(false);
|
|
61
|
+
(0, vitest_1.expect)(await cache.has(nonce2)).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
(0, vitest_1.it)("should provide accurate statistics", async () => {
|
|
64
|
+
const nonce1 = "stats-nonce-1";
|
|
65
|
+
const nonce2 = "stats-nonce-2";
|
|
66
|
+
// Add nonces
|
|
67
|
+
await cache.add(nonce1, 60);
|
|
68
|
+
await cache.add(nonce2, 0.1); // This will expire quickly
|
|
69
|
+
// Wait for one to expire
|
|
70
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
71
|
+
const stats = cache.getStats();
|
|
72
|
+
(0, vitest_1.expect)(stats.size).toBeGreaterThanOrEqual(1); // At least one still in cache
|
|
73
|
+
(0, vitest_1.expect)(stats.expired).toBeGreaterThanOrEqual(0); // Some may be expired
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
(0, vitest_1.describe)("Atomic Operations", () => {
|
|
77
|
+
(0, vitest_1.it)("should prevent duplicate nonce addition", async () => {
|
|
78
|
+
const nonce = "atomic-test-nonce";
|
|
79
|
+
// Add nonce first time
|
|
80
|
+
await cache.add(nonce, 60);
|
|
81
|
+
// Second attempt should fail
|
|
82
|
+
await (0, vitest_1.expect)(cache.add(nonce, 60)).rejects.toThrow("Nonce atomic-test-nonce already exists");
|
|
83
|
+
// Nonce should still exist
|
|
84
|
+
(0, vitest_1.expect)(await cache.has(nonce)).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
(0, vitest_1.describe)("Memory Management", () => {
|
|
88
|
+
(0, vitest_1.it)("should clear all entries", async () => {
|
|
89
|
+
// Add multiple nonces
|
|
90
|
+
await cache.add("nonce-1", 60);
|
|
91
|
+
await cache.add("nonce-2", 60);
|
|
92
|
+
await cache.add("nonce-3", 60);
|
|
93
|
+
// All should exist
|
|
94
|
+
(0, vitest_1.expect)(await cache.has("nonce-1")).toBe(true);
|
|
95
|
+
(0, vitest_1.expect)(await cache.has("nonce-2")).toBe(true);
|
|
96
|
+
(0, vitest_1.expect)(await cache.has("nonce-3")).toBe(true);
|
|
97
|
+
// Clear cache
|
|
98
|
+
cache.clear();
|
|
99
|
+
// None should exist
|
|
100
|
+
(0, vitest_1.expect)(await cache.has("nonce-1")).toBe(false);
|
|
101
|
+
(0, vitest_1.expect)(await cache.has("nonce-2")).toBe(false);
|
|
102
|
+
(0, vitest_1.expect)(await cache.has("nonce-3")).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
(0, vitest_1.it)("should properly destroy cache and stop cleanup", async () => {
|
|
105
|
+
const cache2 = new memory_nonce_cache_js_1.MemoryNonceCache(50);
|
|
106
|
+
await cache2.add("test-nonce", 60);
|
|
107
|
+
(0, vitest_1.expect)(await cache2.has("test-nonce")).toBe(true);
|
|
108
|
+
// Destroy should clear cache and stop intervals
|
|
109
|
+
cache2.destroy();
|
|
110
|
+
(0, vitest_1.expect)(await cache2.has("test-nonce")).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
(0, vitest_1.describe)("Edge Cases", () => {
|
|
114
|
+
(0, vitest_1.it)("should handle empty nonce strings", async () => {
|
|
115
|
+
await (0, vitest_1.expect)(cache.add("", 60)).resolves.not.toThrow();
|
|
116
|
+
(0, vitest_1.expect)(await cache.has("")).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
(0, vitest_1.it)("should handle zero TTL", async () => {
|
|
119
|
+
const nonce = "zero-ttl-nonce";
|
|
120
|
+
await cache.add(nonce, 0);
|
|
121
|
+
// Zero TTL means it should be expired immediately
|
|
122
|
+
// The implementation stores it but it should be considered expired
|
|
123
|
+
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
(0, vitest_1.it)("should handle negative TTL", async () => {
|
|
126
|
+
const nonce = "negative-ttl-nonce";
|
|
127
|
+
await cache.add(nonce, -1);
|
|
128
|
+
// Should be immediately expired
|
|
129
|
+
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Simplified tests for Nonce Cache Factory
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const nonce_cache_factory_js_1 = require("../nonce-cache-factory.js");
|
|
8
|
+
const memory_nonce_cache_js_1 = require("../memory-nonce-cache.js");
|
|
9
|
+
// Mock environment variables
|
|
10
|
+
const originalEnv = process.env;
|
|
11
|
+
(0, vitest_1.describe)("Nonce Cache Factory (Simplified)", () => {
|
|
12
|
+
(0, vitest_1.beforeEach)(() => {
|
|
13
|
+
vitest_1.vi.clearAllMocks();
|
|
14
|
+
// Reset environment
|
|
15
|
+
process.env = { ...originalEnv };
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.afterEach)(() => {
|
|
18
|
+
process.env = originalEnv;
|
|
19
|
+
});
|
|
20
|
+
(0, vitest_1.describe)("detectCacheType", () => {
|
|
21
|
+
(0, vitest_1.it)("should detect explicit cache type from environment", () => {
|
|
22
|
+
process.env.mcpi_NONCE_CACHE_TYPE = "redis";
|
|
23
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("redis");
|
|
24
|
+
process.env.mcpi_NONCE_CACHE_TYPE = "dynamodb";
|
|
25
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("dynamodb");
|
|
26
|
+
process.env.mcpi_NONCE_CACHE_TYPE = "cloudflare-kv";
|
|
27
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("cloudflare-kv");
|
|
28
|
+
process.env.mcpi_NONCE_CACHE_TYPE = "memory";
|
|
29
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("memory");
|
|
30
|
+
});
|
|
31
|
+
(0, vitest_1.it)("should ignore invalid explicit cache types", () => {
|
|
32
|
+
process.env.mcpi_NONCE_CACHE_TYPE = "invalid-type";
|
|
33
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("memory");
|
|
34
|
+
});
|
|
35
|
+
(0, vitest_1.it)("should detect Redis from environment variables", () => {
|
|
36
|
+
process.env.REDIS_URL = "redis://localhost:6379";
|
|
37
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("redis");
|
|
38
|
+
delete process.env.REDIS_URL;
|
|
39
|
+
process.env.mcpi_REDIS_URL = "redis://localhost:6379";
|
|
40
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("redis");
|
|
41
|
+
});
|
|
42
|
+
(0, vitest_1.it)("should detect DynamoDB from environment variables", () => {
|
|
43
|
+
process.env.AWS_REGION = "us-east-1";
|
|
44
|
+
process.env.mcpi_DYNAMODB_TABLE = "nonce-cache";
|
|
45
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("dynamodb");
|
|
46
|
+
delete process.env.AWS_REGION;
|
|
47
|
+
process.env.AWS_DEFAULT_REGION = "us-west-2";
|
|
48
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("dynamodb");
|
|
49
|
+
});
|
|
50
|
+
(0, vitest_1.it)("should default to memory cache", () => {
|
|
51
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("memory");
|
|
52
|
+
});
|
|
53
|
+
(0, vitest_1.it)("should warn about memory cache in production", () => {
|
|
54
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
55
|
+
process.env.NODE_ENV = "production";
|
|
56
|
+
(0, nonce_cache_factory_js_1.detectCacheType)();
|
|
57
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("Using memory cache in production"));
|
|
58
|
+
consoleSpy.mockRestore();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
(0, vitest_1.describe)("createNonceCache", () => {
|
|
62
|
+
(0, vitest_1.it)("should create memory cache by default", async () => {
|
|
63
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)();
|
|
64
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
65
|
+
// Cleanup
|
|
66
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
67
|
+
cache.destroy();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
(0, vitest_1.it)("should create memory cache when explicitly configured", async () => {
|
|
71
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({ type: "memory" });
|
|
72
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
73
|
+
// Cleanup
|
|
74
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
75
|
+
cache.destroy();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
(0, vitest_1.it)("should fall back to memory cache when Redis URL is missing", async () => {
|
|
79
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
80
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({ type: "redis" });
|
|
81
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
82
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith("Redis URL not found, falling back to memory cache");
|
|
83
|
+
consoleSpy.mockRestore();
|
|
84
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
85
|
+
cache.destroy();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
(0, vitest_1.it)("should fall back to memory cache when DynamoDB table is missing", async () => {
|
|
89
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
90
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({ type: "dynamodb" });
|
|
91
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
92
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith("DynamoDB table name not found, falling back to memory cache");
|
|
93
|
+
consoleSpy.mockRestore();
|
|
94
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
95
|
+
cache.destroy();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
(0, vitest_1.it)("should fall back to memory cache when KV namespace is missing", async () => {
|
|
99
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
100
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({ type: "cloudflare-kv" });
|
|
101
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
102
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith("Cloudflare KV namespace not found, falling back to memory cache");
|
|
103
|
+
consoleSpy.mockRestore();
|
|
104
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
105
|
+
cache.destroy();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
(0, vitest_1.describe)("createNonceCacheWithConfig", () => {
|
|
110
|
+
(0, vitest_1.it)("should create cache with default options", async () => {
|
|
111
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCacheWithConfig)();
|
|
112
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
113
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
114
|
+
cache.destroy();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
(0, vitest_1.it)("should fall back to memory cache on errors by default", async () => {
|
|
118
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
119
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCacheWithConfig)({
|
|
120
|
+
config: {
|
|
121
|
+
type: "redis",
|
|
122
|
+
// No URL provided, should cause fallback
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
126
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalled();
|
|
127
|
+
consoleSpy.mockRestore();
|
|
128
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
129
|
+
cache.destroy();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for Nonce Cache Factory
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const nonce_cache_factory_js_1 = require("../nonce-cache-factory.js");
|
|
8
|
+
const memory_nonce_cache_js_1 = require("../memory-nonce-cache.js");
|
|
9
|
+
// Mock environment variables
|
|
10
|
+
const originalEnv = process.env;
|
|
11
|
+
(0, vitest_1.describe)("Nonce Cache Factory", () => {
|
|
12
|
+
(0, vitest_1.beforeEach)(() => {
|
|
13
|
+
vitest_1.vi.clearAllMocks();
|
|
14
|
+
// Reset environment
|
|
15
|
+
process.env = { ...originalEnv };
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.afterEach)(() => {
|
|
18
|
+
process.env = originalEnv;
|
|
19
|
+
});
|
|
20
|
+
(0, vitest_1.describe)("detectCacheType", () => {
|
|
21
|
+
(0, vitest_1.it)("should detect explicit cache type from environment", () => {
|
|
22
|
+
process.env.mcpi_NONCE_CACHE_TYPE = "redis";
|
|
23
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("redis");
|
|
24
|
+
process.env.mcpi_NONCE_CACHE_TYPE = "dynamodb";
|
|
25
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("dynamodb");
|
|
26
|
+
process.env.mcpi_NONCE_CACHE_TYPE = "cloudflare-kv";
|
|
27
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("cloudflare-kv");
|
|
28
|
+
process.env.mcpi_NONCE_CACHE_TYPE = "memory";
|
|
29
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("memory");
|
|
30
|
+
});
|
|
31
|
+
(0, vitest_1.it)("should ignore invalid explicit cache types", () => {
|
|
32
|
+
process.env.mcpi_NONCE_CACHE_TYPE = "invalid-type";
|
|
33
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("memory");
|
|
34
|
+
});
|
|
35
|
+
(0, vitest_1.it)("should detect Redis from environment variables", () => {
|
|
36
|
+
process.env.REDIS_URL = "redis://localhost:6379";
|
|
37
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("redis");
|
|
38
|
+
delete process.env.REDIS_URL;
|
|
39
|
+
process.env.mcpi_REDIS_URL = "redis://localhost:6379";
|
|
40
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("redis");
|
|
41
|
+
});
|
|
42
|
+
(0, vitest_1.it)("should detect DynamoDB from environment variables", () => {
|
|
43
|
+
process.env.AWS_REGION = "us-east-1";
|
|
44
|
+
process.env.mcpi_DYNAMODB_TABLE = "nonce-cache";
|
|
45
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("dynamodb");
|
|
46
|
+
delete process.env.AWS_REGION;
|
|
47
|
+
process.env.AWS_DEFAULT_REGION = "us-west-2";
|
|
48
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("dynamodb");
|
|
49
|
+
});
|
|
50
|
+
(0, vitest_1.it)("should detect Cloudflare Workers environment", () => {
|
|
51
|
+
// Mock Cloudflare Workers globals
|
|
52
|
+
const originalGlobalThis = globalThis;
|
|
53
|
+
Object.defineProperty(globalThis, "caches", {
|
|
54
|
+
value: {},
|
|
55
|
+
configurable: true,
|
|
56
|
+
});
|
|
57
|
+
Object.defineProperty(globalThis, "fetch", {
|
|
58
|
+
value: () => { },
|
|
59
|
+
configurable: true,
|
|
60
|
+
});
|
|
61
|
+
Object.defineProperty(globalThis, "addEventListener", {
|
|
62
|
+
value: () => { },
|
|
63
|
+
configurable: true,
|
|
64
|
+
});
|
|
65
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("cloudflare-kv");
|
|
66
|
+
// Cleanup
|
|
67
|
+
delete globalThis.caches;
|
|
68
|
+
delete globalThis.addEventListener;
|
|
69
|
+
});
|
|
70
|
+
(0, vitest_1.it)("should default to memory cache", () => {
|
|
71
|
+
(0, vitest_1.expect)((0, nonce_cache_factory_js_1.detectCacheType)()).toBe("memory");
|
|
72
|
+
});
|
|
73
|
+
(0, vitest_1.it)("should warn about memory cache in production", () => {
|
|
74
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
75
|
+
process.env.NODE_ENV = "production";
|
|
76
|
+
(0, nonce_cache_factory_js_1.detectCacheType)();
|
|
77
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("Using memory cache in production"));
|
|
78
|
+
consoleSpy.mockRestore();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
(0, vitest_1.describe)("createNonceCache", () => {
|
|
82
|
+
(0, vitest_1.it)("should create memory cache by default", async () => {
|
|
83
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)();
|
|
84
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
85
|
+
// Cleanup
|
|
86
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
87
|
+
cache.destroy();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
(0, vitest_1.it)("should create memory cache when explicitly configured", async () => {
|
|
91
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({ type: "memory" });
|
|
92
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
93
|
+
// Cleanup
|
|
94
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
95
|
+
cache.destroy();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
(0, vitest_1.it)("should fall back to memory cache when Redis URL is missing", async () => {
|
|
99
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
100
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({ type: "redis" });
|
|
101
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
102
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith("Redis URL not found, falling back to memory cache");
|
|
103
|
+
consoleSpy.mockRestore();
|
|
104
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
105
|
+
cache.destroy();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
(0, vitest_1.it)("should fall back to memory cache when DynamoDB table is missing", async () => {
|
|
109
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
110
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({ type: "dynamodb" });
|
|
111
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
112
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith("DynamoDB table name not found, falling back to memory cache");
|
|
113
|
+
consoleSpy.mockRestore();
|
|
114
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
115
|
+
cache.destroy();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
(0, vitest_1.it)("should fall back to memory cache when KV namespace is missing", async () => {
|
|
119
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
120
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({ type: "cloudflare-kv" });
|
|
121
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
122
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith("Cloudflare KV namespace not found, falling back to memory cache");
|
|
123
|
+
consoleSpy.mockRestore();
|
|
124
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
125
|
+
cache.destroy();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
(0, vitest_1.it)("should handle Redis connection failures gracefully", async () => {
|
|
129
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
130
|
+
// Mock Redis import to throw error
|
|
131
|
+
vitest_1.vi.doMock("redis", () => {
|
|
132
|
+
throw new Error("Redis not available");
|
|
133
|
+
});
|
|
134
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({
|
|
135
|
+
type: "redis",
|
|
136
|
+
redis: { url: "redis://localhost:6379", keyPrefix: "test:" },
|
|
137
|
+
});
|
|
138
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
139
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("Failed to connect to Redis"), vitest_1.expect.any(Error));
|
|
140
|
+
consoleSpy.mockRestore();
|
|
141
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
142
|
+
cache.destroy();
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
(0, vitest_1.it)("should handle DynamoDB initialization failures gracefully", async () => {
|
|
146
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
147
|
+
// Mock AWS SDK import to throw error
|
|
148
|
+
vitest_1.vi.doMock("@aws-sdk/client-dynamodb", () => {
|
|
149
|
+
throw new Error("AWS SDK not available");
|
|
150
|
+
});
|
|
151
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({
|
|
152
|
+
type: "dynamodb",
|
|
153
|
+
dynamodb: {
|
|
154
|
+
tableName: "test-table",
|
|
155
|
+
keyAttribute: "nonce",
|
|
156
|
+
ttlAttribute: "ttl",
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
160
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("Failed to initialize DynamoDB"), vitest_1.expect.any(Error));
|
|
161
|
+
consoleSpy.mockRestore();
|
|
162
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
163
|
+
cache.destroy();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
(0, vitest_1.it)("should handle Cloudflare KV namespace not found", async () => {
|
|
167
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
168
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({
|
|
169
|
+
type: "cloudflare-kv",
|
|
170
|
+
cloudflareKv: {
|
|
171
|
+
namespace: "NONEXISTENT_KV",
|
|
172
|
+
keyPrefix: "test:",
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
176
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("Failed to initialize Cloudflare KV"), vitest_1.expect.any(Error));
|
|
177
|
+
consoleSpy.mockRestore();
|
|
178
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
179
|
+
cache.destroy();
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
(0, vitest_1.describe)("createNonceCacheWithConfig", () => {
|
|
184
|
+
(0, vitest_1.it)("should create cache with default options", async () => {
|
|
185
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCacheWithConfig)();
|
|
186
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
187
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
188
|
+
cache.destroy();
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
(0, vitest_1.it)("should fall back to memory cache on errors by default", async () => {
|
|
192
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
193
|
+
// Mock to throw error
|
|
194
|
+
vitest_1.vi.doMock("redis", () => {
|
|
195
|
+
throw new Error("Connection failed");
|
|
196
|
+
});
|
|
197
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCacheWithConfig)({
|
|
198
|
+
config: {
|
|
199
|
+
type: "redis",
|
|
200
|
+
redis: { url: "redis://localhost:6379", keyPrefix: "test:" },
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
204
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalled();
|
|
205
|
+
consoleSpy.mockRestore();
|
|
206
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
207
|
+
cache.destroy();
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
(0, vitest_1.it)("should throw error when fallbackToMemory is false", async () => {
|
|
211
|
+
// Mock to throw error
|
|
212
|
+
vitest_1.vi.doMock("redis", () => {
|
|
213
|
+
throw new Error("Connection failed");
|
|
214
|
+
});
|
|
215
|
+
await (0, vitest_1.expect)((0, nonce_cache_factory_js_1.createNonceCacheWithConfig)({
|
|
216
|
+
config: {
|
|
217
|
+
type: "redis",
|
|
218
|
+
redis: { url: "redis://localhost:6379", keyPrefix: "test:" },
|
|
219
|
+
},
|
|
220
|
+
fallbackToMemory: false,
|
|
221
|
+
})).rejects.toThrow();
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
(0, vitest_1.describe)("Environment Variable Priority", () => {
|
|
225
|
+
(0, vitest_1.it)("should prioritize config over environment variables", async () => {
|
|
226
|
+
process.env.REDIS_URL = "redis://env-redis:6379";
|
|
227
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
228
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({
|
|
229
|
+
type: "redis",
|
|
230
|
+
redis: { url: "redis://config-redis:6379", keyPrefix: "test:" },
|
|
231
|
+
});
|
|
232
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
233
|
+
// Should have tried to use config URL, not env URL
|
|
234
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("Failed to connect to Redis"), vitest_1.expect.any(Error));
|
|
235
|
+
consoleSpy.mockRestore();
|
|
236
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
237
|
+
cache.destroy();
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
(0, vitest_1.it)("should use environment variables when config is not provided", async () => {
|
|
241
|
+
process.env.mcpi_REDIS_URL = "redis://env-redis:6379";
|
|
242
|
+
const consoleSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
243
|
+
const cache = await (0, nonce_cache_factory_js_1.createNonceCache)({ type: "redis" });
|
|
244
|
+
(0, vitest_1.expect)(cache).toBeInstanceOf(memory_nonce_cache_js_1.MemoryNonceCache);
|
|
245
|
+
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("Failed to connect to Redis"), vitest_1.expect.any(Error));
|
|
246
|
+
consoleSpy.mockRestore();
|
|
247
|
+
if (cache instanceof memory_nonce_cache_js_1.MemoryNonceCache) {
|
|
248
|
+
cache.destroy();
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for Redis Nonce Cache
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const redis_nonce_cache_js_1 = require("../redis-nonce-cache.js");
|
|
8
|
+
// Mock Redis client
|
|
9
|
+
const mockRedis = {
|
|
10
|
+
exists: vitest_1.vi.fn(),
|
|
11
|
+
set: vitest_1.vi.fn(),
|
|
12
|
+
};
|
|
13
|
+
(0, vitest_1.describe)("RedisNonceCache", () => {
|
|
14
|
+
let cache;
|
|
15
|
+
(0, vitest_1.beforeEach)(() => {
|
|
16
|
+
vitest_1.vi.clearAllMocks();
|
|
17
|
+
cache = new redis_nonce_cache_js_1.RedisNonceCache(mockRedis, "test:nonce:");
|
|
18
|
+
});
|
|
19
|
+
(0, vitest_1.describe)("Basic Operations", () => {
|
|
20
|
+
(0, vitest_1.it)("should add and check nonce existence", async () => {
|
|
21
|
+
const nonce = "test-nonce-123";
|
|
22
|
+
// Mock Redis responses
|
|
23
|
+
mockRedis.exists.mockResolvedValue(0); // doesn't exist
|
|
24
|
+
mockRedis.set.mockResolvedValue("OK"); // successful set
|
|
25
|
+
// Initially should not exist
|
|
26
|
+
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
27
|
+
(0, vitest_1.expect)(mockRedis.exists).toHaveBeenCalledWith("test:nonce:test-nonce-123");
|
|
28
|
+
// Add nonce
|
|
29
|
+
await cache.add(nonce, 60);
|
|
30
|
+
(0, vitest_1.expect)(mockRedis.set).toHaveBeenCalledWith("test:nonce:test-nonce-123", "1", "EX", 60, "NX");
|
|
31
|
+
// Mock that it now exists
|
|
32
|
+
mockRedis.exists.mockResolvedValue(1);
|
|
33
|
+
(0, vitest_1.expect)(await cache.has(nonce)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
(0, vitest_1.it)("should prevent duplicate nonce addition", async () => {
|
|
36
|
+
const nonce = "duplicate-nonce";
|
|
37
|
+
// Mock Redis SET NX returning null (key already exists)
|
|
38
|
+
mockRedis.set.mockResolvedValue(null);
|
|
39
|
+
// Adding duplicate nonce should throw
|
|
40
|
+
await (0, vitest_1.expect)(cache.add(nonce, 60)).rejects.toThrow("Nonce duplicate-nonce already exists - potential replay attack");
|
|
41
|
+
});
|
|
42
|
+
(0, vitest_1.it)("should handle Redis connection errors gracefully", async () => {
|
|
43
|
+
const nonce = "error-nonce";
|
|
44
|
+
// Mock Redis throwing connection error
|
|
45
|
+
mockRedis.exists.mockRejectedValue(new Error("Connection failed"));
|
|
46
|
+
mockRedis.set.mockRejectedValue(new Error("Connection failed"));
|
|
47
|
+
// Should propagate errors with context
|
|
48
|
+
await (0, vitest_1.expect)(cache.has(nonce)).rejects.toThrow("Failed to check nonce existence");
|
|
49
|
+
await (0, vitest_1.expect)(cache.add(nonce, 60)).rejects.toThrow("Failed to add nonce to cache");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
(0, vitest_1.describe)("Atomic Operations", () => {
|
|
53
|
+
(0, vitest_1.it)("should use atomic SET NX EX command", async () => {
|
|
54
|
+
const nonce = "atomic-test-nonce";
|
|
55
|
+
const ttl = 300;
|
|
56
|
+
mockRedis.set.mockResolvedValue("OK");
|
|
57
|
+
await cache.add(nonce, ttl);
|
|
58
|
+
// Verify atomic command was used
|
|
59
|
+
(0, vitest_1.expect)(mockRedis.set).toHaveBeenCalledWith("test:nonce:atomic-test-nonce", "1", "EX", ttl, "NX");
|
|
60
|
+
});
|
|
61
|
+
(0, vitest_1.it)("should handle concurrent add attempts", async () => {
|
|
62
|
+
const nonce = "concurrent-nonce";
|
|
63
|
+
// First call succeeds
|
|
64
|
+
mockRedis.set.mockResolvedValueOnce("OK");
|
|
65
|
+
// Second call fails (key exists)
|
|
66
|
+
mockRedis.set.mockResolvedValueOnce(null);
|
|
67
|
+
// First add should succeed
|
|
68
|
+
await (0, vitest_1.expect)(cache.add(nonce, 60)).resolves.not.toThrow();
|
|
69
|
+
// Second add should fail
|
|
70
|
+
await (0, vitest_1.expect)(cache.add(nonce, 60)).rejects.toThrow("potential replay attack");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
(0, vitest_1.describe)("Cleanup", () => {
|
|
74
|
+
(0, vitest_1.it)("should be a no-op since Redis handles expiry", async () => {
|
|
75
|
+
// cleanup() should not call any Redis methods
|
|
76
|
+
await cache.cleanup();
|
|
77
|
+
(0, vitest_1.expect)(mockRedis.exists).not.toHaveBeenCalled();
|
|
78
|
+
(0, vitest_1.expect)(mockRedis.set).not.toHaveBeenCalled();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
(0, vitest_1.describe)("Key Prefix", () => {
|
|
82
|
+
(0, vitest_1.it)("should use custom key prefix", () => {
|
|
83
|
+
const customCache = new redis_nonce_cache_js_1.RedisNonceCache(mockRedis, "custom:prefix:");
|
|
84
|
+
mockRedis.exists.mockResolvedValue(0);
|
|
85
|
+
customCache.has("test-nonce");
|
|
86
|
+
(0, vitest_1.expect)(mockRedis.exists).toHaveBeenCalledWith("custom:prefix:test-nonce");
|
|
87
|
+
});
|
|
88
|
+
(0, vitest_1.it)("should use default key prefix", () => {
|
|
89
|
+
const defaultCache = new redis_nonce_cache_js_1.RedisNonceCache(mockRedis);
|
|
90
|
+
mockRedis.exists.mockResolvedValue(0);
|
|
91
|
+
defaultCache.has("test-nonce");
|
|
92
|
+
(0, vitest_1.expect)(mockRedis.exists).toHaveBeenCalledWith("mcpi:nonce:test-nonce");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NonceCache } from "@kya-os/contracts/handshake";
|
|
2
|
+
/**
|
|
3
|
+
* Cloudflare KV-based nonce cache implementation
|
|
4
|
+
* Suitable for Cloudflare Workers deployments
|
|
5
|
+
*/
|
|
6
|
+
export declare class CloudflareKVNonceCache implements NonceCache {
|
|
7
|
+
private kv;
|
|
8
|
+
private keyPrefix;
|
|
9
|
+
constructor(kv: any, keyPrefix?: string);
|
|
10
|
+
private getKey;
|
|
11
|
+
has(nonce: string): Promise<boolean>;
|
|
12
|
+
add(nonce: string, ttl: number): Promise<void>;
|
|
13
|
+
cleanup(): Promise<void>;
|
|
14
|
+
}
|