@sylphx/flow 1.8.0 → 1.8.2

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/CHANGELOG.md +72 -0
  2. package/assets/output-styles/silent.md +145 -8
  3. package/assets/rules/core.md +19 -2
  4. package/package.json +2 -12
  5. package/src/commands/flow/execute.ts +470 -0
  6. package/src/commands/flow/index.ts +11 -0
  7. package/src/commands/flow/prompt.ts +35 -0
  8. package/src/commands/flow/setup.ts +312 -0
  9. package/src/commands/flow/targets.ts +18 -0
  10. package/src/commands/flow/types.ts +47 -0
  11. package/src/commands/flow-command.ts +18 -967
  12. package/src/commands/flow-orchestrator.ts +14 -5
  13. package/src/commands/hook-command.ts +1 -1
  14. package/src/commands/init-core.ts +12 -3
  15. package/src/commands/run-command.ts +1 -1
  16. package/src/config/rules.ts +1 -1
  17. package/src/core/error-handling.ts +1 -1
  18. package/src/core/loop-controller.ts +1 -1
  19. package/src/core/state-detector.ts +1 -1
  20. package/src/core/target-manager.ts +1 -1
  21. package/src/index.ts +1 -1
  22. package/src/shared/files/index.ts +1 -1
  23. package/src/shared/processing/index.ts +1 -1
  24. package/src/targets/claude-code.ts +3 -3
  25. package/src/targets/opencode.ts +3 -3
  26. package/src/utils/agent-enhancer.ts +2 -2
  27. package/src/utils/{mcp-config.ts → config/mcp-config.ts} +4 -4
  28. package/src/utils/{paths.ts → config/paths.ts} +1 -1
  29. package/src/utils/{settings.ts → config/settings.ts} +1 -1
  30. package/src/utils/{target-config.ts → config/target-config.ts} +5 -5
  31. package/src/utils/{target-utils.ts → config/target-utils.ts} +3 -3
  32. package/src/utils/display/banner.ts +25 -0
  33. package/src/utils/display/status.ts +55 -0
  34. package/src/utils/{file-operations.ts → files/file-operations.ts} +2 -2
  35. package/src/utils/files/jsonc.ts +36 -0
  36. package/src/utils/{sync-utils.ts → files/sync-utils.ts} +3 -3
  37. package/src/utils/index.ts +42 -61
  38. package/src/utils/version.ts +47 -0
  39. package/src/components/benchmark-monitor.tsx +0 -331
  40. package/src/components/reindex-progress.tsx +0 -261
  41. package/src/composables/functional/index.ts +0 -14
  42. package/src/composables/functional/useEnvironment.ts +0 -171
  43. package/src/composables/functional/useFileSystem.ts +0 -139
  44. package/src/composables/index.ts +0 -4
  45. package/src/composables/useEnv.ts +0 -13
  46. package/src/composables/useRuntimeConfig.ts +0 -27
  47. package/src/core/ai-sdk.ts +0 -603
  48. package/src/core/app-factory.ts +0 -381
  49. package/src/core/builtin-agents.ts +0 -9
  50. package/src/core/command-system.ts +0 -550
  51. package/src/core/config-system.ts +0 -550
  52. package/src/core/connection-pool.ts +0 -390
  53. package/src/core/di-container.ts +0 -155
  54. package/src/core/headless-display.ts +0 -96
  55. package/src/core/interfaces/index.ts +0 -22
  56. package/src/core/interfaces/repository.interface.ts +0 -91
  57. package/src/core/interfaces/service.interface.ts +0 -133
  58. package/src/core/interfaces.ts +0 -96
  59. package/src/core/result.ts +0 -351
  60. package/src/core/service-config.ts +0 -252
  61. package/src/core/session-service.ts +0 -121
  62. package/src/core/storage-factory.ts +0 -115
  63. package/src/core/stream-handler.ts +0 -288
  64. package/src/core/type-utils.ts +0 -427
  65. package/src/core/unified-storage.ts +0 -456
  66. package/src/core/validation/limit.ts +0 -46
  67. package/src/core/validation/query.ts +0 -20
  68. package/src/db/auto-migrate.ts +0 -322
  69. package/src/db/base-database-client.ts +0 -144
  70. package/src/db/cache-db.ts +0 -218
  71. package/src/db/cache-schema.ts +0 -75
  72. package/src/db/database.ts +0 -70
  73. package/src/db/index.ts +0 -252
  74. package/src/db/memory-db.ts +0 -153
  75. package/src/db/memory-schema.ts +0 -29
  76. package/src/db/schema.ts +0 -289
  77. package/src/db/session-repository.ts +0 -733
  78. package/src/domains/index.ts +0 -6
  79. package/src/domains/utilities/index.ts +0 -6
  80. package/src/domains/utilities/time/index.ts +0 -5
  81. package/src/domains/utilities/time/tools.ts +0 -291
  82. package/src/services/agent-service.ts +0 -273
  83. package/src/services/evaluation-service.ts +0 -271
  84. package/src/services/functional/evaluation-logic.ts +0 -296
  85. package/src/services/functional/file-processor.ts +0 -273
  86. package/src/services/functional/index.ts +0 -12
  87. package/src/services/memory.service.ts +0 -476
  88. package/src/types/api/batch.ts +0 -108
  89. package/src/types/api/errors.ts +0 -118
  90. package/src/types/api/index.ts +0 -55
  91. package/src/types/api/requests.ts +0 -76
  92. package/src/types/api/responses.ts +0 -180
  93. package/src/types/api/websockets.ts +0 -85
  94. package/src/types/benchmark.ts +0 -49
  95. package/src/types/database.types.ts +0 -510
  96. package/src/types/memory-types.ts +0 -63
  97. package/src/utils/advanced-tokenizer.ts +0 -191
  98. package/src/utils/ai-model-fetcher.ts +0 -19
  99. package/src/utils/async-file-operations.ts +0 -516
  100. package/src/utils/audio-player.ts +0 -345
  101. package/src/utils/codebase-helpers.ts +0 -211
  102. package/src/utils/console-ui.ts +0 -79
  103. package/src/utils/database-errors.ts +0 -140
  104. package/src/utils/debug-logger.ts +0 -49
  105. package/src/utils/file-scanner.ts +0 -259
  106. package/src/utils/help.ts +0 -20
  107. package/src/utils/immutable-cache.ts +0 -106
  108. package/src/utils/jsonc.ts +0 -158
  109. package/src/utils/memory-tui.ts +0 -414
  110. package/src/utils/models-dev.ts +0 -91
  111. package/src/utils/parallel-operations.ts +0 -487
  112. package/src/utils/process-manager.ts +0 -155
  113. package/src/utils/prompts.ts +0 -120
  114. package/src/utils/search-tool-builder.ts +0 -214
  115. package/src/utils/session-manager.ts +0 -168
  116. package/src/utils/session-title.ts +0 -87
  117. package/src/utils/simplified-errors.ts +0 -410
  118. package/src/utils/template-engine.ts +0 -94
  119. package/src/utils/test-audio.ts +0 -71
  120. package/src/utils/todo-context.ts +0 -46
  121. package/src/utils/token-counter.ts +0 -288
  122. /package/src/utils/{cli-output.ts → display/cli-output.ts} +0 -0
  123. /package/src/utils/{logger.ts → display/logger.ts} +0 -0
  124. /package/src/utils/{notifications.ts → display/notifications.ts} +0 -0
  125. /package/src/utils/{secret-utils.ts → security/secret-utils.ts} +0 -0
  126. /package/src/utils/{security.ts → security/security.ts} +0 -0
