veryfront 0.0.82 → 0.0.84

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.
Files changed (126) hide show
  1. package/README.md +18 -17
  2. package/esm/deno.js +1 -1
  3. package/esm/proxy/cache/index.d.ts +41 -0
  4. package/esm/proxy/cache/index.d.ts.map +1 -0
  5. package/esm/proxy/cache/index.js +75 -0
  6. package/esm/proxy/cache/memory-cache.d.ts +18 -0
  7. package/esm/proxy/cache/memory-cache.d.ts.map +1 -0
  8. package/esm/proxy/cache/memory-cache.js +100 -0
  9. package/esm/proxy/cache/redis-cache.d.ts +27 -0
  10. package/esm/proxy/cache/redis-cache.d.ts.map +1 -0
  11. package/esm/proxy/cache/redis-cache.js +183 -0
  12. package/esm/proxy/cache/resilient-cache.d.ts +44 -0
  13. package/esm/proxy/cache/resilient-cache.d.ts.map +1 -0
  14. package/esm/proxy/cache/resilient-cache.js +178 -0
  15. package/esm/proxy/cache/types.d.ts +65 -0
  16. package/esm/proxy/cache/types.d.ts.map +1 -0
  17. package/esm/proxy/cache/types.js +7 -0
  18. package/esm/proxy/handler.d.ts +81 -0
  19. package/esm/proxy/handler.d.ts.map +1 -0
  20. package/esm/proxy/handler.js +417 -0
  21. package/esm/proxy/logger.d.ts +29 -0
  22. package/esm/proxy/logger.d.ts.map +1 -0
  23. package/esm/proxy/logger.js +258 -0
  24. package/esm/proxy/oauth-client.d.ts +15 -0
  25. package/esm/proxy/oauth-client.d.ts.map +1 -0
  26. package/esm/proxy/oauth-client.js +52 -0
  27. package/esm/proxy/token-manager.d.ts +59 -0
  28. package/esm/proxy/token-manager.d.ts.map +1 -0
  29. package/esm/proxy/token-manager.js +125 -0
  30. package/esm/proxy/tracing.d.ts +39 -0
  31. package/esm/proxy/tracing.d.ts.map +1 -0
  32. package/esm/proxy/tracing.js +194 -0
  33. package/esm/src/cache/backend.d.ts +2 -0
  34. package/esm/src/cache/backend.d.ts.map +1 -1
  35. package/esm/src/cache/backend.js +2 -0
  36. package/esm/src/cache/cache-key-builder.d.ts +0 -4
  37. package/esm/src/cache/cache-key-builder.d.ts.map +1 -1
  38. package/esm/src/cache/cache-key-builder.js +0 -6
  39. package/esm/src/cache/multi-tier.d.ts +0 -29
  40. package/esm/src/cache/multi-tier.d.ts.map +1 -1
  41. package/esm/src/cache/multi-tier.js +0 -26
  42. package/esm/src/cli/app/actions.d.ts +26 -0
  43. package/esm/src/cli/app/actions.d.ts.map +1 -0
  44. package/esm/src/cli/app/actions.js +152 -0
  45. package/esm/src/cli/app/components/inline-input.d.ts +35 -0
  46. package/esm/src/cli/app/components/inline-input.d.ts.map +1 -0
  47. package/esm/src/cli/app/components/inline-input.js +220 -0
  48. package/esm/src/cli/app/components/list-select.d.ts +69 -0
  49. package/esm/src/cli/app/components/list-select.d.ts.map +1 -0
  50. package/esm/src/cli/app/components/list-select.js +137 -0
  51. package/esm/src/cli/app/index.d.ts +45 -0
  52. package/esm/src/cli/app/index.d.ts.map +1 -0
  53. package/esm/src/cli/app/index.js +1252 -0
  54. package/esm/src/cli/app/state.d.ts +122 -0
  55. package/esm/src/cli/app/state.d.ts.map +1 -0
  56. package/esm/src/cli/app/state.js +232 -0
  57. package/esm/src/cli/app/views/dashboard.d.ts +19 -0
  58. package/esm/src/cli/app/views/dashboard.d.ts.map +1 -0
  59. package/esm/src/cli/app/views/dashboard.js +178 -0
  60. package/esm/src/cli/commands/dev.js +2 -2
  61. package/esm/src/cli/commands/new.js +1 -1
  62. package/esm/src/cli/index/command-router.d.ts.map +1 -1
  63. package/esm/src/cli/index/command-router.js +9 -39
  64. package/esm/src/cli/index/start-handler.d.ts +3 -0
  65. package/esm/src/cli/index/start-handler.d.ts.map +1 -0
  66. package/esm/src/cli/index/start-handler.js +145 -0
  67. package/esm/src/cli/mcp/index.d.ts +11 -0
  68. package/esm/src/cli/mcp/index.d.ts.map +1 -0
  69. package/esm/src/cli/mcp/index.js +10 -0
  70. package/esm/src/cli/ui/tui.js +1 -1
  71. package/esm/src/middleware/builtin/security/redis-rate-limit.d.ts +2 -0
  72. package/esm/src/middleware/builtin/security/redis-rate-limit.d.ts.map +1 -1
  73. package/esm/src/middleware/builtin/security/redis-rate-limit.js +23 -9
  74. package/esm/src/modules/react-loader/ssr-module-loader/cache/redis.d.ts +10 -0
  75. package/esm/src/modules/react-loader/ssr-module-loader/cache/redis.d.ts.map +1 -1
  76. package/esm/src/modules/react-loader/ssr-module-loader/cache/redis.js +30 -42
  77. package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts.map +1 -1
  78. package/esm/src/modules/react-loader/ssr-module-loader/loader.js +34 -13
  79. package/esm/src/platform/adapters/fs/cache/file-cache.d.ts.map +1 -1
  80. package/esm/src/platform/adapters/fs/cache/file-cache.js +9 -3
  81. package/esm/src/server/context/cache-invalidation.d.ts.map +1 -1
  82. package/esm/src/server/context/cache-invalidation.js +4 -0
  83. package/esm/src/server/handlers/dev/dashboard/api.js +4 -0
  84. package/esm/src/server/handlers/dev/projects/ui-handler.d.ts.map +1 -1
  85. package/esm/src/server/handlers/dev/projects/ui-handler.js +6 -0
  86. package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
  87. package/esm/src/transforms/esm/http-cache.js +139 -64
  88. package/esm/src/utils/index.d.ts +1 -1
  89. package/esm/src/utils/index.d.ts.map +1 -1
  90. package/esm/src/utils/index.js +1 -1
  91. package/package.json +2 -1
  92. package/src/deno.js +1 -1
  93. package/src/proxy/cache/index.ts +93 -0
  94. package/src/proxy/cache/memory-cache.ts +120 -0
  95. package/src/proxy/cache/redis-cache.ts +203 -0
  96. package/src/proxy/cache/resilient-cache.ts +205 -0
  97. package/src/proxy/cache/types.ts +72 -0
  98. package/src/proxy/handler.ts +593 -0
  99. package/src/proxy/logger.ts +329 -0
  100. package/src/proxy/oauth-client.ts +91 -0
  101. package/src/proxy/token-manager.ts +174 -0
  102. package/src/proxy/tracing.ts +237 -0
  103. package/src/src/cache/backend.ts +3 -0
  104. package/src/src/cache/cache-key-builder.ts +0 -9
  105. package/src/src/cache/multi-tier.ts +0 -41
  106. package/src/src/cli/app/actions.ts +190 -0
  107. package/src/src/cli/app/components/inline-input.ts +255 -0
  108. package/src/src/cli/app/components/list-select.ts +215 -0
  109. package/src/src/cli/app/index.ts +1471 -0
  110. package/src/src/cli/app/state.ts +385 -0
  111. package/src/src/cli/app/views/dashboard.ts +212 -0
  112. package/src/src/cli/commands/dev.ts +2 -2
  113. package/src/src/cli/commands/new.ts +1 -1
  114. package/src/src/cli/index/command-router.ts +9 -40
  115. package/src/src/cli/index/start-handler.ts +195 -0
  116. package/src/src/cli/mcp/index.ts +11 -0
  117. package/src/src/cli/ui/tui.ts +1 -1
  118. package/src/src/middleware/builtin/security/redis-rate-limit.ts +24 -11
  119. package/src/src/modules/react-loader/ssr-module-loader/cache/redis.ts +36 -50
  120. package/src/src/modules/react-loader/ssr-module-loader/loader.ts +38 -14
  121. package/src/src/platform/adapters/fs/cache/file-cache.ts +9 -3
  122. package/src/src/server/context/cache-invalidation.ts +4 -0
  123. package/src/src/server/handlers/dev/dashboard/api.ts +2 -0
  124. package/src/src/server/handlers/dev/projects/ui-handler.ts +6 -0
  125. package/src/src/transforms/esm/http-cache.ts +148 -73
  126. package/src/src/utils/index.ts +0 -1
