@sylphx/flow 1.0.1 → 1.0.3
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/CHANGELOG.md +12 -0
- package/package.json +10 -9
- package/src/commands/codebase-command.ts +168 -0
- package/src/commands/flow-command.ts +1137 -0
- package/src/commands/flow-orchestrator.ts +296 -0
- package/src/commands/hook-command.ts +444 -0
- package/src/commands/init-command.ts +92 -0
- package/src/commands/init-core.ts +322 -0
- package/src/commands/knowledge-command.ts +161 -0
- package/src/commands/run-command.ts +120 -0
- package/src/components/benchmark-monitor.tsx +331 -0
- package/src/components/reindex-progress.tsx +261 -0
- package/src/composables/functional/index.ts +14 -0
- package/src/composables/functional/useEnvironment.ts +171 -0
- package/src/composables/functional/useFileSystem.ts +139 -0
- package/src/composables/index.ts +5 -0
- package/src/composables/useEnv.ts +13 -0
- package/src/composables/useRuntimeConfig.ts +27 -0
- package/src/composables/useTargetConfig.ts +45 -0
- package/src/config/ai-config.ts +376 -0
- package/src/config/constants.ts +35 -0
- package/src/config/index.ts +27 -0
- package/src/config/rules.ts +43 -0
- package/src/config/servers.ts +371 -0
- package/src/config/targets.ts +126 -0
- package/src/core/agent-loader.ts +141 -0
- package/src/core/agent-manager.ts +174 -0
- package/src/core/ai-sdk.ts +603 -0
- package/src/core/app-factory.ts +381 -0
- package/src/core/builtin-agents.ts +9 -0
- package/src/core/command-system.ts +550 -0
- package/src/core/config-system.ts +550 -0
- package/src/core/connection-pool.ts +390 -0
- package/src/core/di-container.ts +155 -0
- package/src/core/error-handling.ts +519 -0
- package/src/core/formatting/bytes.test.ts +115 -0
- package/src/core/formatting/bytes.ts +64 -0
- package/src/core/functional/async.ts +313 -0
- package/src/core/functional/either.ts +109 -0
- package/src/core/functional/error-handler.ts +135 -0
- package/src/core/functional/error-types.ts +311 -0
- package/src/core/functional/index.ts +19 -0
- package/src/core/functional/option.ts +142 -0
- package/src/core/functional/pipe.ts +189 -0
- package/src/core/functional/result.ts +204 -0
- package/src/core/functional/validation.ts +138 -0
- package/src/core/headless-display.ts +96 -0
- package/src/core/index.ts +6 -0
- package/src/core/installers/file-installer.ts +303 -0
- package/src/core/installers/mcp-installer.ts +213 -0
- package/src/core/interfaces/index.ts +22 -0
- package/src/core/interfaces/repository.interface.ts +91 -0
- package/src/core/interfaces/service.interface.ts +133 -0
- package/src/core/interfaces.ts +129 -0
- package/src/core/loop-controller.ts +200 -0
- package/src/core/result.ts +351 -0
- package/src/core/rule-loader.ts +147 -0
- package/src/core/rule-manager.ts +240 -0
- package/src/core/service-config.ts +252 -0
- package/src/core/session-service.ts +121 -0
- package/src/core/state-detector.ts +389 -0
- package/src/core/storage-factory.ts +115 -0
- package/src/core/stream-handler.ts +288 -0
- package/src/core/target-manager.ts +161 -0
- package/src/core/type-utils.ts +427 -0
- package/src/core/unified-storage.ts +456 -0
- package/src/core/upgrade-manager.ts +300 -0
- package/src/core/validation/limit.test.ts +155 -0
- package/src/core/validation/limit.ts +46 -0
- package/src/core/validation/query.test.ts +44 -0
- package/src/core/validation/query.ts +20 -0
- package/src/db/auto-migrate.ts +322 -0
- package/src/db/base-database-client.ts +144 -0
- package/src/db/cache-db.ts +218 -0
- package/src/db/cache-schema.ts +75 -0
- package/src/db/database.ts +70 -0
- package/src/db/index.ts +252 -0
- package/src/db/memory-db.ts +153 -0
- package/src/db/memory-schema.ts +29 -0
- package/src/db/schema.ts +289 -0
- package/src/db/session-repository.ts +733 -0
- package/src/domains/codebase/index.ts +5 -0
- package/src/domains/codebase/tools.ts +139 -0
- package/src/domains/index.ts +8 -0
- package/src/domains/knowledge/index.ts +10 -0
- package/src/domains/knowledge/resources.ts +537 -0
- package/src/domains/knowledge/tools.ts +174 -0
- package/src/domains/utilities/index.ts +6 -0
- package/src/domains/utilities/time/index.ts +5 -0
- package/src/domains/utilities/time/tools.ts +291 -0
- package/src/index.ts +211 -0
- package/src/services/agent-service.ts +273 -0
- package/src/services/claude-config-service.ts +252 -0
- package/src/services/config-service.ts +258 -0
- package/src/services/evaluation-service.ts +271 -0
- package/src/services/functional/evaluation-logic.ts +296 -0
- package/src/services/functional/file-processor.ts +273 -0
- package/src/services/functional/index.ts +12 -0
- package/src/services/index.ts +13 -0
- package/src/services/mcp-service.ts +432 -0
- package/src/services/memory.service.ts +476 -0
- package/src/services/search/base-indexer.ts +156 -0
- package/src/services/search/codebase-indexer-types.ts +38 -0
- package/src/services/search/codebase-indexer.ts +647 -0
- package/src/services/search/embeddings-provider.ts +455 -0
- package/src/services/search/embeddings.ts +316 -0
- package/src/services/search/functional-indexer.ts +323 -0
- package/src/services/search/index.ts +27 -0
- package/src/services/search/indexer.ts +380 -0
- package/src/services/search/knowledge-indexer.ts +422 -0
- package/src/services/search/semantic-search.ts +244 -0
- package/src/services/search/tfidf.ts +559 -0
- package/src/services/search/unified-search-service.ts +888 -0
- package/src/services/smart-config-service.ts +385 -0
- package/src/services/storage/cache-storage.ts +487 -0
- package/src/services/storage/drizzle-storage.ts +581 -0
- package/src/services/storage/index.ts +15 -0
- package/src/services/storage/lancedb-vector-storage.ts +494 -0
- package/src/services/storage/memory-storage.ts +268 -0
- package/src/services/storage/separated-storage.ts +467 -0
- package/src/services/storage/vector-storage.ts +13 -0
- package/src/shared/agents/index.ts +63 -0
- package/src/shared/files/index.ts +99 -0
- package/src/shared/index.ts +32 -0
- package/src/shared/logging/index.ts +24 -0
- package/src/shared/processing/index.ts +153 -0
- package/src/shared/types/index.ts +25 -0
- package/src/targets/claude-code.ts +574 -0
- package/src/targets/functional/claude-code-logic.ts +185 -0
- package/src/targets/functional/index.ts +6 -0
- package/src/targets/opencode.ts +529 -0
- package/src/types/agent.types.ts +32 -0
- package/src/types/api/batch.ts +108 -0
- package/src/types/api/errors.ts +118 -0
- package/src/types/api/index.ts +55 -0
- package/src/types/api/requests.ts +76 -0
- package/src/types/api/responses.ts +180 -0
- package/src/types/api/websockets.ts +85 -0
- package/src/types/api.types.ts +9 -0
- package/src/types/benchmark.ts +49 -0
- package/src/types/cli.types.ts +87 -0
- package/src/types/common.types.ts +35 -0
- package/src/types/database.types.ts +510 -0
- package/src/types/mcp-config.types.ts +448 -0
- package/src/types/mcp.types.ts +69 -0
- package/src/types/memory-types.ts +63 -0
- package/src/types/provider.types.ts +28 -0
- package/src/types/rule.types.ts +24 -0
- package/src/types/session.types.ts +214 -0
- package/src/types/target-config.types.ts +295 -0
- package/src/types/target.types.ts +140 -0
- package/src/types/todo.types.ts +25 -0
- package/src/types.ts +40 -0
- package/src/utils/advanced-tokenizer.ts +191 -0
- package/src/utils/agent-enhancer.ts +114 -0
- package/src/utils/ai-model-fetcher.ts +19 -0
- package/src/utils/async-file-operations.ts +516 -0
- package/src/utils/audio-player.ts +345 -0
- package/src/utils/cli-output.ts +266 -0
- package/src/utils/codebase-helpers.ts +211 -0
- package/src/utils/console-ui.ts +79 -0
- package/src/utils/database-errors.ts +140 -0
- package/src/utils/debug-logger.ts +49 -0
- package/src/utils/error-handler.ts +53 -0
- package/src/utils/file-operations.ts +310 -0
- package/src/utils/file-scanner.ts +259 -0
- package/src/utils/functional/array.ts +355 -0
- package/src/utils/functional/index.ts +15 -0
- package/src/utils/functional/object.ts +279 -0
- package/src/utils/functional/string.ts +281 -0
- package/src/utils/functional.ts +543 -0
- package/src/utils/help.ts +20 -0
- package/src/utils/immutable-cache.ts +106 -0
- package/src/utils/index.ts +78 -0
- package/src/utils/jsonc.ts +158 -0
- package/src/utils/logger.ts +396 -0
- package/src/utils/mcp-config.ts +249 -0
- package/src/utils/memory-tui.ts +414 -0
- package/src/utils/models-dev.ts +91 -0
- package/src/utils/notifications.ts +169 -0
- package/src/utils/object-utils.ts +51 -0
- package/src/utils/parallel-operations.ts +487 -0
- package/src/utils/paths.ts +143 -0
- package/src/utils/process-manager.ts +155 -0
- package/src/utils/prompts.ts +120 -0
- package/src/utils/search-tool-builder.ts +214 -0
- package/src/utils/secret-utils.ts +179 -0
- package/src/utils/security.ts +537 -0
- package/src/utils/session-manager.ts +168 -0
- package/src/utils/session-title.ts +87 -0
- package/src/utils/settings.ts +182 -0
- package/src/utils/simplified-errors.ts +410 -0
- package/src/utils/sync-utils.ts +159 -0
- package/src/utils/target-config.ts +570 -0
- package/src/utils/target-utils.ts +394 -0
- package/src/utils/template-engine.ts +94 -0
- package/src/utils/test-audio.ts +71 -0
- package/src/utils/todo-context.ts +46 -0
- package/src/utils/token-counter.ts +288 -0
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -59554
- package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
- package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
- package/dist/shared/chunk-25dwp0dp.js +0 -89
- package/dist/shared/chunk-3pjb6063.js +0 -208
- package/dist/shared/chunk-4d6ydpw7.js +0 -2854
- package/dist/shared/chunk-4wjcadjk.js +0 -225
- package/dist/shared/chunk-5j4w74t6.js +0 -30
- package/dist/shared/chunk-5j8m3dh3.js +0 -58
- package/dist/shared/chunk-5thh3qem.js +0 -91
- package/dist/shared/chunk-6g9xy73m.js +0 -252
- package/dist/shared/chunk-7eq34c42.js +0 -23
- package/dist/shared/chunk-c2gwgx3r.js +0 -115
- package/dist/shared/chunk-cjd3mk4c.js +0 -1320
- package/dist/shared/chunk-g5cv6703.js +0 -368
- package/dist/shared/chunk-hpkhykhq.js +0 -574
- package/dist/shared/chunk-m2322pdk.js +0 -122
- package/dist/shared/chunk-nd5fdvaq.js +0 -26
- package/dist/shared/chunk-pgd3m6zf.js +0 -108
- package/dist/shared/chunk-qk8n91hw.js +0 -494
- package/dist/shared/chunk-rkkn8szp.js +0 -16855
- package/dist/shared/chunk-t16rfxh0.js +0 -61
- package/dist/shared/chunk-t4fbfa5v.js +0 -19
- package/dist/shared/chunk-t77h86w6.js +0 -276
- package/dist/shared/chunk-v0ez4aef.js +0 -71
- package/dist/shared/chunk-v29j2r3s.js +0 -32051
- package/dist/shared/chunk-vfbc6ew5.js +0 -765
- package/dist/shared/chunk-vmeqwm1c.js +0 -204
- package/dist/shared/chunk-x66eh37x.js +0 -137
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Connection Pool
|
|
3
|
+
*
|
|
4
|
+
* Manages a pool of database connections for better performance and resource management
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface ConnectionConfig {
|
|
8
|
+
maxConnections?: number;
|
|
9
|
+
minConnections?: number;
|
|
10
|
+
acquireTimeout?: number;
|
|
11
|
+
idleTimeout?: number;
|
|
12
|
+
maxLifetime?: number;
|
|
13
|
+
healthCheckInterval?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface Connection<T = any> {
|
|
17
|
+
id: string;
|
|
18
|
+
instance: T;
|
|
19
|
+
createdAt: number;
|
|
20
|
+
lastUsed: number;
|
|
21
|
+
isInUse: boolean;
|
|
22
|
+
isHealthy: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ConnectionPoolInstance<T> {
|
|
26
|
+
acquire(): Promise<T>;
|
|
27
|
+
release(connectionInstance: T): Promise<void>;
|
|
28
|
+
getStats(): {
|
|
29
|
+
totalConnections: number;
|
|
30
|
+
activeConnections: number;
|
|
31
|
+
idleConnections: number;
|
|
32
|
+
unhealthyConnections: number;
|
|
33
|
+
maxConnections?: number;
|
|
34
|
+
minConnections?: number;
|
|
35
|
+
};
|
|
36
|
+
dispose(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a connection pool for database connections
|
|
41
|
+
*/
|
|
42
|
+
export function createConnectionPool<T>(
|
|
43
|
+
createConnection: () => Promise<T>,
|
|
44
|
+
destroyConnection: (connection: T) => Promise<void>,
|
|
45
|
+
healthCheck: (connection: T) => Promise<boolean>,
|
|
46
|
+
configInput: ConnectionConfig = {}
|
|
47
|
+
): ConnectionPoolInstance<T> {
|
|
48
|
+
// Closure-based state
|
|
49
|
+
const connections = new Map<string, Connection<T>>();
|
|
50
|
+
let availableConnections: string[] = [];
|
|
51
|
+
let connectionCount = 0;
|
|
52
|
+
let healthCheckTimer: NodeJS.Timeout | undefined;
|
|
53
|
+
let isDisposing = false;
|
|
54
|
+
|
|
55
|
+
const config: Required<ConnectionConfig> = {
|
|
56
|
+
maxConnections: 10,
|
|
57
|
+
minConnections: 2,
|
|
58
|
+
acquireTimeout: 30000,
|
|
59
|
+
idleTimeout: 300000,
|
|
60
|
+
maxLifetime: 3600000,
|
|
61
|
+
healthCheckInterval: 60000,
|
|
62
|
+
...configInput,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generate a unique connection ID
|
|
67
|
+
*/
|
|
68
|
+
const generateConnectionId = (): string => {
|
|
69
|
+
return `conn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if a connection is valid
|
|
74
|
+
*/
|
|
75
|
+
const isConnectionValid = (connection: Connection<T>): boolean => {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
|
|
78
|
+
// Check age
|
|
79
|
+
if (now - connection.createdAt > config.maxLifetime) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check if it's idle for too long (but only if not in use)
|
|
84
|
+
if (!connection.isInUse && now - connection.lastUsed > config.idleTimeout) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check health
|
|
89
|
+
return connection.isHealthy;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create a new connection
|
|
94
|
+
*/
|
|
95
|
+
const createNewConnection = async (): Promise<T> => {
|
|
96
|
+
const startTime = Date.now();
|
|
97
|
+
const connectionInstance = await createConnection();
|
|
98
|
+
const createTime = Date.now() - startTime;
|
|
99
|
+
|
|
100
|
+
const connection: Connection<T> = {
|
|
101
|
+
id: generateConnectionId(),
|
|
102
|
+
instance: connectionInstance,
|
|
103
|
+
createdAt: Date.now(),
|
|
104
|
+
lastUsed: Date.now(),
|
|
105
|
+
isInUse: true,
|
|
106
|
+
isHealthy: true,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
connections.set(connection.id, connection);
|
|
110
|
+
connectionCount++;
|
|
111
|
+
|
|
112
|
+
console.debug(
|
|
113
|
+
`New connection created in ${createTime}ms, total connections: ${connectionCount}`
|
|
114
|
+
);
|
|
115
|
+
return connectionInstance;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Wait for a connection to become available
|
|
120
|
+
*/
|
|
121
|
+
const waitForConnection = async (): Promise<T> => {
|
|
122
|
+
const timeout = config.acquireTimeout;
|
|
123
|
+
const startTime = Date.now();
|
|
124
|
+
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
const checkInterval = setInterval(() => {
|
|
127
|
+
if (isDisposing) {
|
|
128
|
+
clearInterval(checkInterval);
|
|
129
|
+
reject(new Error('Connection pool is disposing'));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (availableConnections.length > 0) {
|
|
134
|
+
clearInterval(checkInterval);
|
|
135
|
+
const connectionId = availableConnections.shift()!;
|
|
136
|
+
const connection = connections.get(connectionId)!;
|
|
137
|
+
|
|
138
|
+
if (isConnectionValid(connection)) {
|
|
139
|
+
connection.isInUse = true;
|
|
140
|
+
connection.lastUsed = Date.now();
|
|
141
|
+
resolve(connection.instance);
|
|
142
|
+
} else {
|
|
143
|
+
// Remove invalid connection and try again
|
|
144
|
+
connections.delete(connectionId);
|
|
145
|
+
connectionCount--;
|
|
146
|
+
destroyConnection(connection.instance).catch(console.error);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check timeout
|
|
152
|
+
if (Date.now() - startTime > timeout) {
|
|
153
|
+
clearInterval(checkInterval);
|
|
154
|
+
reject(new Error('Connection acquire timeout'));
|
|
155
|
+
}
|
|
156
|
+
}, 100);
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Maintain minimum connections
|
|
162
|
+
*/
|
|
163
|
+
const maintainMinConnections = async (): Promise<void> => {
|
|
164
|
+
const minConnections = config.minConnections;
|
|
165
|
+
const availableCount = availableConnections.length;
|
|
166
|
+
|
|
167
|
+
if (availableCount < minConnections && connectionCount < config.maxConnections) {
|
|
168
|
+
const needed = minConnections - availableCount;
|
|
169
|
+
for (let i = 0; i < needed; i++) {
|
|
170
|
+
createNewConnection()
|
|
171
|
+
.then((connection) => release(connection))
|
|
172
|
+
.catch((error) => console.error('Failed to maintain minimum connection:', error));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Start health check timer
|
|
179
|
+
*/
|
|
180
|
+
const startHealthCheck = (): void => {
|
|
181
|
+
const interval = config.healthCheckInterval;
|
|
182
|
+
|
|
183
|
+
healthCheckTimer = setInterval(async () => {
|
|
184
|
+
if (isDisposing) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
for (const [id, connection] of connections) {
|
|
189
|
+
try {
|
|
190
|
+
const isHealthy = await healthCheck(connection.instance);
|
|
191
|
+
|
|
192
|
+
if (isHealthy) {
|
|
193
|
+
connection.isHealthy = true;
|
|
194
|
+
} else {
|
|
195
|
+
connection.isHealthy = false;
|
|
196
|
+
console.warn(`Connection ${id} failed health check`);
|
|
197
|
+
|
|
198
|
+
// Remove unhealthy connection if not in use
|
|
199
|
+
if (!connection.isInUse) {
|
|
200
|
+
connections.delete(id);
|
|
201
|
+
connectionCount--;
|
|
202
|
+
const index = availableConnections.indexOf(id);
|
|
203
|
+
if (index > -1) {
|
|
204
|
+
availableConnections.splice(index, 1);
|
|
205
|
+
}
|
|
206
|
+
await destroyConnection(connection.instance);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error(`Health check failed for connection ${id}:`, error);
|
|
211
|
+
connection.isHealthy = false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Maintain minimum connections
|
|
216
|
+
await maintainMinConnections();
|
|
217
|
+
}, interval);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Initialize minimum connections
|
|
222
|
+
*/
|
|
223
|
+
const initializeMinConnections = async (): Promise<void> => {
|
|
224
|
+
const minConnections = config.minConnections;
|
|
225
|
+
const promises = [];
|
|
226
|
+
|
|
227
|
+
for (let i = 0; i < minConnections; i++) {
|
|
228
|
+
promises.push(
|
|
229
|
+
createNewConnection()
|
|
230
|
+
.then((connection) => release(connection))
|
|
231
|
+
.catch((error) => console.error('Failed to initialize minimum connection:', error))
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
await Promise.all(promises);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Acquire a connection from the pool
|
|
240
|
+
*/
|
|
241
|
+
const acquire = async (): Promise<T> => {
|
|
242
|
+
if (isDisposing) {
|
|
243
|
+
throw new Error('Connection pool is disposing');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Try to get an available connection
|
|
247
|
+
while (availableConnections.length > 0) {
|
|
248
|
+
const connectionId = availableConnections.shift()!;
|
|
249
|
+
const connection = connections.get(connectionId)!;
|
|
250
|
+
|
|
251
|
+
if (isConnectionValid(connection)) {
|
|
252
|
+
connection.isInUse = true;
|
|
253
|
+
connection.lastUsed = Date.now();
|
|
254
|
+
return connection.instance;
|
|
255
|
+
}
|
|
256
|
+
// Remove invalid connection
|
|
257
|
+
connections.delete(connectionId);
|
|
258
|
+
connectionCount--;
|
|
259
|
+
await destroyConnection(connection.instance);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Create new connection if under limit
|
|
263
|
+
if (connectionCount < config.maxConnections) {
|
|
264
|
+
return await createNewConnection();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Wait for a connection to become available
|
|
268
|
+
return await waitForConnection();
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Release a connection back to the pool
|
|
273
|
+
*/
|
|
274
|
+
const release = async (connectionInstance: T): Promise<void> => {
|
|
275
|
+
for (const [id, connection] of connections) {
|
|
276
|
+
if (connection.instance === connectionInstance) {
|
|
277
|
+
connection.isInUse = false;
|
|
278
|
+
connection.lastUsed = Date.now();
|
|
279
|
+
availableConnections.push(id);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Connection not found in pool, destroy it
|
|
285
|
+
await destroyConnection(connectionInstance);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get pool statistics
|
|
290
|
+
*/
|
|
291
|
+
const getStats = () => {
|
|
292
|
+
const activeConnections = Array.from(connections.values()).filter((c) => c.isInUse).length;
|
|
293
|
+
const idleConnections = availableConnections.length;
|
|
294
|
+
const unhealthyConnections = Array.from(connections.values()).filter(
|
|
295
|
+
(c) => !c.isHealthy
|
|
296
|
+
).length;
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
totalConnections: connectionCount,
|
|
300
|
+
activeConnections,
|
|
301
|
+
idleConnections,
|
|
302
|
+
unhealthyConnections,
|
|
303
|
+
maxConnections: config.maxConnections,
|
|
304
|
+
minConnections: config.minConnections,
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Close all connections and dispose the pool
|
|
310
|
+
*/
|
|
311
|
+
const dispose = async (): Promise<void> => {
|
|
312
|
+
isDisposing = true;
|
|
313
|
+
|
|
314
|
+
if (healthCheckTimer) {
|
|
315
|
+
clearInterval(healthCheckTimer);
|
|
316
|
+
healthCheckTimer = undefined;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const destroyPromises = Array.from(connections.values()).map(async (connection) => {
|
|
320
|
+
try {
|
|
321
|
+
await destroyConnection(connection.instance);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
console.error('Error destroying connection during pool disposal:', error);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
await Promise.all(destroyPromises);
|
|
328
|
+
|
|
329
|
+
connections.clear();
|
|
330
|
+
availableConnections = [];
|
|
331
|
+
connectionCount = 0;
|
|
332
|
+
|
|
333
|
+
console.log('Connection pool disposed');
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// Initialize pool
|
|
337
|
+
startHealthCheck();
|
|
338
|
+
initializeMinConnections();
|
|
339
|
+
|
|
340
|
+
return {
|
|
341
|
+
acquire,
|
|
342
|
+
release,
|
|
343
|
+
getStats,
|
|
344
|
+
dispose,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @deprecated Use createConnectionPool() for new code
|
|
350
|
+
*/
|
|
351
|
+
export class ConnectionPool<T> {
|
|
352
|
+
private instance: ConnectionPoolInstance<T>;
|
|
353
|
+
|
|
354
|
+
constructor(
|
|
355
|
+
createConnection: () => Promise<T>,
|
|
356
|
+
destroyConnection: (connection: T) => Promise<void>,
|
|
357
|
+
healthCheck: (connection: T) => Promise<boolean>,
|
|
358
|
+
config: ConnectionConfig = {}
|
|
359
|
+
) {
|
|
360
|
+
this.instance = createConnectionPool(createConnection, destroyConnection, healthCheck, config);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async acquire(): Promise<T> {
|
|
364
|
+
return this.instance.acquire();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async release(connectionInstance: T): Promise<void> {
|
|
368
|
+
return this.instance.release(connectionInstance);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
getStats() {
|
|
372
|
+
return this.instance.getStats();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async dispose(): Promise<void> {
|
|
376
|
+
return this.instance.dispose();
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Create a connection pool for database connections
|
|
382
|
+
*/
|
|
383
|
+
export function createDatabaseConnectionPool(
|
|
384
|
+
createDbConnection: () => Promise<any>,
|
|
385
|
+
destroyDbConnection: (connection: any) => Promise<void>,
|
|
386
|
+
healthCheckFn: (connection: any) => Promise<boolean>,
|
|
387
|
+
config?: ConnectionConfig
|
|
388
|
+
): ConnectionPoolInstance<any> {
|
|
389
|
+
return createConnectionPool(createDbConnection, destroyDbConnection, healthCheckFn, config);
|
|
390
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight Dependency Injection Container
|
|
3
|
+
*
|
|
4
|
+
* A minimal DI container focusing on core services: database, logging, configuration
|
|
5
|
+
* Uses constructor injection pattern for better testability
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type ServiceFactory<T> = () => T | Promise<T>;
|
|
9
|
+
export type ServiceLifetime = 'singleton' | 'transient' | 'scoped';
|
|
10
|
+
|
|
11
|
+
export interface ServiceDescriptor<T = any> {
|
|
12
|
+
factory: ServiceFactory<T>;
|
|
13
|
+
lifetime: ServiceLifetime;
|
|
14
|
+
instance?: T;
|
|
15
|
+
isResolved?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class DIContainer {
|
|
19
|
+
private services = new Map<string, ServiceDescriptor>();
|
|
20
|
+
private scopedInstances = new Map<string, any>();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Register a service with the container
|
|
24
|
+
*/
|
|
25
|
+
register<T>(
|
|
26
|
+
token: string,
|
|
27
|
+
factory: ServiceFactory<T>,
|
|
28
|
+
lifetime: ServiceLifetime = 'singleton'
|
|
29
|
+
): void {
|
|
30
|
+
this.services.set(token, {
|
|
31
|
+
factory,
|
|
32
|
+
lifetime,
|
|
33
|
+
isResolved: false,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Register a singleton instance
|
|
39
|
+
*/
|
|
40
|
+
registerInstance<T>(token: string, instance: T): void {
|
|
41
|
+
this.services.set(token, {
|
|
42
|
+
factory: () => instance,
|
|
43
|
+
lifetime: 'singleton',
|
|
44
|
+
instance,
|
|
45
|
+
isResolved: true,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve a service from the container
|
|
51
|
+
*/
|
|
52
|
+
async resolve<T>(token: string): Promise<T> {
|
|
53
|
+
const descriptor = this.services.get(token);
|
|
54
|
+
|
|
55
|
+
if (!descriptor) {
|
|
56
|
+
throw new Error(`Service not registered: ${token}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
switch (descriptor.lifetime) {
|
|
60
|
+
case 'singleton':
|
|
61
|
+
return this.resolveSingleton<T>(descriptor);
|
|
62
|
+
case 'transient':
|
|
63
|
+
return this.resolveTransient<T>(descriptor);
|
|
64
|
+
case 'scoped':
|
|
65
|
+
return this.resolveScoped<T>(token, descriptor);
|
|
66
|
+
default:
|
|
67
|
+
throw new Error(`Unsupported service lifetime: ${descriptor.lifetime}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if a service is registered
|
|
73
|
+
*/
|
|
74
|
+
isRegistered(token: string): boolean {
|
|
75
|
+
return this.services.has(token);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create a new scope for scoped services
|
|
80
|
+
*/
|
|
81
|
+
createScope(): DIContainer {
|
|
82
|
+
const scope = new DIContainer();
|
|
83
|
+
// Copy all service descriptors but not instances
|
|
84
|
+
for (const [token, descriptor] of this.services) {
|
|
85
|
+
scope.services.set(token, { ...descriptor });
|
|
86
|
+
}
|
|
87
|
+
return scope;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Clear scoped instances (useful for request cleanup)
|
|
92
|
+
*/
|
|
93
|
+
clearScope(): void {
|
|
94
|
+
this.scopedInstances.clear();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Dispose all singleton services that have dispose method
|
|
99
|
+
*/
|
|
100
|
+
async dispose(): Promise<void> {
|
|
101
|
+
for (const descriptor of this.services.values()) {
|
|
102
|
+
if (descriptor.instance && typeof descriptor.instance.dispose === 'function') {
|
|
103
|
+
try {
|
|
104
|
+
await descriptor.instance.dispose();
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error('Error disposing service:', error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
this.services.clear();
|
|
111
|
+
this.scopedInstances.clear();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private async resolveSingleton<T>(descriptor: ServiceDescriptor<T>): Promise<T> {
|
|
115
|
+
if (descriptor.isResolved && descriptor.instance) {
|
|
116
|
+
return descriptor.instance;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const instance = await descriptor.factory();
|
|
120
|
+
descriptor.instance = instance;
|
|
121
|
+
descriptor.isResolved = true;
|
|
122
|
+
|
|
123
|
+
return instance;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private async resolveTransient<T>(descriptor: ServiceDescriptor<T>): Promise<T> {
|
|
127
|
+
return await descriptor.factory();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private async resolveScoped<T>(token: string, descriptor: ServiceDescriptor<T>): Promise<T> {
|
|
131
|
+
if (this.scopedInstances.has(token)) {
|
|
132
|
+
return this.scopedInstances.get(token);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const instance = await descriptor.factory();
|
|
136
|
+
this.scopedInstances.set(token, instance);
|
|
137
|
+
|
|
138
|
+
return instance;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Global container instance
|
|
143
|
+
export const container = new DIContainer();
|
|
144
|
+
|
|
145
|
+
// Service tokens constants
|
|
146
|
+
export const SERVICE_TOKENS = {
|
|
147
|
+
DATABASE: 'database',
|
|
148
|
+
LOGGER: 'logger',
|
|
149
|
+
CONFIG: 'config',
|
|
150
|
+
MEMORY_STORAGE: 'memoryStorage',
|
|
151
|
+
SEARCH_SERVICE: 'searchService',
|
|
152
|
+
MCP_SERVICE: 'mcpService',
|
|
153
|
+
EMBEDDING_PROVIDER: 'embeddingProvider',
|
|
154
|
+
TARGET_MANAGER: 'targetManager',
|
|
155
|
+
} as const;
|