@sylphx/flow 1.7.0 → 1.8.1

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 (131) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/assets/agents/coder.md +72 -119
  3. package/assets/agents/orchestrator.md +26 -90
  4. package/assets/agents/reviewer.md +76 -47
  5. package/assets/agents/writer.md +82 -63
  6. package/assets/output-styles/silent.md +141 -8
  7. package/assets/rules/code-standards.md +9 -33
  8. package/assets/rules/core.md +67 -59
  9. package/package.json +2 -12
  10. package/src/commands/flow/execute.ts +470 -0
  11. package/src/commands/flow/index.ts +11 -0
  12. package/src/commands/flow/prompt.ts +35 -0
  13. package/src/commands/flow/setup.ts +312 -0
  14. package/src/commands/flow/targets.ts +18 -0
  15. package/src/commands/flow/types.ts +47 -0
  16. package/src/commands/flow-command.ts +18 -967
  17. package/src/commands/flow-orchestrator.ts +14 -5
  18. package/src/commands/hook-command.ts +1 -1
  19. package/src/commands/init-core.ts +12 -3
  20. package/src/commands/run-command.ts +1 -1
  21. package/src/config/rules.ts +1 -1
  22. package/src/core/error-handling.ts +1 -1
  23. package/src/core/loop-controller.ts +1 -1
  24. package/src/core/state-detector.ts +1 -1
  25. package/src/core/target-manager.ts +1 -1
  26. package/src/index.ts +1 -1
  27. package/src/shared/files/index.ts +1 -1
  28. package/src/shared/processing/index.ts +1 -1
  29. package/src/targets/claude-code.ts +3 -3
  30. package/src/targets/opencode.ts +3 -3
  31. package/src/utils/agent-enhancer.ts +2 -2
  32. package/src/utils/{mcp-config.ts → config/mcp-config.ts} +4 -4
  33. package/src/utils/{paths.ts → config/paths.ts} +1 -1
  34. package/src/utils/{settings.ts → config/settings.ts} +1 -1
  35. package/src/utils/{target-config.ts → config/target-config.ts} +5 -5
  36. package/src/utils/{target-utils.ts → config/target-utils.ts} +3 -3
  37. package/src/utils/display/banner.ts +25 -0
  38. package/src/utils/display/status.ts +55 -0
  39. package/src/utils/{file-operations.ts → files/file-operations.ts} +2 -2
  40. package/src/utils/files/jsonc.ts +36 -0
  41. package/src/utils/{sync-utils.ts → files/sync-utils.ts} +3 -3
  42. package/src/utils/index.ts +42 -61
  43. package/src/utils/version.ts +47 -0
  44. package/src/components/benchmark-monitor.tsx +0 -331
  45. package/src/components/reindex-progress.tsx +0 -261
  46. package/src/composables/functional/index.ts +0 -14
  47. package/src/composables/functional/useEnvironment.ts +0 -171
  48. package/src/composables/functional/useFileSystem.ts +0 -139
  49. package/src/composables/index.ts +0 -4
  50. package/src/composables/useEnv.ts +0 -13
  51. package/src/composables/useRuntimeConfig.ts +0 -27
  52. package/src/core/ai-sdk.ts +0 -603
  53. package/src/core/app-factory.ts +0 -381
  54. package/src/core/builtin-agents.ts +0 -9
  55. package/src/core/command-system.ts +0 -550
  56. package/src/core/config-system.ts +0 -550
  57. package/src/core/connection-pool.ts +0 -390
  58. package/src/core/di-container.ts +0 -155
  59. package/src/core/headless-display.ts +0 -96
  60. package/src/core/interfaces/index.ts +0 -22
  61. package/src/core/interfaces/repository.interface.ts +0 -91
  62. package/src/core/interfaces/service.interface.ts +0 -133
  63. package/src/core/interfaces.ts +0 -96
  64. package/src/core/result.ts +0 -351
  65. package/src/core/service-config.ts +0 -252
  66. package/src/core/session-service.ts +0 -121
  67. package/src/core/storage-factory.ts +0 -115
  68. package/src/core/stream-handler.ts +0 -288
  69. package/src/core/type-utils.ts +0 -427
  70. package/src/core/unified-storage.ts +0 -456
  71. package/src/core/validation/limit.ts +0 -46
  72. package/src/core/validation/query.ts +0 -20
  73. package/src/db/auto-migrate.ts +0 -322
  74. package/src/db/base-database-client.ts +0 -144
  75. package/src/db/cache-db.ts +0 -218
  76. package/src/db/cache-schema.ts +0 -75
  77. package/src/db/database.ts +0 -70
  78. package/src/db/index.ts +0 -252
  79. package/src/db/memory-db.ts +0 -153
  80. package/src/db/memory-schema.ts +0 -29
  81. package/src/db/schema.ts +0 -289
  82. package/src/db/session-repository.ts +0 -733
  83. package/src/domains/index.ts +0 -6
  84. package/src/domains/utilities/index.ts +0 -6
  85. package/src/domains/utilities/time/index.ts +0 -5
  86. package/src/domains/utilities/time/tools.ts +0 -291
  87. package/src/services/agent-service.ts +0 -273
  88. package/src/services/evaluation-service.ts +0 -271
  89. package/src/services/functional/evaluation-logic.ts +0 -296
  90. package/src/services/functional/file-processor.ts +0 -273
  91. package/src/services/functional/index.ts +0 -12
  92. package/src/services/memory.service.ts +0 -476
  93. package/src/types/api/batch.ts +0 -108
  94. package/src/types/api/errors.ts +0 -118
  95. package/src/types/api/index.ts +0 -55
  96. package/src/types/api/requests.ts +0 -76
  97. package/src/types/api/responses.ts +0 -180
  98. package/src/types/api/websockets.ts +0 -85
  99. package/src/types/benchmark.ts +0 -49
  100. package/src/types/database.types.ts +0 -510
  101. package/src/types/memory-types.ts +0 -63
  102. package/src/utils/advanced-tokenizer.ts +0 -191
  103. package/src/utils/ai-model-fetcher.ts +0 -19
  104. package/src/utils/async-file-operations.ts +0 -516
  105. package/src/utils/audio-player.ts +0 -345
  106. package/src/utils/codebase-helpers.ts +0 -211
  107. package/src/utils/console-ui.ts +0 -79
  108. package/src/utils/database-errors.ts +0 -140
  109. package/src/utils/debug-logger.ts +0 -49
  110. package/src/utils/file-scanner.ts +0 -259
  111. package/src/utils/help.ts +0 -20
  112. package/src/utils/immutable-cache.ts +0 -106
  113. package/src/utils/jsonc.ts +0 -158
  114. package/src/utils/memory-tui.ts +0 -414
  115. package/src/utils/models-dev.ts +0 -91
  116. package/src/utils/parallel-operations.ts +0 -487
  117. package/src/utils/process-manager.ts +0 -155
  118. package/src/utils/prompts.ts +0 -120
  119. package/src/utils/search-tool-builder.ts +0 -214
  120. package/src/utils/session-manager.ts +0 -168
  121. package/src/utils/session-title.ts +0 -87
  122. package/src/utils/simplified-errors.ts +0 -410
  123. package/src/utils/template-engine.ts +0 -94
  124. package/src/utils/test-audio.ts +0 -71
  125. package/src/utils/todo-context.ts +0 -46
  126. package/src/utils/token-counter.ts +0 -288
  127. /package/src/utils/{cli-output.ts → display/cli-output.ts} +0 -0
  128. /package/src/utils/{logger.ts → display/logger.ts} +0 -0
  129. /package/src/utils/{notifications.ts → display/notifications.ts} +0 -0
  130. /package/src/utils/{secret-utils.ts → security/secret-utils.ts} +0 -0
  131. /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
- };