@@ -1,140 +0,0 @@
1
- /**
2
- * Database Error Handling - Simplified System
3
- * Replaces complex error hierarchy with simplified database-specific errors
4
- */
5
-
6
- import {
7
- AppError,
8
- createDatabaseError,
9
- createValidationError,
10
- ErrorCategory,
11
- ErrorHandler,
12
- ErrorSeverity,
13
- DatabaseError as SimplifiedDatabaseError,
14
- ValidationError as SimplifiedValidationError,
15
- } from './simplified-errors.js';
16
-
17
- /**
18
- * Simplified Database Error with additional database context
19
- */
20
- export class DatabaseError extends SimplifiedDatabaseError {
21
- constructor(
22
- message: string,
23
- operation?: string,
24
- cause?: Error,
25
- context?: Record<string, unknown>
26
- ) {
27
- super(message, operation, context?.query as string);
28
- this.cause = cause;
29
- if (context) {
30
- this.context = { ...this.context, ...context };
31
- }
32
- }
33
- }
34
-
35
- /**
36
- * Database-specific validation error
37
- */
38
- export class ValidationError extends SimplifiedValidationError {
39
- constructor(message: string, field: string, value?: unknown, cause?: Error) {
40
- super(message, field, value);
41
- this.cause = cause;
42
- }
43
- }
44
-
45
- /**
46
- * Database connection error
47
- */
48
- export class ConnectionError extends AppError {
49
- constructor(message: string, connectionDetails?: Record<string, unknown>, cause?: Error) {
50
- super(
51
- message,
52
- 'CONNECTION_ERROR',
53
- ErrorCategory.NETWORK,
54
- ErrorSeverity.HIGH,
55
- connectionDetails,
56
- cause
57
- );
58
- this.name = 'ConnectionError';
59
- }
60
- }
61
-
62
- /**
63
- * Database migration error
64
- */
65
- export class MigrationError extends AppError {
66
- public readonly migrationName?: string;
67
-
68
- constructor(message: string, migrationName?: string, cause?: Error) {
69
- super(
70
- message,
71
- 'MIGRATION_ERROR',
72
- ErrorCategory.DATABASE,
73
- ErrorSeverity.HIGH,
74
- { migrationName },
75
- cause
76
- );
77
- this.migrationName = migrationName;
78
- this.name = 'MigrationError';
79
- }
80
- }
81
-
82
- /**
83
- * Execute database operation with comprehensive error handling
84
- */
85
- export async function executeOperation<T>(
86
- operation: string,
87
- fn: () => Promise<T>,
88
- context?: Record<string, unknown>
89
- ): Promise<T> {
90
- const result = await ErrorHandler.execute(fn, { operation, ...context });
91
-
92
- if (result.success) {
93
- return result.data;
94
- }
95
-
96
- // Convert to appropriate database error type
97
- if (result.error instanceof AppError) {
98
- throw result.error;
99
- }
100
-
101
- // Unknown error - wrap in DatabaseError
102
- throw createDatabaseError(result.error.message, operation, context?.query as string);
103
- }
104
-
105
- /**
106
- * Type guard functions for database errors
107
- */
108
- export function isDatabaseError(error: unknown): error is DatabaseError {
109
- return error instanceof DatabaseError;
110
- }
111
-
112
- export function isValidationError(error: unknown): error is ValidationError {
113
- return error instanceof ValidationError;
114
- }
115
-
116
- export function isConnectionError(error: unknown): error is ConnectionError {
117
- return error instanceof ConnectionError;
118
- }
119
-
120
- export function isMigrationError(error: unknown): error is MigrationError {
121
- return error instanceof MigrationError;
122
- }
123
-
124
- /**
125
- * Convenience functions for creating database errors
126
- */
127
- export const createMigrationError = (
128
- message: string,
129
- migrationName?: string,
130
- cause?: Error
131
- ): MigrationError => new MigrationError(message, migrationName, cause);
132
-
133
- export const createConnectionError = (
134
- message: string,
135
- connectionDetails?: Record<string, unknown>,
136
- cause?: Error
137
- ): ConnectionError => new ConnectionError(message, connectionDetails, cause);
138
-
139
- // Re-export for backward compatibility
140
- export { createDatabaseError, createValidationError, ErrorHandler, AppError };
@@ -1,49 +0,0 @@
1
- /**
2
- * Debug Logger
3
- * Uses industry-standard 'debug' package
4
- *
5
- * Usage:
6
- * DEBUG=* bun ./packages/flow/src/index.ts // All debug logs
7
- * DEBUG=sylphx:* bun ... // All sylphx namespaces
8
- * DEBUG=sylphx:search:* bun ... // Search namespace
9
- * (no DEBUG) bun ... // No debug logs
10
- *
11
- * Examples:
12
- * import { createLogger } from '../utils/debug-logger.js';
13
- *
14
- * const log = createLogger('search:indexing');
15
- * log('Indexing started:', filePath);
16
- *
17
- * Features from 'debug' package:
18
- * - Color-coded namespaces
19
- * - Timestamp support (DEBUG_COLORS=no for no color)
20
- * - Wildcard matching (DEBUG=sylphx:*)
21
- * - Conditional logging (no performance impact when disabled)
22
- * - Industry standard (used by Express, Socket.io, etc.)
23
- */
24
-
25
- import debug from 'debug';
26
-
27
- /**
28
- * Create a logger for a specific namespace
29
- * Namespace will be prefixed with 'sylphx:'
30
- *
31
- * @example
32
- * const log = createLogger('search:indexing');
33
- * log('Indexing started:', filePath);
34
- *
35
- * // Enable with:
36
- * // DEBUG=sylphx:search:indexing bun ./packages/flow/src/index.ts
37
- */
38
- export function createLogger(namespace: string) {
39
- return debug(`sylphx:${namespace}`);
40
- }
41
-
42
- /**
43
- * For backwards compatibility
44
- * @deprecated Use createLogger instead
45
- */
46
- export function debugLog(namespace: string, ...args: any[]) {
47
- const log = debug(`sylphx:${namespace}`);
48
- log(...args);
49
- }
@@ -1,259 +0,0 @@
1
- /**
2
- * File Scanner
3
- * Scan project files for @file auto-completion with caching
4
- */
5
-
6
- import { readdir, stat, readFile as fsReadFile, writeFile } from 'node:fs/promises';
7
- import { join, relative } from 'node:path';
8
- import { readFile } from 'node:fs/promises';
9
- import { homedir } from 'node:os';
10
-
11
- export interface FileInfo {
12
- path: string;
13
- relativePath: string;
14
- size: number;
15
- }
16
-
17
- // Default ignore patterns
18
- const DEFAULT_IGNORE = [
19
- 'node_modules',
20
- '.git',
21
- 'dist',
22
- 'build',
23
- '.next',
24
- '.vercel',
25
- '.turbo',
26
- 'coverage',
27
- '.cache',
28
- '.sylphx',
29
- 'bun.lock',
30
- 'package-lock.json',
31
- 'yarn.lock',
32
- ];
33
-
34
- /**
35
- * Load .gitignore patterns
36
- */
37
- async function loadGitignore(rootPath: string): Promise<Set<string>> {
38
- const patterns = new Set<string>(DEFAULT_IGNORE);
39
-
40
- try {
41
- const gitignorePath = join(rootPath, '.gitignore');
42
- const content = await readFile(gitignorePath, 'utf8');
43
-
44
- // Parse gitignore file
45
- for (const line of content.split('\n')) {
46
- const trimmed = line.trim();
47
- // Skip empty lines and comments
48
- if (trimmed && !trimmed.startsWith('#')) {
49
- // Remove trailing slashes
50
- const pattern = trimmed.endsWith('/') ? trimmed.slice(0, -1) : trimmed;
51
- patterns.add(pattern);
52
- }
53
- }
54
- } catch {
55
- // No .gitignore file, use defaults only
56
- }
57
-
58
- return patterns;
59
- }
60
-
61
- /**
62
- * Check if path should be ignored
63
- */
64
- function shouldIgnore(relativePath: string, patterns: Set<string>): boolean {
65
- // Check if any part of the path matches ignore patterns
66
- const parts = relativePath.split('/');
67
-
68
- for (const pattern of patterns) {
69
- // Exact match
70
- if (relativePath === pattern) return true;
71
-
72
- // Directory match
73
- if (parts.includes(pattern)) return true;
74
-
75
- // Glob pattern (basic support for *)
76
- if (pattern.includes('*')) {
77
- const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
78
- if (regex.test(relativePath)) return true;
79
- }
80
- }
81
-
82
- return false;
83
- }
84
-
85
- /**
86
- * Recursively scan directory for files (parallelized)
87
- */
88
- async function scanDirectory(
89
- dirPath: string,
90
- rootPath: string,
91
- patterns: Set<string>,
92
- results: FileInfo[] = []
93
- ): Promise<FileInfo[]> {
94
- try {
95
- const entries = await readdir(dirPath, { withFileTypes: true });
96
-
97
- // Separate files and directories for parallel processing
98
- const files: typeof entries = [];
99
- const directories: typeof entries = [];
100
-
101
- for (const entry of entries) {
102
- const fullPath = join(dirPath, entry.name);
103
- const relativePath = relative(rootPath, fullPath);
104
-
105
- // Skip ignored paths
106
- if (shouldIgnore(relativePath, patterns)) {
107
- continue;
108
- }
109
-
110
- if (entry.isDirectory()) {
111
- directories.push(entry);
112
- } else if (entry.isFile()) {
113
- files.push(entry);
114
- }
115
- }
116
-
117
- // Process files in parallel (no need to stat each one individually)
118
- // Use entry.isFile() which we already know, skip stat() call for performance
119
- const fileResults = files.map((entry) => {
120
- const fullPath = join(dirPath, entry.name);
121
- const relativePath = relative(rootPath, fullPath);
122
- return {
123
- path: fullPath,
124
- relativePath,
125
- size: 0, // We skip stat() for performance, size not critical for autocomplete
126
- };
127
- });
128
- results.push(...fileResults);
129
-
130
- // Process subdirectories in parallel
131
- if (directories.length > 0) {
132
- const subdirResults = await Promise.all(
133
- directories.map((entry) => {
134
- const fullPath = join(dirPath, entry.name);
135
- return scanDirectory(fullPath, rootPath, patterns, []);
136
- })
137
- );
138
-
139
- // Flatten results from all subdirectories
140
- for (const subdirResult of subdirResults) {
141
- results.push(...subdirResult);
142
- }
143
- }
144
- } catch (error) {
145
- // Skip directories we can't read
146
- }
147
-
148
- return results;
149
- }
150
-
151
- // Cache for scanned files
152
- const CACHE_DIR = join(homedir(), '.sylphx', 'cache');
153
- const CACHE_VERSION = 1;
154
-
155
- interface ScanCache {
156
- version: number;
157
- rootPath: string;
158
- timestamp: number;
159
- files: FileInfo[];
160
- }
161
-
162
- /**
163
- * Get cache file path for a project
164
- */
165
- function getCachePath(rootPath: string): string {
166
- // Use hash of root path as cache filename
167
- const hash = Buffer.from(rootPath).toString('base64').replace(/[/+=]/g, '_');
168
- return join(CACHE_DIR, `filescan-${hash}.json`);
169
- }
170
-
171
- /**
172
- * Load cached file list if valid
173
- */
174
- async function loadCache(rootPath: string): Promise<FileInfo[] | null> {
175
- try {
176
- const cachePath = getCachePath(rootPath);
177
- const content = await fsReadFile(cachePath, 'utf8');
178
- const cache: ScanCache = JSON.parse(content);
179
-
180
- // Validate cache
181
- if (cache.version !== CACHE_VERSION || cache.rootPath !== rootPath) {
182
- return null;
183
- }
184
-
185
- // Check if cache is still fresh (less than 5 minutes old)
186
- const age = Date.now() - cache.timestamp;
187
- const MAX_CACHE_AGE = 5 * 60 * 1000; // 5 minutes
188
- if (age > MAX_CACHE_AGE) {
189
- return null;
190
- }
191
-
192
- return cache.files;
193
- } catch {
194
- return null;
195
- }
196
- }
197
-
198
- /**
199
- * Save file list to cache
200
- */
201
- async function saveCache(rootPath: string, files: FileInfo[]): Promise<void> {
202
- try {
203
- const cachePath = getCachePath(rootPath);
204
- const cache: ScanCache = {
205
- version: CACHE_VERSION,
206
- rootPath,
207
- timestamp: Date.now(),
208
- files,
209
- };
210
-
211
- // Ensure cache directory exists
212
- const { mkdir } = await import('node:fs/promises');
213
- await mkdir(CACHE_DIR, { recursive: true });
214
-
215
- // Write cache file
216
- await writeFile(cachePath, JSON.stringify(cache), 'utf8');
217
- } catch (error) {
218
- // Ignore cache write errors
219
- console.warn('Failed to write file scanner cache:', error);
220
- }
221
- }
222
-
223
- /**
224
- * Scan project files with caching
225
- * Returns list of files respecting .gitignore
226
- */
227
- export async function scanProjectFiles(rootPath: string): Promise<FileInfo[]> {
228
- // Try to load from cache first
229
- const cached = await loadCache(rootPath);
230
- if (cached) {
231
- return cached;
232
- }
233
-
234
- // Cache miss or stale, scan filesystem
235
- const patterns = await loadGitignore(rootPath);
236
- const files = await scanDirectory(rootPath, rootPath, patterns);
237
-
238
- // Sort by path for consistent ordering
239
- files.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
240
-
241
- // Save to cache for next time
242
- saveCache(rootPath, files).catch(() => {
243
- // Ignore cache save errors
244
- });
245
-
246
- return files;
247
- }
248
-
249
- /**
250
- * Filter files by query string
251
- */
252
- export function filterFiles(files: FileInfo[], query: string): FileInfo[] {
253
- if (!query) return files;
254
-
255
- const lowerQuery = query.toLowerCase();
256
- return files.filter((file) =>
257
- file.relativePath.toLowerCase().includes(lowerQuery)
258
- );
259
- }
package/src/utils/help.ts DELETED
@@ -1,20 +0,0 @@
1
- export function showDefaultHelp(): void {
2
- console.log('🚀 Sylphx Flow CLI - Legacy project initialization and flow management');
3
- console.log('=========================================');
4
- console.log('');
5
- console.log('Available commands:');
6
- console.log(' init Initialize project with Sylphx Flow');
7
- console.log(' run Run workflows and flows');
8
- console.log(' codebase Search and analyze codebase');
9
- console.log(' knowledge Manage knowledge base');
10
- console.log(' hook Load dynamic content for hooks');
11
- console.log('');
12
- console.log('Examples:');
13
- console.log(' sylphx-flow init');
14
- console.log(' sylphx-flow init --target claude-code');
15
- console.log(' sylphx-flow run "your prompt"');
16
- console.log(' sylphx-flow codebase search "function"');
17
- console.log(' sylphx-flow knowledge search "React patterns"');
18
- console.log('');
19
- console.log('Run "sylphx-flow <command> --help" for more information about a command.');
20
- }
@@ -1,106 +0,0 @@
1
- /**
2
- * Immutable Cache
3
- * Functional cache abstraction that returns new state on mutations
4
- */
5
-
6
- export interface CacheState<K, V> {
7
- readonly entries: ReadonlyMap<K, V>;
8
- readonly size: number;
9
- }
10
-
11
- /**
12
- * Create an empty cache state
13
- */
14
- export const createCache = <K, V>(): CacheState<K, V> => ({
15
- entries: new Map(),
16
- size: 0,
17
- });
18
-
19
- /**
20
- * Set a value in the cache, returning new cache state
21
- */
22
- export const cacheSet = <K, V>(cache: CacheState<K, V>, key: K, value: V): CacheState<K, V> => {
23
- const newEntries = new Map(cache.entries);
24
- newEntries.set(key, value);
25
- return {
26
- entries: newEntries,
27
- size: newEntries.size,
28
- };
29
- };
30
-
31
- /**
32
- * Get a value from the cache
33
- */
34
- export const cacheGet = <K, V>(cache: CacheState<K, V>, key: K): V | undefined => {
35
- return cache.entries.get(key);
36
- };
37
-
38
- /**
39
- * Delete a key from the cache, returning new cache state
40
- */
41
- export const cacheDelete = <K, V>(cache: CacheState<K, V>, key: K): CacheState<K, V> => {
42
- const newEntries = new Map(cache.entries);
43
- newEntries.delete(key);
44
- return {
45
- entries: newEntries,
46
- size: newEntries.size,
47
- };
48
- };
49
-
50
- /**
51
- * Delete multiple keys matching a predicate, returning new cache state
52
- */
53
- export const cacheDeleteWhere = <K, V>(
54
- cache: CacheState<K, V>,
55
- predicate: (key: K, value: V) => boolean
56
- ): CacheState<K, V> => {
57
- const newEntries = new Map<K, V>();
58
- for (const [key, value] of cache.entries) {
59
- if (!predicate(key, value)) {
60
- newEntries.set(key, value);
61
- }
62
- }
63
- return {
64
- entries: newEntries,
65
- size: newEntries.size,
66
- };
67
- };
68
-
69
- /**
70
- * Clear all entries from the cache, returning new cache state
71
- */
72
- export const cacheClear = <K, V>(): CacheState<K, V> => createCache();
73
-
74
- /**
75
- * Get all keys from the cache
76
- */
77
- export const cacheKeys = <K, V>(cache: CacheState<K, V>): K[] => {
78
- return Array.from(cache.entries.keys());
79
- };
80
-
81
- /**
82
- * Enforce maximum cache size by removing oldest entries (FIFO)
83
- * Returns new cache state
84
- */
85
- export const cacheEnforceLimit = <K, V>(
86
- cache: CacheState<K, V>,
87
- maxSize: number
88
- ): CacheState<K, V> => {
89
- if (cache.size <= maxSize) {
90
- return cache;
91
- }
92
-
93
- const entriesToRemove = cache.size - maxSize;
94
- const keys = Array.from(cache.entries.keys());
95
- const keysToRemove = keys.slice(0, entriesToRemove);
96
-
97
- const newEntries = new Map(cache.entries);
98
- for (const key of keysToRemove) {
99
- newEntries.delete(key);
100
- }
101
-
102
- return {
103
- entries: newEntries,
104
- size: newEntries.size,
105
- };
106
- };