package/README.md CHANGED
@@ -1,25 +1,26 @@
1
- # Veryfront
1
+ # Veryfront Code
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/veryfront.svg)](https://www.npmjs.com/package/veryfront)
4
- [![CI/CD](https://github.com/veryfront/veryfront-renderer/actions/workflows/ci.yml/badge.svg)](https://github.com/veryfront/veryfront-renderer/actions/workflows/ci.yml)
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
-
7
- The all-in-one React framework for building AI-powered applications and agents.
8
-
9
- ## Features
10
-
11
- - **Zero config** — Auto-discovery from file structure
12
- - **Multi-runtime** — Deno, Node.js, Bun, Cloudflare Workers
13
- - **Full-stack React** — SSR, SSG, ISR, streaming
14
- - **MCP built-in** — Model Context Protocol server
15
- - **Production-ready** — Rate limiting, caching, observability
16
-
17
- ## Quick Start
3
+ The simplest way to build AI-powered apps.
18
4
 
19
5
  ```bash
20
- npx veryfront demo
6
+ npx veryfront
21
7
  ```
22
8
 
9
+ ```
10
+ ○ ○ ○ ○ ○ ○ ○
11
+ ○ ● ● ● ○ ○ ○ Veryfront Code is running
12
+ ○ ● ● ● ○ ○ ○
13
+ ○ ● ● ○ ● ● ○ Url http://veryfront.me:3000
14
+ ○ ○ ○ ● ● ● ○ Mcp http://veryfront.me:3002/mcp
15
+ ○ ○ ○ ● ● ● ○
16
+ ○ ○ ○ ○ ○ ○ ○
17
+
18
+ ✓ Server ready
19
+ ✓ MCP ready
20
+ ```
21
+
22
+ One command. Zero config. Just build.
23
+
23
24
  ## Project Structure
24
25
 
25
26
  ```
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.0.82",
3
+ "version": "0.0.84",
4
4
  "nodeModulesDir": "auto",
5
5
  "exclude": [
6
6
  "npm/",
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Token Cache Module
3
+ *
4
+ * Provides configurable caching for OAuth tokens.
5
+ * Supports in-memory (single instance) and Redis (distributed) backends.
6
+ * Redis cache includes automatic fallback to memory when Redis is unavailable.
7
+ */
8
+ export type { CacheOptions, CacheStats, MemoryCacheOptions, RedisCacheOptions, TokenCache, TokenCacheEntry, } from "./types.js";
9
+ export { MemoryCache } from "./memory-cache.js";
10
+ export { RedisCache } from "./redis-cache.js";
11
+ export { ResilientCache } from "./resilient-cache.js";
12
+ import type { CacheOptions, TokenCache } from "./types.js";
13
+ /**
14
+ * Create a token cache based on configuration.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // In-memory cache (default)
19
+ * const cache = createCache({ type: "memory" });
20
+ *
21
+ * // Redis cache for distributed deployments
22
+ * const cache = createCache({
23
+ * type: "redis",
24
+ * options: { url: "redis://localhost:6379" }
25
+ * });
26
+ * ```
27
+ */
28
+ export declare function createCache(options: CacheOptions): Promise<TokenCache>;
29
+ /**
30
+ * Create cache from environment variables.
31
+ *
32
+ * Environment variables:
33
+ * - CACHE_TYPE: "memory" or "redis" (default: "memory")
34
+ * - REDIS_URL: Redis connection URL (required if CACHE_TYPE=redis)
35
+ * - REDIS_PREFIX: Key prefix (default: "vf:token:")
36
+ *
37
+ * When CACHE_TYPE=redis, automatically wraps with ResilientCache for
38
+ * graceful fallback to memory when Redis is unavailable.
39
+ */
40
+ export declare function createCacheFromEnv(): Promise<TokenCache>;
41
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/proxy/cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,YAAY,EACV,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAU3D;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAU5E;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,UAAU,CAAC,CAwB9D"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Token Cache Module
3
+ *
4
+ * Provides configurable caching for OAuth tokens.
5
+ * Supports in-memory (single instance) and Redis (distributed) backends.
6
+ * Redis cache includes automatic fallback to memory when Redis is unavailable.
7
+ */
8
+ export { MemoryCache } from "./memory-cache.js";
9
+ export { RedisCache } from "./redis-cache.js";
10
+ export { ResilientCache } from "./resilient-cache.js";
11
+ import { MemoryCache } from "./memory-cache.js";
12
+ import { RedisCache } from "./redis-cache.js";
13
+ import { ResilientCache } from "./resilient-cache.js";
14
+ import { getEnv } from "../../src/platform/compat/process.js";
15
+ import { proxyLogger } from "../logger.js";
16
+ import { withSpan } from "../tracing.js";
17
+ const logger = proxyLogger.child({ module: "cache" });
18
+ /**
19
+ * Create a token cache based on configuration.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * // In-memory cache (default)
24
+ * const cache = createCache({ type: "memory" });
25
+ *
26
+ * // Redis cache for distributed deployments
27
+ * const cache = createCache({
28
+ * type: "redis",
29
+ * options: { url: "redis://localhost:6379" }
30
+ * });
31
+ * ```
32
+ */
33
+ export async function createCache(options) {
34
+ return withSpan("cache.create", async () => {
35
+ switch (options.type) {
36
+ case "redis":
37
+ return new RedisCache(options.options);
38
+ case "memory":
39
+ default:
40
+ return new MemoryCache(options.options);
41
+ }
42
+ }, { "cache.type": options.type });
43
+ }
44
+ /**
45
+ * Create cache from environment variables.
46
+ *
47
+ * Environment variables:
48
+ * - CACHE_TYPE: "memory" or "redis" (default: "memory")
49
+ * - REDIS_URL: Redis connection URL (required if CACHE_TYPE=redis)
50
+ * - REDIS_PREFIX: Key prefix (default: "vf:token:")
51
+ *
52
+ * When CACHE_TYPE=redis, automatically wraps with ResilientCache for
53
+ * graceful fallback to memory when Redis is unavailable.
54
+ */
55
+ export async function createCacheFromEnv() {
56
+ return withSpan("cache.createFromEnv", async () => {
57
+ const cacheType = getEnv("CACHE_TYPE") || "memory";
58
+ if (cacheType === "redis") {
59
+ const url = getEnv("REDIS_URL");
60
+ if (!url) {
61
+ logger.warn("[Cache] CACHE_TYPE=redis but REDIS_URL not set, falling back to memory");
62
+ return new MemoryCache();
63
+ }
64
+ const redisCache = new RedisCache({
65
+ url,
66
+ prefix: getEnv("REDIS_PREFIX") || "vf:token:",
67
+ });
68
+ // Wrap Redis with resilient fallback to memory cache
69
+ // This ensures the proxy continues to function when Redis is unavailable
70
+ logger.info("[Cache] Using Redis with memory fallback (ResilientCache)");
71
+ return new ResilientCache(redisCache, new MemoryCache());
72
+ }
73
+ return new MemoryCache();
74
+ }, { "cache.type": getEnv("CACHE_TYPE") || "memory" });
75
+ }
@@ -0,0 +1,18 @@
1
+ import type { CacheStats, MemoryCacheOptions, TokenCache, TokenCacheEntry } from "./types.js";
2
+ export declare class MemoryCache implements TokenCache {
3
+ private cache;
4
+ private hits;
5
+ private misses;
6
+ private maxSize;
7
+ private cleanupTimer;
8
+ constructor(options?: MemoryCacheOptions);
9
+ get(key: string): Promise<TokenCacheEntry | null>;
10
+ set(key: string, entry: TokenCacheEntry): Promise<void>;
11
+ delete(key: string): Promise<void>;
12
+ clear(): Promise<void>;
13
+ has(key: string): Promise<boolean>;
14
+ stats(): Promise<CacheStats>;
15
+ close(): Promise<void>;
16
+ private cleanup;
17
+ }
18
+ //# sourceMappingURL=memory-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-cache.d.ts","sourceRoot":"","sources":["../../../src/proxy/cache/memory-cache.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAM9F,qBAAa,WAAY,YAAW,UAAU;IAC5C,OAAO,CAAC,KAAK,CAAsC;IACnD,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAuD;gBAE/D,OAAO,GAAE,kBAAuB;IAM5C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAoBjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAclC,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAS5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAUtB,OAAO,CAAC,OAAO;CAehB"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * In-Memory Token Cache - single-instance deployments.
3
+ */
4
+ import * as dntShim from "../../_dnt.shims.js";
5
+ import { withSpan } from "../tracing.js";
6
+ const DEFAULT_MAX_SIZE = 1000;
7
+ const DEFAULT_CLEANUP_INTERVAL = 60_000;
8
+ export class MemoryCache {
9
+ cache = new Map();
10
+ hits = 0;
11
+ misses = 0;
12
+ maxSize;
13
+ cleanupTimer = null;
14
+ constructor(options = {}) {
15
+ this.maxSize = options.maxSize ?? DEFAULT_MAX_SIZE;
16
+ const interval = options.cleanupInterval ?? DEFAULT_CLEANUP_INTERVAL;
17
+ this.cleanupTimer = dntShim.setInterval(() => this.cleanup(), interval);
18
+ }
19
+ get(key) {
20
+ return withSpan("cache.memory.get", async () => {
21
+ const entry = this.cache.get(key);
22
+ if (!entry) {
23
+ this.misses++;
24
+ return null;
25
+ }
26
+ if (Date.now() >= entry.expiresAt) {
27
+ this.cache.delete(key);
28
+ this.misses++;
29
+ return null;
30
+ }
31
+ this.hits++;
32
+ return entry;
33
+ }, { "cache.key": key });
34
+ }
35
+ set(key, entry) {
36
+ return withSpan("cache.memory.set", async () => {
37
+ if (this.cache.size >= this.maxSize) {
38
+ const firstKey = this.cache.keys().next().value;
39
+ if (firstKey) {
40
+ this.cache.delete(firstKey);
41
+ }
42
+ }
43
+ this.cache.set(key, entry);
44
+ }, { "cache.key": key });
45
+ }
46
+ delete(key) {
47
+ return withSpan("cache.memory.delete", async () => {
48
+ this.cache.delete(key);
49
+ }, { "cache.key": key });
50
+ }
51
+ clear() {
52
+ return withSpan("cache.memory.clear", async () => {
53
+ this.cache.clear();
54
+ this.hits = 0;
55
+ this.misses = 0;
56
+ });
57
+ }
58
+ has(key) {
59
+ return withSpan("cache.memory.has", async () => {
60
+ const entry = this.cache.get(key);
61
+ if (!entry)
62
+ return false;
63
+ if (Date.now() >= entry.expiresAt) {
64
+ this.cache.delete(key);
65
+ return false;
66
+ }
67
+ return true;
68
+ }, { "cache.key": key });
69
+ }
70
+ stats() {
71
+ return withSpan("cache.memory.stats", async () => ({
72
+ hits: this.hits,
73
+ misses: this.misses,
74
+ size: this.cache.size,
75
+ type: "memory",
76
+ }));
77
+ }
78
+ close() {
79
+ return withSpan("cache.memory.close", async () => {
80
+ if (this.cleanupTimer) {
81
+ clearInterval(this.cleanupTimer);
82
+ this.cleanupTimer = null;
83
+ }
84
+ this.cache.clear();
85
+ });
86
+ }
87
+ cleanup() {
88
+ const now = Date.now();
89
+ let cleaned = 0;
90
+ for (const [key, entry] of this.cache.entries()) {
91
+ if (now >= entry.expiresAt) {
92
+ this.cache.delete(key);
93
+ cleaned++;
94
+ }
95
+ }
96
+ if (cleaned > 0) {
97
+ console.log(`[MemoryCache] Cleaned ${cleaned} expired entries`);
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Redis Token Cache
3
+ *
4
+ * Uses the standard `redis` package for cross-runtime compatibility.
5
+ * Works in Deno, Node.js, and Bun.
6
+ */
7
+ import type { CacheStats, RedisCacheOptions, TokenCache, TokenCacheEntry } from "./types.js";
8
+ export declare class RedisCache implements TokenCache {
9
+ private client;
10
+ private prefix;
11
+ private url;
12
+ private connectTimeout;
13
+ private hits;
14
+ private misses;
15
+ private connected;
16
+ constructor(options: RedisCacheOptions);
17
+ private key;
18
+ get(key: string): Promise<TokenCacheEntry | null>;
19
+ set(key: string, entry: TokenCacheEntry): Promise<void>;
20
+ delete(key: string): Promise<void>;
21
+ clear(): Promise<void>;
22
+ has(key: string): Promise<boolean>;
23
+ stats(): Promise<CacheStats>;
24
+ close(): Promise<void>;
25
+ private ensureConnected;
26
+ }
27
+ //# sourceMappingURL=redis-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-cache.d.ts","sourceRoot":"","sources":["../../../src/proxy/cache/redis-cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAO7F,qBAAa,UAAW,YAAW,UAAU;IAC3C,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,SAAS,CAAS;gBAEd,OAAO,EAAE,iBAAiB;IAMtC,OAAO,CAAC,GAAG;IAIL,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IA8BjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAevD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAalC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmCtB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAclC,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAe5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAcd,eAAe;CAgC9B"}
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Redis Token Cache
3
+ *
4
+ * Uses the standard `redis` package for cross-runtime compatibility.
5
+ * Works in Deno, Node.js, and Bun.
6
+ */
7
+ import { createClient } from "redis";
8
+ import { withSpan } from "../tracing.js";
9
+ const DEFAULT_PREFIX = "vf:token:";
10
+ const DEFAULT_CONNECT_TIMEOUT = 5000;
11
+ const DEFAULT_SCAN_COUNT = 100;
12
+ export class RedisCache {
13
+ client = null;
14
+ prefix;
15
+ url;
16
+ connectTimeout;
17
+ hits = 0;
18
+ misses = 0;
19
+ connected = false;
20
+ constructor(options) {
21
+ this.url = options.url;
22
+ this.prefix = options.prefix ?? DEFAULT_PREFIX;
23
+ this.connectTimeout = options.connectTimeout ?? DEFAULT_CONNECT_TIMEOUT;
24
+ }
25
+ key(k) {
26
+ return `${this.prefix}${k}`;
27
+ }
28
+ async get(key) {
29
+ return withSpan("cache.redis.get", async () => {
30
+ try {
31
+ await this.ensureConnected();
32
+ const data = await this.client.get(this.key(key));
33
+ if (!data) {
34
+ this.misses++;
35
+ return null;
36
+ }
37
+ const entry = JSON.parse(data);
38
+ if (Date.now() >= entry.expiresAt) {
39
+ await this.client.del(this.key(key));
40
+ this.misses++;
41
+ return null;
42
+ }
43
+ this.hits++;
44
+ return entry;
45
+ }
46
+ catch (error) {
47
+ console.error("[RedisCache] Get error:", error);
48
+ this.connected = false;
49
+ this.misses++;
50
+ throw error;
51
+ }
52
+ }, { "cache.key": key });
53
+ }
54
+ async set(key, entry) {
55
+ return withSpan("cache.redis.set", async () => {
56
+ try {
57
+ await this.ensureConnected();
58
+ const ttlMs = entry.expiresAt - Date.now();
59
+ const ttlSeconds = Math.max(1, Math.floor(ttlMs / 1000));
60
+ await this.client.setEx(this.key(key), ttlSeconds, JSON.stringify(entry));
61
+ }
62
+ catch (error) {
63
+ console.error("[RedisCache] Set error:", error);
64
+ this.connected = false;
65
+ throw error;
66
+ }
67
+ }, { "cache.key": key });
68
+ }
69
+ async delete(key) {
70
+ return withSpan("cache.redis.delete", async () => {
71
+ try {
72
+ await this.ensureConnected();
73
+ await this.client.del(this.key(key));
74
+ }
75
+ catch (error) {
76
+ console.error("[RedisCache] Delete error:", error);
77
+ this.connected = false;
78
+ throw error;
79
+ }
80
+ }, { "cache.key": key });
81
+ }
82
+ async clear() {
83
+ return withSpan("cache.redis.clear", async () => {
84
+ try {
85
+ await this.ensureConnected();
86
+ const pattern = `${this.prefix}*`;
87
+ let cursor = "0";
88
+ let totalDeleted = 0;
89
+ do {
90
+ const result = await this.client.scan(cursor, {
91
+ MATCH: pattern,
92
+ COUNT: DEFAULT_SCAN_COUNT,
93
+ });
94
+ cursor = String(result.cursor);
95
+ if (result.keys.length > 0) {
96
+ totalDeleted += await this.client.del(result.keys);
97
+ }
98
+ } while (cursor !== "0");
99
+ if (totalDeleted > 0) {
100
+ console.log(`[RedisCache] Cleared ${totalDeleted} keys`);
101
+ }
102
+ this.hits = 0;
103
+ this.misses = 0;
104
+ }
105
+ catch (error) {
106
+ console.error("[RedisCache] Clear error:", error);
107
+ this.connected = false;
108
+ throw error;
109
+ }
110
+ });
111
+ }
112
+ async has(key) {
113
+ return withSpan("cache.redis.has", async () => {
114
+ try {
115
+ await this.ensureConnected();
116
+ const exists = await this.client.exists(this.key(key));
117
+ return exists === 1;
118
+ }
119
+ catch (error) {
120
+ console.error("[RedisCache] Has error:", error);
121
+ this.connected = false;
122
+ throw error;
123
+ }
124
+ }, { "cache.key": key });
125
+ }
126
+ async stats() {
127
+ return withSpan("cache.redis.stats", async () => {
128
+ let size = 0;
129
+ try {
130
+ await this.ensureConnected();
131
+ size = await this.client.dbSize();
132
+ }
133
+ catch (error) {
134
+ this.connected = false;
135
+ console.warn("[RedisCache] Stats error:", error);
136
+ }
137
+ return { hits: this.hits, misses: this.misses, size, type: "redis" };
138
+ });
139
+ }
140
+ async close() {
141
+ return withSpan("cache.redis.close", async () => {
142
+ if (this.client) {
143
+ try {
144
+ await this.client.quit();
145
+ }
146
+ catch {
147
+ // Ignore close errors
148
+ }
149
+ this.client = null;
150
+ }
151
+ this.connected = false;
152
+ });
153
+ }
154
+ async ensureConnected() {
155
+ return withSpan("cache.redis.connect", async () => {
156
+ if (this.connected && this.client) {
157
+ return;
158
+ }
159
+ // Create client with connection options
160
+ this.client = createClient({
161
+ url: this.url,
162
+ socket: {
163
+ connectTimeout: this.connectTimeout,
164
+ reconnectStrategy: (retries) => {
165
+ // Exponential backoff with max 3 retries
166
+ if (retries > 3) {
167
+ return new Error("Max reconnection attempts reached");
168
+ }
169
+ return Math.min(retries * 100, 3000);
170
+ },
171
+ },
172
+ });
173
+ // Handle connection errors
174
+ this.client.on("error", (err) => {
175
+ console.error("[RedisCache] Client error:", err);
176
+ this.connected = false;
177
+ });
178
+ await this.client.connect();
179
+ this.connected = true;
180
+ console.log("[RedisCache] Connected");
181
+ });
182
+ }
183
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Resilient Token Cache
3
+ *
4
+ * Wraps a primary cache (Redis) with a fallback cache (Memory).
5
+ * Automatically falls back to memory cache when Redis operations fail.
6
+ * Provides graceful degradation instead of hard failures.
7
+ */
8
+ import type { CacheStats, TokenCache, TokenCacheEntry } from "./types.js";
9
+ export declare class ResilientCache implements TokenCache {
10
+ private primary;
11
+ private fallback;
12
+ private usingFallback;
13
+ private failureCount;
14
+ private circuitOpenedAt;
15
+ constructor(primary: TokenCache, fallback: TokenCache);
16
+ /**
17
+ * Check if we should try primary again after circuit was opened.
18
+ */
19
+ private shouldTryPrimary;
20
+ /**
21
+ * Record a successful primary operation - reset failure state.
22
+ */
23
+ private recordSuccess;
24
+ /**
25
+ * Record a primary failure - may trigger fallback.
26
+ */
27
+ private recordFailure;
28
+ get(key: string): Promise<TokenCacheEntry | null>;
29
+ set(key: string, entry: TokenCacheEntry): Promise<void>;
30
+ delete(key: string): Promise<void>;
31
+ clear(): Promise<void>;
32
+ has(key: string): Promise<boolean>;
33
+ stats(): Promise<CacheStats>;
34
+ close(): Promise<void>;
35
+ /**
36
+ * Get current resilience status for debugging/health checks.
37
+ */
38
+ getStatus(): {
39
+ usingFallback: boolean;
40
+ failureCount: number;
41
+ circuitOpenedAt: number | null;
42
+ };
43
+ }
44
+ //# sourceMappingURL=resilient-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resilient-cache.d.ts","sourceRoot":"","sources":["../../../src/proxy/cache/resilient-cache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAS1E,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,eAAe,CAAuB;gBAElC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU;IAKrD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAexB;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,OAAO,CAAC,aAAa;IAcf,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAmBjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBvD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAetB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBlC,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAwB5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B;;OAEG;IACH,SAAS,IAAI;QACX,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;KAChC;CAOF"}