tracelattice 1.2.5
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/LICENSE +24 -0
- package/README.md +112 -0
- package/dist/ServerConfig.d.ts +229 -0
- package/dist/ServerConfig.d.ts.map +1 -0
- package/dist/ServerConfig.js +121 -0
- package/dist/ServerConfig.js.map +1 -0
- package/dist/__tests__/base-registry.test.d.ts +2 -0
- package/dist/__tests__/base-registry.test.d.ts.map +1 -0
- package/dist/__tests__/base-transport-cov.test.d.ts +2 -0
- package/dist/__tests__/base-transport-cov.test.d.ts.map +1 -0
- package/dist/__tests__/base-transport.test.d.ts +2 -0
- package/dist/__tests__/base-transport.test.d.ts.map +1 -0
- package/dist/__tests__/config-loader.test.d.ts +2 -0
- package/dist/__tests__/config-loader.test.d.ts.map +1 -0
- package/dist/__tests__/connection-pool-cov.test.d.ts +2 -0
- package/dist/__tests__/connection-pool-cov.test.d.ts.map +1 -0
- package/dist/__tests__/connection-pool.test.d.ts +2 -0
- package/dist/__tests__/connection-pool.test.d.ts.map +1 -0
- package/dist/__tests__/container.test.d.ts +2 -0
- package/dist/__tests__/container.test.d.ts.map +1 -0
- package/dist/__tests__/crud.test.d.ts +2 -0
- package/dist/__tests__/crud.test.d.ts.map +1 -0
- package/dist/__tests__/discovery-cache.test.d.ts +2 -0
- package/dist/__tests__/discovery-cache.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test.d.ts +2 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/factories.test.d.ts +2 -0
- package/dist/__tests__/factories.test.d.ts.map +1 -0
- package/dist/__tests__/health-checker-cov.test.d.ts +2 -0
- package/dist/__tests__/health-checker-cov.test.d.ts.map +1 -0
- package/dist/__tests__/health-checker.test.d.ts +2 -0
- package/dist/__tests__/health-checker.test.d.ts.map +1 -0
- package/dist/__tests__/helpers/factories.d.ts +36 -0
- package/dist/__tests__/helpers/factories.d.ts.map +1 -0
- package/dist/__tests__/helpers/index.d.ts +3 -0
- package/dist/__tests__/helpers/index.d.ts.map +1 -0
- package/dist/__tests__/helpers/timers.d.ts +4 -0
- package/dist/__tests__/helpers/timers.d.ts.map +1 -0
- package/dist/__tests__/history-manager.test.d.ts +2 -0
- package/dist/__tests__/history-manager.test.d.ts.map +1 -0
- package/dist/__tests__/http-helpers-cov.test.d.ts +2 -0
- package/dist/__tests__/http-helpers-cov.test.d.ts.map +1 -0
- package/dist/__tests__/http-transport-cov.test.d.ts +2 -0
- package/dist/__tests__/http-transport-cov.test.d.ts.map +1 -0
- package/dist/__tests__/http-transport.test.d.ts +2 -0
- package/dist/__tests__/http-transport.test.d.ts.map +1 -0
- package/dist/__tests__/input-normalizer.test.d.ts +8 -0
- package/dist/__tests__/input-normalizer.test.d.ts.map +1 -0
- package/dist/__tests__/integration.test.d.ts +2 -0
- package/dist/__tests__/integration.test.d.ts.map +1 -0
- package/dist/__tests__/lib-server.test.d.ts +2 -0
- package/dist/__tests__/lib-server.test.d.ts.map +1 -0
- package/dist/__tests__/memory-persistence.test.d.ts +2 -0
- package/dist/__tests__/memory-persistence.test.d.ts.map +1 -0
- package/dist/__tests__/metrics-integration.test.d.ts +2 -0
- package/dist/__tests__/metrics-integration.test.d.ts.map +1 -0
- package/dist/__tests__/persistence.test.d.ts +2 -0
- package/dist/__tests__/persistence.test.d.ts.map +1 -0
- package/dist/__tests__/reasoning-integration.test.d.ts +11 -0
- package/dist/__tests__/reasoning-integration.test.d.ts.map +1 -0
- package/dist/__tests__/reasoning-types.test.d.ts +2 -0
- package/dist/__tests__/reasoning-types.test.d.ts.map +1 -0
- package/dist/__tests__/request-context.test.d.ts +2 -0
- package/dist/__tests__/request-context.test.d.ts.map +1 -0
- package/dist/__tests__/sanitize.test.d.ts +2 -0
- package/dist/__tests__/sanitize.test.d.ts.map +1 -0
- package/dist/__tests__/schema.test.d.ts +2 -0
- package/dist/__tests__/schema.test.d.ts.map +1 -0
- package/dist/__tests__/sequentialthinking-tools.test.d.ts +2 -0
- package/dist/__tests__/sequentialthinking-tools.test.d.ts.map +1 -0
- package/dist/__tests__/server-config.test.d.ts +2 -0
- package/dist/__tests__/server-config.test.d.ts.map +1 -0
- package/dist/__tests__/skill-discovery.test.d.ts +2 -0
- package/dist/__tests__/skill-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/skill-registry.test.d.ts +2 -0
- package/dist/__tests__/skill-registry.test.d.ts.map +1 -0
- package/dist/__tests__/skill-watcher.test.d.ts +2 -0
- package/dist/__tests__/skill-watcher.test.d.ts.map +1 -0
- package/dist/__tests__/sqlite-persistence.test.d.ts +2 -0
- package/dist/__tests__/sqlite-persistence.test.d.ts.map +1 -0
- package/dist/__tests__/sse-transport-cov.test.d.ts +2 -0
- package/dist/__tests__/sse-transport-cov.test.d.ts.map +1 -0
- package/dist/__tests__/sse-transport.test.d.ts +2 -0
- package/dist/__tests__/sse-transport.test.d.ts.map +1 -0
- package/dist/__tests__/streamable-http-cov.test.d.ts +2 -0
- package/dist/__tests__/streamable-http-cov.test.d.ts.map +1 -0
- package/dist/__tests__/streamable-http-transport.test.d.ts +2 -0
- package/dist/__tests__/streamable-http-transport.test.d.ts.map +1 -0
- package/dist/__tests__/structured-logger.test.d.ts +2 -0
- package/dist/__tests__/structured-logger.test.d.ts.map +1 -0
- package/dist/__tests__/thought-evaluator.test.d.ts +2 -0
- package/dist/__tests__/thought-evaluator.test.d.ts.map +1 -0
- package/dist/__tests__/thought-formatter.test.d.ts +2 -0
- package/dist/__tests__/thought-formatter.test.d.ts.map +1 -0
- package/dist/__tests__/thought-processor.test.d.ts +8 -0
- package/dist/__tests__/thought-processor.test.d.ts.map +1 -0
- package/dist/__tests__/tool-registry-cov.test.d.ts +2 -0
- package/dist/__tests__/tool-registry-cov.test.d.ts.map +1 -0
- package/dist/__tests__/tool-registry.test.d.ts +2 -0
- package/dist/__tests__/tool-registry.test.d.ts.map +1 -0
- package/dist/__tests__/tool-watcher.test.d.ts +2 -0
- package/dist/__tests__/tool-watcher.test.d.ts.map +1 -0
- package/dist/__tests__/worker-manager-cov.test.d.ts +2 -0
- package/dist/__tests__/worker-manager-cov.test.d.ts.map +1 -0
- package/dist/__tests__/worker-manager.test.d.ts +2 -0
- package/dist/__tests__/worker-manager.test.d.ts.map +1 -0
- package/dist/cache/DiscoveryCache.d.ts +269 -0
- package/dist/cache/DiscoveryCache.d.ts.map +1 -0
- package/dist/cache/DiscoveryCache.js +100 -0
- package/dist/cache/DiscoveryCache.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +114 -0
- package/dist/cli.js.map +1 -0
- package/dist/cluster/WorkerManager.d.ts +166 -0
- package/dist/cluster/WorkerManager.d.ts.map +1 -0
- package/dist/cluster/WorkerManager.js +202 -0
- package/dist/cluster/WorkerManager.js.map +1 -0
- package/dist/cluster/worker.d.ts +11 -0
- package/dist/cluster/worker.d.ts.map +1 -0
- package/dist/cluster/worker.js +36 -0
- package/dist/cluster/worker.js.map +1 -0
- package/dist/config/ConfigLoader.d.ts +224 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +85 -0
- package/dist/config/ConfigLoader.js.map +1 -0
- package/dist/context/RequestContext.d.ts +61 -0
- package/dist/context/RequestContext.d.ts.map +1 -0
- package/dist/context/RequestContext.js +17 -0
- package/dist/context/RequestContext.js.map +1 -0
- package/dist/contracts/index.d.ts +10 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/interfaces.d.ts +107 -0
- package/dist/contracts/interfaces.d.ts.map +1 -0
- package/dist/contracts/interfaces.js +1 -0
- package/dist/core/HistoryManager.d.ts +514 -0
- package/dist/core/HistoryManager.d.ts.map +1 -0
- package/dist/core/HistoryManager.js +331 -0
- package/dist/core/HistoryManager.js.map +1 -0
- package/dist/core/IHistoryManager.d.ts +100 -0
- package/dist/core/IHistoryManager.d.ts.map +1 -0
- package/dist/core/IHistoryManager.js +1 -0
- package/dist/core/InputNormalizer.d.ts +139 -0
- package/dist/core/InputNormalizer.d.ts.map +1 -0
- package/dist/core/InputNormalizer.js +101 -0
- package/dist/core/InputNormalizer.js.map +1 -0
- package/dist/core/ThoughtEvaluator.d.ts +127 -0
- package/dist/core/ThoughtEvaluator.d.ts.map +1 -0
- package/dist/core/ThoughtEvaluator.js +346 -0
- package/dist/core/ThoughtEvaluator.js.map +1 -0
- package/dist/core/ThoughtFormatter.d.ts +133 -0
- package/dist/core/ThoughtFormatter.d.ts.map +1 -0
- package/dist/core/ThoughtFormatter.js +70 -0
- package/dist/core/ThoughtFormatter.js.map +1 -0
- package/dist/core/ThoughtProcessor.d.ts +218 -0
- package/dist/core/ThoughtProcessor.d.ts.map +1 -0
- package/dist/core/ThoughtProcessor.js +205 -0
- package/dist/core/ThoughtProcessor.js.map +1 -0
- package/dist/core/reasoning.d.ts +169 -0
- package/dist/core/reasoning.d.ts.map +1 -0
- package/dist/core/reasoning.js +1 -0
- package/dist/core/step.d.ts +45 -0
- package/dist/core/step.d.ts.map +1 -0
- package/dist/core/step.js +1 -0
- package/dist/core/thought.d.ts +190 -0
- package/dist/core/thought.d.ts.map +1 -0
- package/dist/core/thought.js +1 -0
- package/dist/di/Container.d.ts +226 -0
- package/dist/di/Container.d.ts.map +1 -0
- package/dist/di/Container.js +96 -0
- package/dist/di/Container.js.map +1 -0
- package/dist/di/ServiceRegistry.d.ts +32 -0
- package/dist/di/ServiceRegistry.d.ts.map +1 -0
- package/dist/di/ServiceRegistry.js +1 -0
- package/dist/errors.d.ts +482 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +108 -0
- package/dist/errors.js.map +1 -0
- package/dist/health/HealthChecker.d.ts +73 -0
- package/dist/health/HealthChecker.d.ts.map +1 -0
- package/dist/health/HealthChecker.js +69 -0
- package/dist/health/HealthChecker.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/lib.d.ts +205 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +219 -0
- package/dist/lib.js.map +1 -0
- package/dist/logger/NullLogger.d.ts +154 -0
- package/dist/logger/NullLogger.d.ts.map +1 -0
- package/dist/logger/NullLogger.js +24 -0
- package/dist/logger/NullLogger.js.map +1 -0
- package/dist/logger/StructuredLogger.d.ts +327 -0
- package/dist/logger/StructuredLogger.d.ts.map +1 -0
- package/dist/logger/StructuredLogger.js +72 -0
- package/dist/logger/StructuredLogger.js.map +1 -0
- package/dist/metrics/__tests__/metrics.test.d.ts +2 -0
- package/dist/metrics/__tests__/metrics.test.d.ts.map +1 -0
- package/dist/metrics/metrics.impl.d.ts +252 -0
- package/dist/metrics/metrics.impl.d.ts.map +1 -0
- package/dist/metrics/metrics.impl.js +197 -0
- package/dist/metrics/metrics.impl.js.map +1 -0
- package/dist/persistence/FilePersistence.d.ts +66 -0
- package/dist/persistence/FilePersistence.d.ts.map +1 -0
- package/dist/persistence/FilePersistence.js +132 -0
- package/dist/persistence/FilePersistence.js.map +1 -0
- package/dist/persistence/MemoryPersistence.d.ts +68 -0
- package/dist/persistence/MemoryPersistence.d.ts.map +1 -0
- package/dist/persistence/MemoryPersistence.js +51 -0
- package/dist/persistence/MemoryPersistence.js.map +1 -0
- package/dist/persistence/PersistenceBackend.d.ts +69 -0
- package/dist/persistence/PersistenceBackend.d.ts.map +1 -0
- package/dist/persistence/PersistenceBackend.js +1 -0
- package/dist/persistence/PersistenceFactory.d.ts +21 -0
- package/dist/persistence/PersistenceFactory.d.ts.map +1 -0
- package/dist/persistence/PersistenceFactory.js +25 -0
- package/dist/persistence/PersistenceFactory.js.map +1 -0
- package/dist/persistence/SqlitePersistence.d.ts +60 -0
- package/dist/persistence/SqlitePersistence.d.ts.map +1 -0
- package/dist/persistence/SqlitePersistence.js +136 -0
- package/dist/persistence/SqlitePersistence.js.map +1 -0
- package/dist/pool/ConnectionPool.d.ts +215 -0
- package/dist/pool/ConnectionPool.d.ts.map +1 -0
- package/dist/pool/ConnectionPool.js +187 -0
- package/dist/pool/ConnectionPool.js.map +1 -0
- package/dist/registry/BaseRegistry.d.ts +203 -0
- package/dist/registry/BaseRegistry.d.ts.map +1 -0
- package/dist/registry/BaseRegistry.js +165 -0
- package/dist/registry/BaseRegistry.js.map +1 -0
- package/dist/registry/SkillRegistry.d.ts +69 -0
- package/dist/registry/SkillRegistry.d.ts.map +1 -0
- package/dist/registry/SkillRegistry.js +88 -0
- package/dist/registry/SkillRegistry.js.map +1 -0
- package/dist/registry/ToolRegistry.d.ts +69 -0
- package/dist/registry/ToolRegistry.d.ts.map +1 -0
- package/dist/registry/ToolRegistry.js +93 -0
- package/dist/registry/ToolRegistry.js.map +1 -0
- package/dist/sanitize.d.ts +63 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +14 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/schema.d.ts +531 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +204 -0
- package/dist/schema.js.map +1 -0
- package/dist/telemetry/Telemetry.d.ts +36 -0
- package/dist/telemetry/Telemetry.d.ts.map +1 -0
- package/dist/telemetry/Telemetry.js +68 -0
- package/dist/telemetry/Telemetry.js.map +1 -0
- package/dist/telemetry/__tests__/Telemetry.test.d.ts +2 -0
- package/dist/telemetry/__tests__/Telemetry.test.d.ts.map +1 -0
- package/dist/transport/BaseTransport.d.ts +184 -0
- package/dist/transport/BaseTransport.d.ts.map +1 -0
- package/dist/transport/BaseTransport.js +200 -0
- package/dist/transport/BaseTransport.js.map +1 -0
- package/dist/transport/HttpHelpers.d.ts +60 -0
- package/dist/transport/HttpHelpers.d.ts.map +1 -0
- package/dist/transport/HttpHelpers.js +50 -0
- package/dist/transport/HttpHelpers.js.map +1 -0
- package/dist/transport/HttpTransport.d.ts +134 -0
- package/dist/transport/HttpTransport.d.ts.map +1 -0
- package/dist/transport/HttpTransport.js +175 -0
- package/dist/transport/HttpTransport.js.map +1 -0
- package/dist/transport/SseTransport.d.ts +133 -0
- package/dist/transport/SseTransport.d.ts.map +1 -0
- package/dist/transport/SseTransport.js +318 -0
- package/dist/transport/SseTransport.js.map +1 -0
- package/dist/transport/StreamableHttpTransport.d.ts +224 -0
- package/dist/transport/StreamableHttpTransport.d.ts.map +1 -0
- package/dist/transport/StreamableHttpTransport.js +407 -0
- package/dist/transport/StreamableHttpTransport.js.map +1 -0
- package/dist/types/disposable.d.ts +22 -0
- package/dist/types/disposable.d.ts.map +1 -0
- package/dist/types/disposable.js +1 -0
- package/dist/types/server-config.d.ts +32 -0
- package/dist/types/server-config.d.ts.map +1 -0
- package/dist/types/server-config.js +1 -0
- package/dist/types/skill.d.ts +69 -0
- package/dist/types/skill.d.ts.map +1 -0
- package/dist/types/skill.js +1 -0
- package/dist/types/tool.d.ts +68 -0
- package/dist/types/tool.d.ts.map +1 -0
- package/dist/types/tool.js +1 -0
- package/dist/watchers/SkillWatcher.d.ts +132 -0
- package/dist/watchers/SkillWatcher.d.ts.map +1 -0
- package/dist/watchers/SkillWatcher.js +73 -0
- package/dist/watchers/SkillWatcher.js.map +1 -0
- package/dist/watchers/ToolWatcher.d.ts +109 -0
- package/dist/watchers/ToolWatcher.d.ts.map +1 -0
- package/dist/watchers/ToolWatcher.js +71 -0
- package/dist/watchers/ToolWatcher.js.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence/SqlitePersistence.js","sources":["../../src/persistence/SqlitePersistence.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { ThoughtData } from '../core/thought.js';\nimport type { PersistenceBackend, PersistenceConfig } from './PersistenceBackend.js';\n\n/**\n * Type definition for the better-sqlite3 Database interface.\n * This allows us to use the library without importing it directly.\n */\ninterface Database {\n\texec(sql: string): void;\n\tprepare(sql: string): Statement;\n\tclose(): void;\n\tpragma(pragma: string): unknown;\n}\n\ninterface Statement {\n\trun(...params: unknown[]): RunResult;\n\tget(...params: unknown[]): unknown;\n\tall(...params: unknown[]): unknown[];\n}\n\ninterface RunResult {\n\tchanges: number;\n\tlastInsertRowid: number;\n}\n\n/**\n * SQLite-based persistence backend.\n *\n * Provides efficient, transactional persistence using SQLite.\n * Requires the 'better-sqlite3' package to be installed.\n *\n * @example\n * ```typescript\n * const backend = await SqlitePersistence.create({\n * dbPath: './data/history.db',\n * enableWAL: true\n * });\n * ```\n */\nexport class SqlitePersistence implements PersistenceBackend {\n\tprivate _db: Database;\n\tprivate _maxHistorySize: number;\n\tprivate _persistBranches: boolean;\n\n\tprivate constructor(db: Database, options: PersistenceConfig['options']) {\n\t\tthis._db = db;\n\t\tthis._maxHistorySize = options?.maxHistorySize ?? 10000;\n\t\tthis._persistBranches = options?.persistBranches ?? true;\n\t\tthis._initializeSchema();\n\t}\n\n\t/**\n\t * Creates a new SqlitePersistence instance with dynamic import of better-sqlite3.\n\t *\n\t * @param options - Configuration options\n\t * @returns A Promise that resolves to a SqlitePersistence instance\n\t * @throws Error if better-sqlite3 is not installed\n\t *\n\t * @example\n\t * ```typescript\n\t * const backend = await SqlitePersistence.create({\n\t * dbPath: './data/history.db',\n\t * enableWAL: true\n\t * });\n\t * ```\n\t */\n\tstatic async create(options?: PersistenceConfig['options']): Promise<SqlitePersistence> {\n\t\t// Default to .claude/data in current directory or home directory\n\t\tconst defaultDataDir = existsSync('.claude/data')\n\t\t\t? '.claude/data'\n\t\t\t: join(homedir(), '.claude/data');\n\t\tconst dbPath = options?.dbPath ?? join(defaultDataDir, 'history.db');\n\n\t\t// Load better-sqlite3 dynamically (optional dependency)\n\t\tlet Database: new (path: string) => Database;\n\t\ttry {\n\t\t\tconst module = await import('better-sqlite3');\n\t\t\tDatabase = module.default;\n\t\t} catch {\n\t\t\tthrow new Error(\n\t\t\t\t`SQLite persistence requires 'better-sqlite3' package. Install it with: npm install better-sqlite3`\n\t\t\t);\n\t\t}\n\n\t\tconst db = new Database(dbPath);\n\n\t\t// Enable WAL mode for better concurrency if specified\n\t\tif (options?.enableWAL !== false) {\n\t\t\tdb.pragma('journal_mode = WAL');\n\t\t}\n\n\t\t// Performance and safety PRAGMAs\n\t\tdb.pragma('synchronous = NORMAL');\n\t\tdb.pragma('foreign_keys = ON');\n\t\tdb.pragma('busy_timeout = 5000');\n\t\tdb.pragma('cache_size = -64000'); // 64MB\n\t\tdb.pragma('temp_store = MEMORY');\n\n\t\treturn new SqlitePersistence(db, options);\n\t}\n\n\tprivate _initializeSchema(): void {\n\t\t// Create thoughts table\n\t\tthis._db.exec(`\n CREATE TABLE IF NOT EXISTS thoughts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n data TEXT NOT NULL,\n created_at INTEGER DEFAULT (strftime('%s', 'now'))\n )\n `);\n\n\t\t// Create branches table\n\t\tif (this._persistBranches) {\n\t\t\tthis._db.exec(`\n CREATE TABLE IF NOT EXISTS branches (\n branch_id TEXT PRIMARY KEY,\n data TEXT NOT NULL,\n updated_at INTEGER DEFAULT (strftime('%s', 'now'))\n )\n `);\n\t\t}\n\n\t\t// Create indexes for better performance\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_thoughts_created_at ON thoughts(created_at)\n `);\n\t}\n\n\tpublic async saveThought(thought: ThoughtData): Promise<void> {\n\t\tconst stmt = this._db.prepare('INSERT INTO thoughts (data) VALUES (?)');\n\t\tstmt.run(JSON.stringify(thought));\n\n\t\t// Trim old thoughts if over limit\n\t\tconst countStmt = this._db.prepare('SELECT COUNT(*) as count FROM thoughts');\n\t\tconst { count } = countStmt.get() as { count: number };\n\n\t\tif (count > this._maxHistorySize) {\n\t\t\tconst deleteStmt = this._db.prepare(\n\t\t\t\t`DELETE FROM thoughts WHERE id IN (\n SELECT id FROM thoughts ORDER BY id ASC LIMIT ?\n )`\n\t\t\t);\n\t\t\tdeleteStmt.run(count - this._maxHistorySize);\n\t\t}\n\t}\n\n\tpublic async loadHistory(): Promise<ThoughtData[]> {\n\t\tconst stmt = this._db.prepare('SELECT data FROM thoughts ORDER BY id ASC');\n\t\tconst rows = stmt.all() as { data: string }[];\n\n\t\treturn rows\n\t\t\t.map((row) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.parse(row.data) as ThoughtData;\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t})\n\t\t\t.filter((t): t is ThoughtData => t !== null);\n\t}\n\n\tpublic async saveBranch(branchId: string, thoughts: ThoughtData[]): Promise<void> {\n\t\tif (!this._persistBranches) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst stmt = this._db.prepare(\n\t\t\t'INSERT OR REPLACE INTO branches (branch_id, data, updated_at) VALUES (?, ?, strftime(\"%s\", \"now\"))'\n\t\t);\n\t\tstmt.run(branchId, JSON.stringify(thoughts));\n\t}\n\n\tpublic async loadBranch(branchId: string): Promise<ThoughtData[] | undefined> {\n\t\tif (!this._persistBranches) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst stmt = this._db.prepare('SELECT data FROM branches WHERE branch_id = ?');\n\t\tconst row = stmt.get(branchId) as { data: string } | undefined;\n\n\t\tif (!row) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\ttry {\n\t\t\tconst data = JSON.parse(row.data) as ThoughtData[];\n\t\t\treturn Array.isArray(data) ? data : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tpublic async listBranches(): Promise<string[]> {\n\t\tif (!this._persistBranches) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst stmt = this._db.prepare('SELECT branch_id FROM branches ORDER BY branch_id ASC');\n\t\tconst rows = stmt.all() as { branch_id: string }[];\n\t\treturn rows.map((row) => row.branch_id);\n\t}\n\n\tpublic async clear(): Promise<void> {\n\t\tthis._db.exec('DELETE FROM thoughts');\n\t\tif (this._persistBranches) {\n\t\t\tthis._db.exec('DELETE FROM branches');\n\t\t}\n\t}\n\n\tpublic async healthy(): Promise<boolean> {\n\t\ttry {\n\t\t\t// Simple health check - try to execute a query\n\t\t\tthis._db.prepare('SELECT 1').get();\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Close the database connection with proper cleanup.\n\t * Runs WAL checkpoint before closing to ensure all data is persisted.\n\t */\n\tpublic async close(): Promise<void> {\n\t\tif (this._db) {\n\t\t\ttry {\n\t\t\t\t// Run WAL checkpoint to ensure all data is persisted\n\t\t\t\tthis._db.pragma('wal_checkpoint(TRUNCATE)');\n\t\t\t} catch {\n\t\t\t\t// Ignore checkpoint errors - still try to close\n\t\t\t}\n\t\t\tthis._db.close();\n\t\t}\n\t}\n\n\t/**\n\t * Get statistics about the persisted data.\n\t */\n\tpublic getStats(): {\n\t\tthoughtCount: number;\n\t\tbranchCount: number;\n\t\tdbSize: number;\n\t} {\n\t\tconst thoughtStmt = this._db.prepare('SELECT COUNT(*) as count FROM thoughts');\n\t\tconst { count: thoughtCount } = thoughtStmt.get() as { count: number };\n\n\t\tlet branchCount = 0;\n\t\tif (this._persistBranches) {\n\t\t\tconst branchStmt = this._db.prepare('SELECT COUNT(*) as count FROM branches');\n\t\t\tconst result = branchStmt.get() as { count: number } | undefined;\n\t\t\tbranchCount = result?.count ?? 0;\n\t\t}\n\n\t\treturn {\n\t\t\tthoughtCount,\n\t\t\tbranchCount,\n\t\t\tdbSize: 0, // Would need to check file size\n\t\t};\n\t}\n}\n"],"names":["SqlitePersistence","db","options","defaultDataDir","existsSync","join","homedir","dbPath","Database","module","Error","thought","stmt","JSON","countStmt","count","deleteStmt","rows","row","t","branchId","thoughts","data","Array","undefined","thoughtStmt","thoughtCount","branchCount","branchStmt","result"],"mappings":"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"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection Pool for managing concurrent user sessions.
|
|
3
|
+
*
|
|
4
|
+
* This module provides session management for multi-user scenarios,
|
|
5
|
+
* allowing multiple concurrent clients to each have isolated state.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const pool = new ConnectionPool({
|
|
10
|
+
* maxSessions: 100,
|
|
11
|
+
* sessionTimeout: 300000 // 5 minutes
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* const sessionId = await pool.createSession();
|
|
15
|
+
* await pool.process(sessionId, thought);
|
|
16
|
+
* await pool.closeSession(sessionId);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import type { ThoughtData } from '../core/thought.js';
|
|
20
|
+
import type { Logger } from '../logger/StructuredLogger.js';
|
|
21
|
+
import type { IDisposable } from '../types/disposable.js';
|
|
22
|
+
export interface SessionOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Maximum number of concurrent sessions
|
|
25
|
+
* @default 100
|
|
26
|
+
*/
|
|
27
|
+
maxSessions?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Logger instance
|
|
30
|
+
*/
|
|
31
|
+
logger?: Logger;
|
|
32
|
+
serverFactory?: () => Promise<SessionServer>;
|
|
33
|
+
/**
|
|
34
|
+
* Session timeout in milliseconds
|
|
35
|
+
* @default 300000 (5 minutes)
|
|
36
|
+
*/
|
|
37
|
+
sessionTimeout?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Whether to enable automatic session cleanup
|
|
40
|
+
* @default true
|
|
41
|
+
*/
|
|
42
|
+
autoCleanup?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Cleanup interval in milliseconds
|
|
45
|
+
* @default 60000 (1 minute)
|
|
46
|
+
*/
|
|
47
|
+
cleanupInterval?: number;
|
|
48
|
+
}
|
|
49
|
+
export interface SessionInfo {
|
|
50
|
+
id: string;
|
|
51
|
+
server: SessionServer;
|
|
52
|
+
createdAt: number;
|
|
53
|
+
lastActivityAt: number;
|
|
54
|
+
isActive: boolean;
|
|
55
|
+
}
|
|
56
|
+
export interface SessionServer {
|
|
57
|
+
processThought(input: ThoughtData): Promise<ProcessResult>;
|
|
58
|
+
stop(): void | Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
export interface ProcessResult {
|
|
61
|
+
content: Array<{
|
|
62
|
+
type: string;
|
|
63
|
+
text: string;
|
|
64
|
+
}>;
|
|
65
|
+
isError?: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Represents a user session with its own server instance.
|
|
69
|
+
*/
|
|
70
|
+
export declare class Session {
|
|
71
|
+
private _server;
|
|
72
|
+
private _id;
|
|
73
|
+
private _createdAt;
|
|
74
|
+
private _lastActivityAt;
|
|
75
|
+
private _isActiveValue;
|
|
76
|
+
private _timeout;
|
|
77
|
+
private _cleanupTimer;
|
|
78
|
+
private _logger;
|
|
79
|
+
constructor(id: string, server: SessionServer, timeout: number, logger: Logger);
|
|
80
|
+
/**
|
|
81
|
+
* Check if the session is active.
|
|
82
|
+
*/
|
|
83
|
+
get isActive(): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Process a thought through this session's server instance.
|
|
86
|
+
*/
|
|
87
|
+
process(input: ThoughtData): Promise<ProcessResult>;
|
|
88
|
+
/**
|
|
89
|
+
* Get session information.
|
|
90
|
+
*/
|
|
91
|
+
getInfo(): SessionInfo;
|
|
92
|
+
/**
|
|
93
|
+
* Check if the session has timed out.
|
|
94
|
+
*/
|
|
95
|
+
isTimedOut(): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Close the session and stop the server.
|
|
98
|
+
*/
|
|
99
|
+
close(): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Start the session timeout timer.
|
|
102
|
+
*/
|
|
103
|
+
private _startTimeout;
|
|
104
|
+
/**
|
|
105
|
+
* Reset the timeout timer after activity.
|
|
106
|
+
*/
|
|
107
|
+
private _resetTimeout;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* ConnectionPool manages multiple concurrent user sessions.
|
|
111
|
+
*
|
|
112
|
+
* Each session has its own server instance with isolated state,
|
|
113
|
+
* allowing multiple users to interact with the system simultaneously.
|
|
114
|
+
*/
|
|
115
|
+
export declare class ConnectionPool implements IDisposable {
|
|
116
|
+
private _sessions;
|
|
117
|
+
private _createSessionLock;
|
|
118
|
+
private _maxSessions;
|
|
119
|
+
private _sessionTimeout;
|
|
120
|
+
private _autoCleanup;
|
|
121
|
+
private _cleanupInterval;
|
|
122
|
+
private _cleanupTimerId;
|
|
123
|
+
private _terminated;
|
|
124
|
+
private _logger;
|
|
125
|
+
private _serverFactory;
|
|
126
|
+
constructor(options?: SessionOptions);
|
|
127
|
+
/**
|
|
128
|
+
* Create a no-op logger when none is provided.
|
|
129
|
+
*/
|
|
130
|
+
private _createNoopLogger;
|
|
131
|
+
/**
|
|
132
|
+
* Create a new session.
|
|
133
|
+
*
|
|
134
|
+
* @returns The session ID
|
|
135
|
+
* @throws Error if max sessions reached
|
|
136
|
+
*/
|
|
137
|
+
createSession(): Promise<string>;
|
|
138
|
+
/**
|
|
139
|
+
* Process a thought in the specified session.
|
|
140
|
+
*
|
|
141
|
+
* @param sessionId - The session ID
|
|
142
|
+
* @param input - The thought data to process
|
|
143
|
+
* @returns Promise with the processing result
|
|
144
|
+
* @throws Error if session not found
|
|
145
|
+
*/
|
|
146
|
+
process(sessionId: string, input: ThoughtData): Promise<ProcessResult>;
|
|
147
|
+
/**
|
|
148
|
+
* Close a session and release resources.
|
|
149
|
+
*
|
|
150
|
+
* @param sessionId - The session ID to close
|
|
151
|
+
* @throws Error if session not found
|
|
152
|
+
*/
|
|
153
|
+
closeSession(sessionId: string): Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Get information about a session.
|
|
156
|
+
*
|
|
157
|
+
* @param sessionId - The session ID
|
|
158
|
+
* @returns Session info or undefined if not found
|
|
159
|
+
*/
|
|
160
|
+
getSessionInfo(sessionId: string): SessionInfo | undefined;
|
|
161
|
+
/**
|
|
162
|
+
* Get all active sessions.
|
|
163
|
+
*
|
|
164
|
+
* @returns Array of session information
|
|
165
|
+
*/
|
|
166
|
+
getActiveSessions(): SessionInfo[];
|
|
167
|
+
/**
|
|
168
|
+
* Get connection pool statistics.
|
|
169
|
+
*/
|
|
170
|
+
getStats(): {
|
|
171
|
+
totalSessions: number;
|
|
172
|
+
activeSessions: number;
|
|
173
|
+
maxSessions: number;
|
|
174
|
+
cleanupEnabled: boolean;
|
|
175
|
+
sessionTimeout: number;
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Start the automatic cleanup timer.
|
|
179
|
+
*/
|
|
180
|
+
private _startCleanup;
|
|
181
|
+
/**
|
|
182
|
+
* Remove timed-out sessions.
|
|
183
|
+
*/
|
|
184
|
+
private _cleanupTimedOutSessions;
|
|
185
|
+
/**
|
|
186
|
+
* Close all sessions and stop the cleanup timer.
|
|
187
|
+
*/
|
|
188
|
+
terminate(): Promise<void>;
|
|
189
|
+
/**
|
|
190
|
+
* Dispose of the connection pool, releasing all resources.
|
|
191
|
+
* Implements the IDisposable interface.
|
|
192
|
+
* Delegates to terminate() for backward compatibility.
|
|
193
|
+
*/
|
|
194
|
+
dispose(): Promise<void>;
|
|
195
|
+
/**
|
|
196
|
+
* Check if the connection pool is active.
|
|
197
|
+
*/
|
|
198
|
+
isRunning(): boolean;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Create a connection pool with the given options.
|
|
202
|
+
*
|
|
203
|
+
* @param options - Connection pool configuration
|
|
204
|
+
* @returns A configured connection pool
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* const pool = createConnectionPool({
|
|
209
|
+
* maxSessions: 50,
|
|
210
|
+
* sessionTimeout: 300000
|
|
211
|
+
* });
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export declare function createConnectionPool(options?: SessionOptions): ConnectionPool;
|
|
215
|
+
//# sourceMappingURL=ConnectionPool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConnectionPool.d.ts","sourceRoot":"","sources":["../../src/pool/ConnectionPool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { MaxSessionsReachedError, PoolTerminatedError, SessionNotActiveError, SessionNotFoundError } from "../errors.js";
|
|
2
|
+
class Session {
|
|
3
|
+
_server;
|
|
4
|
+
_id;
|
|
5
|
+
_createdAt;
|
|
6
|
+
_lastActivityAt;
|
|
7
|
+
_isActiveValue;
|
|
8
|
+
_timeout;
|
|
9
|
+
_cleanupTimer = null;
|
|
10
|
+
_logger;
|
|
11
|
+
constructor(id, server, timeout, logger){
|
|
12
|
+
this._server = server;
|
|
13
|
+
this._id = id;
|
|
14
|
+
this._createdAt = Date.now();
|
|
15
|
+
this._lastActivityAt = this._createdAt;
|
|
16
|
+
this._isActiveValue = true;
|
|
17
|
+
this._timeout = timeout;
|
|
18
|
+
this._logger = logger;
|
|
19
|
+
this._startTimeout();
|
|
20
|
+
}
|
|
21
|
+
get isActive() {
|
|
22
|
+
return this._isActiveValue;
|
|
23
|
+
}
|
|
24
|
+
async process(input) {
|
|
25
|
+
if (!this.isActive) throw new SessionNotActiveError(this._id);
|
|
26
|
+
this._lastActivityAt = Date.now();
|
|
27
|
+
this._resetTimeout();
|
|
28
|
+
return this._server.processThought(input);
|
|
29
|
+
}
|
|
30
|
+
getInfo() {
|
|
31
|
+
return {
|
|
32
|
+
id: this._id,
|
|
33
|
+
server: this._server,
|
|
34
|
+
createdAt: this._createdAt,
|
|
35
|
+
lastActivityAt: this._lastActivityAt,
|
|
36
|
+
isActive: this.isActive
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
isTimedOut() {
|
|
40
|
+
return Date.now() - this._lastActivityAt > this._timeout;
|
|
41
|
+
}
|
|
42
|
+
async close() {
|
|
43
|
+
this._isActiveValue = false;
|
|
44
|
+
if (this._cleanupTimer) {
|
|
45
|
+
clearTimeout(this._cleanupTimer);
|
|
46
|
+
this._cleanupTimer = null;
|
|
47
|
+
}
|
|
48
|
+
this._server.stop();
|
|
49
|
+
}
|
|
50
|
+
_startTimeout() {
|
|
51
|
+
if (this._cleanupTimer) clearTimeout(this._cleanupTimer);
|
|
52
|
+
this._cleanupTimer = setTimeout(()=>{
|
|
53
|
+
if (this.isTimedOut()) {
|
|
54
|
+
this._logger.warn(`Session ${this._id} timed out, closing`);
|
|
55
|
+
this.close().catch((err)=>{
|
|
56
|
+
this._logger.error(`Error closing timed out session ${this._id}:`, err);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}, this._timeout);
|
|
60
|
+
}
|
|
61
|
+
_resetTimeout() {
|
|
62
|
+
this._startTimeout();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
class ConnectionPool {
|
|
66
|
+
_sessions = new Map();
|
|
67
|
+
_createSessionLock = null;
|
|
68
|
+
_maxSessions;
|
|
69
|
+
_sessionTimeout;
|
|
70
|
+
_autoCleanup;
|
|
71
|
+
_cleanupInterval;
|
|
72
|
+
_cleanupTimerId = null;
|
|
73
|
+
_terminated = false;
|
|
74
|
+
_logger;
|
|
75
|
+
_serverFactory;
|
|
76
|
+
constructor(options = {}){
|
|
77
|
+
this._maxSessions = options.maxSessions ?? 100;
|
|
78
|
+
this._sessionTimeout = options.sessionTimeout ?? 300000;
|
|
79
|
+
this._autoCleanup = options.autoCleanup ?? true;
|
|
80
|
+
this._cleanupInterval = options.cleanupInterval ?? 60000;
|
|
81
|
+
this._serverFactory = options.serverFactory ?? null;
|
|
82
|
+
this._logger = options.logger ?? this._createNoopLogger();
|
|
83
|
+
if (this._autoCleanup) this._startCleanup();
|
|
84
|
+
}
|
|
85
|
+
_createNoopLogger() {
|
|
86
|
+
return {
|
|
87
|
+
info: ()=>{},
|
|
88
|
+
warn: ()=>{},
|
|
89
|
+
error: ()=>{},
|
|
90
|
+
debug: ()=>{},
|
|
91
|
+
setLevel: ()=>{},
|
|
92
|
+
getLevel: ()=>'info'
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async createSession() {
|
|
96
|
+
while(this._createSessionLock)await this._createSessionLock;
|
|
97
|
+
if (this._terminated) throw new PoolTerminatedError();
|
|
98
|
+
if (this._sessions.size >= this._maxSessions) throw new MaxSessionsReachedError(this._maxSessions);
|
|
99
|
+
if (!this._serverFactory) throw new Error('ConnectionPool requires a serverFactory option to create sessions');
|
|
100
|
+
let resolveLock;
|
|
101
|
+
this._createSessionLock = new Promise((resolve)=>{
|
|
102
|
+
resolveLock = resolve;
|
|
103
|
+
});
|
|
104
|
+
try {
|
|
105
|
+
const sessionId = `session_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
106
|
+
const server = await this._serverFactory();
|
|
107
|
+
const session = new Session(sessionId, server, this._sessionTimeout, this._logger);
|
|
108
|
+
this._sessions.set(sessionId, session);
|
|
109
|
+
this._logger.info(`Created session ${sessionId} (${this._sessions.size}/${this._maxSessions} active sessions)`);
|
|
110
|
+
return sessionId;
|
|
111
|
+
} finally{
|
|
112
|
+
resolveLock();
|
|
113
|
+
this._createSessionLock = null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async process(sessionId, input) {
|
|
117
|
+
const session = this._sessions.get(sessionId);
|
|
118
|
+
if (!session) throw new SessionNotFoundError(sessionId);
|
|
119
|
+
return session.process(input);
|
|
120
|
+
}
|
|
121
|
+
async closeSession(sessionId) {
|
|
122
|
+
const session = this._sessions.get(sessionId);
|
|
123
|
+
if (!session) throw new SessionNotFoundError(sessionId);
|
|
124
|
+
await session.close();
|
|
125
|
+
this._sessions.delete(sessionId);
|
|
126
|
+
this._logger.info(`Closed session ${sessionId} (${this._sessions.size}/${this._maxSessions} active sessions)`);
|
|
127
|
+
}
|
|
128
|
+
getSessionInfo(sessionId) {
|
|
129
|
+
return this._sessions.get(sessionId)?.getInfo();
|
|
130
|
+
}
|
|
131
|
+
getActiveSessions() {
|
|
132
|
+
return Array.from(this._sessions.values()).filter((s)=>s.isActive).map((s)=>s.getInfo());
|
|
133
|
+
}
|
|
134
|
+
getStats() {
|
|
135
|
+
const activeSessions = this.getActiveSessions();
|
|
136
|
+
return {
|
|
137
|
+
totalSessions: this._sessions.size,
|
|
138
|
+
activeSessions: activeSessions.length,
|
|
139
|
+
maxSessions: this._maxSessions,
|
|
140
|
+
cleanupEnabled: this._autoCleanup,
|
|
141
|
+
sessionTimeout: this._sessionTimeout
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
_startCleanup() {
|
|
145
|
+
if (null !== this._cleanupTimerId) clearInterval(this._cleanupTimerId);
|
|
146
|
+
this._cleanupTimerId = setInterval(()=>{
|
|
147
|
+
this._cleanupTimedOutSessions();
|
|
148
|
+
}, this._cleanupInterval);
|
|
149
|
+
}
|
|
150
|
+
_cleanupTimedOutSessions() {
|
|
151
|
+
let cleaned = 0;
|
|
152
|
+
for (const [sessionId, session] of this._sessions.entries())if (session.isTimedOut()) {
|
|
153
|
+
session.close().catch((err)=>{
|
|
154
|
+
this._logger.error(`Error closing timed out session ${sessionId}:`, err);
|
|
155
|
+
});
|
|
156
|
+
this._sessions.delete(sessionId);
|
|
157
|
+
cleaned++;
|
|
158
|
+
}
|
|
159
|
+
if (cleaned > 0) this._logger.info(`Cleaned ${cleaned} timed-out sessions (${this._sessions.size}/${this._maxSessions} active sessions)`);
|
|
160
|
+
}
|
|
161
|
+
async terminate() {
|
|
162
|
+
if (this._terminated) return;
|
|
163
|
+
this._terminated = true;
|
|
164
|
+
if (null !== this._cleanupTimerId) {
|
|
165
|
+
clearInterval(this._cleanupTimerId);
|
|
166
|
+
this._cleanupTimerId = null;
|
|
167
|
+
}
|
|
168
|
+
const closePromises = Array.from(this._sessions.values()).map((session)=>session.close().catch((err)=>{
|
|
169
|
+
this._logger.error(`Error closing session ${session.getInfo().id}:`, err);
|
|
170
|
+
}));
|
|
171
|
+
await Promise.all(closePromises);
|
|
172
|
+
this._sessions.clear();
|
|
173
|
+
this._logger.info('ConnectionPool terminated');
|
|
174
|
+
}
|
|
175
|
+
async dispose() {
|
|
176
|
+
await this.terminate();
|
|
177
|
+
}
|
|
178
|
+
isRunning() {
|
|
179
|
+
return !this._terminated;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function createConnectionPool(options) {
|
|
183
|
+
return new ConnectionPool(options);
|
|
184
|
+
}
|
|
185
|
+
export { ConnectionPool, Session, createConnectionPool };
|
|
186
|
+
|
|
187
|
+
//# sourceMappingURL=ConnectionPool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool/ConnectionPool.js","sources":["../../src/pool/ConnectionPool.ts"],"sourcesContent":["/**\n * Connection Pool for managing concurrent user sessions.\n *\n * This module provides session management for multi-user scenarios,\n * allowing multiple concurrent clients to each have isolated state.\n *\n * @example\n * ```typescript\n * const pool = new ConnectionPool({\n * maxSessions: 100,\n * sessionTimeout: 300000 // 5 minutes\n * });\n *\n * const sessionId = await pool.createSession();\n * await pool.process(sessionId, thought);\n * await pool.closeSession(sessionId);\n * ```\n */\n\nimport type { ThoughtData } from '../core/thought.js';\nimport {\n\tMaxSessionsReachedError,\n\tPoolTerminatedError,\n\tSessionNotActiveError,\n\tSessionNotFoundError,\n} from '../errors.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport type { IDisposable } from '../types/disposable.js';\n\nexport interface SessionOptions {\n\t/**\n\t * Maximum number of concurrent sessions\n\t * @default 100\n\t */\n\tmaxSessions?: number;\n\n\t/**\n\t * Logger instance\n\t */\n\tlogger?: Logger;\n\n\tserverFactory?: () => Promise<SessionServer>;\n\n\t/**\n\t * Session timeout in milliseconds\n\t * @default 300000 (5 minutes)\n\t */\n\tsessionTimeout?: number;\n\n\t/**\n\t * Whether to enable automatic session cleanup\n\t * @default true\n\t */\n\tautoCleanup?: boolean;\n\n\t/**\n\t * Cleanup interval in milliseconds\n\t * @default 60000 (1 minute)\n\t */\n\tcleanupInterval?: number;\n}\n\nexport interface SessionInfo {\n\tid: string;\n\tserver: SessionServer;\n\tcreatedAt: number;\n\tlastActivityAt: number;\n\tisActive: boolean;\n}\n\nexport interface SessionServer {\n\tprocessThought(input: ThoughtData): Promise<ProcessResult>;\n\tstop(): void | Promise<void>;\n}\n\nexport interface ProcessResult {\n\tcontent: Array<{\n\t\ttype: string;\n\t\ttext: string;\n\t}>;\n\tisError?: boolean;\n}\n\n/**\n * Represents a user session with its own server instance.\n */\nexport class Session {\n\tprivate _server: SessionServer;\n\tprivate _id: string;\n\tprivate _createdAt: number;\n\tprivate _lastActivityAt: number;\n\tprivate _isActiveValue: boolean;\n\tprivate _timeout: number;\n\tprivate _cleanupTimer: NodeJS.Timeout | null = null;\n\tprivate _logger: Logger;\n\n\tconstructor(id: string, server: SessionServer, timeout: number, logger: Logger) {\n\t\tthis._server = server;\n\t\tthis._id = id;\n\t\tthis._createdAt = Date.now();\n\t\tthis._lastActivityAt = this._createdAt;\n\t\tthis._isActiveValue = true;\n\t\tthis._timeout = timeout;\n\t\tthis._logger = logger;\n\n\t\t// Start session timeout timer\n\t\tthis._startTimeout();\n\t}\n\n\t/**\n\t * Check if the session is active.\n\t */\n\tget isActive(): boolean {\n\t\treturn this._isActiveValue;\n\t}\n\n\t/**\n\t * Process a thought through this session's server instance.\n\t */\n\tasync process(input: ThoughtData): Promise<ProcessResult> {\n\t\tif (!this.isActive) {\n\t\t\tthrow new SessionNotActiveError(this._id);\n\t\t}\n\n\t\t// Update last activity\n\t\tthis._lastActivityAt = Date.now();\n\n\t\t// Reset timeout timer\n\t\tthis._resetTimeout();\n\n\t\t// Process the thought\n\t\treturn this._server.processThought(input);\n\t}\n\n\t/**\n\t * Get session information.\n\t */\n\tgetInfo(): SessionInfo {\n\t\treturn {\n\t\t\tid: this._id,\n\t\t\tserver: this._server,\n\t\t\tcreatedAt: this._createdAt,\n\t\t\tlastActivityAt: this._lastActivityAt,\n\t\t\tisActive: this.isActive,\n\t\t};\n\t}\n\n\t/**\n\t * Check if the session has timed out.\n\t */\n\tisTimedOut(): boolean {\n\t\treturn Date.now() - this._lastActivityAt > this._timeout;\n\t}\n\n\t/**\n\t * Close the session and stop the server.\n\t */\n\tasync close(): Promise<void> {\n\t\tthis._isActiveValue = false;\n\n\t\t// Stop timeout timer\n\t\tif (this._cleanupTimer) {\n\t\t\tclearTimeout(this._cleanupTimer);\n\t\t\tthis._cleanupTimer = null;\n\t\t}\n\n\t\t// Stop the server\n\t\tthis._server.stop();\n\t}\n\n\t/**\n\t * Start the session timeout timer.\n\t */\n\tprivate _startTimeout(): void {\n\t\tif (this._cleanupTimer) {\n\t\t\tclearTimeout(this._cleanupTimer);\n\t\t}\n\n\t\tthis._cleanupTimer = setTimeout(() => {\n\t\t\tif (this.isTimedOut()) {\n\t\t\t\tthis._logger.warn(`Session ${this._id} timed out, closing`);\n\t\t\t\tthis.close().catch((err) => {\n\t\t\t\t\tthis._logger.error(`Error closing timed out session ${this._id}:`, err);\n\t\t\t\t});\n\t\t\t}\n\t\t}, this._timeout);\n\t}\n\n\t/**\n\t * Reset the timeout timer after activity.\n\t */\n\tprivate _resetTimeout(): void {\n\t\tthis._startTimeout();\n\t}\n}\n\n/**\n * ConnectionPool manages multiple concurrent user sessions.\n *\n * Each session has its own server instance with isolated state,\n * allowing multiple users to interact with the system simultaneously.\n */\nexport class ConnectionPool implements IDisposable {\n\tprivate _sessions: Map<string, Session> = new Map();\n\tprivate _createSessionLock: Promise<void> | null = null;\n\tprivate _maxSessions: number;\n\tprivate _sessionTimeout: number;\n\tprivate _autoCleanup: boolean;\n\tprivate _cleanupInterval: number;\n\tprivate _cleanupTimerId: number | null = null;\n\tprivate _terminated: boolean = false;\n\tprivate _logger: Logger;\n\tprivate _serverFactory: (() => Promise<SessionServer>) | null;\n\n\tconstructor(options: SessionOptions = {}) {\n\t\tthis._maxSessions = options.maxSessions ?? 100;\n\t\tthis._sessionTimeout = options.sessionTimeout ?? 300000; // 5 minutes\n\t\tthis._autoCleanup = options.autoCleanup ?? true;\n\t\tthis._cleanupInterval = options.cleanupInterval ?? 60000; // 1 minute\n\t\tthis._serverFactory = options.serverFactory ?? null;\n\t\tthis._logger = options.logger ?? this._createNoopLogger();\n\n\t\tif (this._autoCleanup) {\n\t\t\tthis._startCleanup();\n\t\t}\n\t}\n\n\t/**\n\t * Create a no-op logger when none is provided.\n\t */\n\tprivate _createNoopLogger(): Logger {\n\t\treturn {\n\t\t\tinfo: (): void => {},\n\t\t\twarn: (): void => {},\n\t\t\terror: (): void => {},\n\t\t\tdebug: (): void => {},\n\t\t\tsetLevel: (): void => {},\n\t\t\tgetLevel: (): 'info' => 'info',\n\t\t};\n\t}\n\n\t/**\n\t * Create a new session.\n\t *\n\t * @returns The session ID\n\t * @throws Error if max sessions reached\n\t */\n\tasync createSession(): Promise<string> {\n\t\twhile (this._createSessionLock) {\n\t\t\tawait this._createSessionLock;\n\t\t}\n\n\t\tif (this._terminated) {\n\t\t\tthrow new PoolTerminatedError();\n\t\t}\n\n\t\tif (this._sessions.size >= this._maxSessions) {\n\t\t\tthrow new MaxSessionsReachedError(this._maxSessions);\n\t\t}\n\n\t\tif (!this._serverFactory) {\n\t\t\tthrow new Error('ConnectionPool requires a serverFactory option to create sessions');\n\t\t}\n\n\t\tlet resolveLock!: () => void;\n\t\tthis._createSessionLock = new Promise<void>((resolve) => {\n\t\t\tresolveLock = resolve;\n\t\t});\n\n\t\ttry {\n\t\t\t// Generate unique session ID\n\t\t\tconst sessionId = `session_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n\n\t\t\t// Create a new server instance for this session\n\t\t\tconst server = await this._serverFactory();\n\n\t\t\t// Create session\n\t\t\tconst session = new Session(sessionId, server, this._sessionTimeout, this._logger);\n\t\t\tthis._sessions.set(sessionId, session);\n\n\t\t\tthis._logger.info(\n\t\t\t\t`Created session ${sessionId} (${this._sessions.size}/${this._maxSessions} active sessions)`\n\t\t\t);\n\t\t\treturn sessionId;\n\t\t} finally {\n\t\t\tresolveLock();\n\t\t\tthis._createSessionLock = null;\n\t\t}\n\t}\n\n\t/**\n\t * Process a thought in the specified session.\n\t *\n\t * @param sessionId - The session ID\n\t * @param input - The thought data to process\n\t * @returns Promise with the processing result\n\t * @throws Error if session not found\n\t */\n\tasync process(sessionId: string, input: ThoughtData): Promise<ProcessResult> {\n\t\tconst session = this._sessions.get(sessionId);\n\n\t\tif (!session) {\n\t\t\tthrow new SessionNotFoundError(sessionId);\n\t\t}\n\n\t\treturn session.process(input);\n\t}\n\n\t/**\n\t * Close a session and release resources.\n\t *\n\t * @param sessionId - The session ID to close\n\t * @throws Error if session not found\n\t */\n\tasync closeSession(sessionId: string): Promise<void> {\n\t\tconst session = this._sessions.get(sessionId);\n\n\t\tif (!session) {\n\t\t\tthrow new SessionNotFoundError(sessionId);\n\t\t}\n\n\t\tawait session.close();\n\t\tthis._sessions.delete(sessionId);\n\n\t\tthis._logger.info(\n\t\t\t`Closed session ${sessionId} (${this._sessions.size}/${this._maxSessions} active sessions)`\n\t\t);\n\t}\n\n\t/**\n\t * Get information about a session.\n\t *\n\t * @param sessionId - The session ID\n\t * @returns Session info or undefined if not found\n\t */\n\tgetSessionInfo(sessionId: string): SessionInfo | undefined {\n\t\treturn this._sessions.get(sessionId)?.getInfo();\n\t}\n\n\t/**\n\t * Get all active sessions.\n\t *\n\t * @returns Array of session information\n\t */\n\tgetActiveSessions(): SessionInfo[] {\n\t\treturn Array.from(this._sessions.values())\n\t\t\t.filter((s) => s.isActive)\n\t\t\t.map((s) => s.getInfo());\n\t}\n\n\t/**\n\t * Get connection pool statistics.\n\t */\n\tgetStats(): {\n\t\ttotalSessions: number;\n\t\tactiveSessions: number;\n\t\tmaxSessions: number;\n\t\tcleanupEnabled: boolean;\n\t\tsessionTimeout: number;\n\t} {\n\t\tconst activeSessions = this.getActiveSessions();\n\n\t\treturn {\n\t\t\ttotalSessions: this._sessions.size,\n\t\t\tactiveSessions: activeSessions.length,\n\t\t\tmaxSessions: this._maxSessions,\n\t\t\tcleanupEnabled: this._autoCleanup,\n\t\t\tsessionTimeout: this._sessionTimeout,\n\t\t};\n\t}\n\n\t/**\n\t * Start the automatic cleanup timer.\n\t */\n\tprivate _startCleanup(): void {\n\t\tif (this._cleanupTimerId !== null) {\n\t\t\tclearInterval(this._cleanupTimerId);\n\t\t}\n\n\t\tthis._cleanupTimerId = setInterval(() => {\n\t\t\tthis._cleanupTimedOutSessions();\n\t\t}, this._cleanupInterval) as unknown as number;\n\t}\n\n\t/**\n\t * Remove timed-out sessions.\n\t */\n\tprivate _cleanupTimedOutSessions(): void {\n\t\tlet cleaned = 0;\n\n\t\tfor (const [sessionId, session] of this._sessions.entries()) {\n\t\t\tif (session.isTimedOut()) {\n\t\t\t\tsession.close().catch((err) => {\n\t\t\t\t\tthis._logger.error(`Error closing timed out session ${sessionId}:`, err);\n\t\t\t\t});\n\t\t\t\tthis._sessions.delete(sessionId);\n\t\t\t\tcleaned++;\n\t\t\t}\n\t\t}\n\n\t\tif (cleaned > 0) {\n\t\t\tthis._logger.info(\n\t\t\t\t`Cleaned ${cleaned} timed-out sessions (${this._sessions.size}/${this._maxSessions} active sessions)`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Close all sessions and stop the cleanup timer.\n\t */\n\tasync terminate(): Promise<void> {\n\t\tif (this._terminated) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._terminated = true;\n\n\t\t// Stop cleanup timer\n\t\tif (this._cleanupTimerId !== null) {\n\t\t\tclearInterval(this._cleanupTimerId);\n\t\t\tthis._cleanupTimerId = null;\n\t\t}\n\n\t\t// Close all sessions\n\t\tconst closePromises = Array.from(this._sessions.values()).map((session) =>\n\t\t\tsession.close().catch((err) => {\n\t\t\t\tthis._logger.error(`Error closing session ${session.getInfo().id}:`, err);\n\t\t\t})\n\t\t);\n\n\t\tawait Promise.all(closePromises);\n\t\tthis._sessions.clear();\n\n\t\tthis._logger.info('ConnectionPool terminated');\n\t}\n\n\t/**\n\t * Dispose of the connection pool, releasing all resources.\n\t * Implements the IDisposable interface.\n\t * Delegates to terminate() for backward compatibility.\n\t */\n\tasync dispose(): Promise<void> {\n\t\tawait this.terminate();\n\t}\n\n\t/**\n\t * Check if the connection pool is active.\n\t */\n\tisRunning(): boolean {\n\t\treturn !this._terminated;\n\t}\n}\n\n/**\n * Create a connection pool with the given options.\n *\n * @param options - Connection pool configuration\n * @returns A configured connection pool\n *\n * @example\n * ```typescript\n * const pool = createConnectionPool({\n * maxSessions: 50,\n * sessionTimeout: 300000\n * });\n * ```\n */\nexport function createConnectionPool(options?: SessionOptions): ConnectionPool {\n\treturn new ConnectionPool(options);\n}\n"],"names":["Session","id","server","timeout","logger","Date","input","SessionNotActiveError","clearTimeout","setTimeout","err","ConnectionPool","Map","options","PoolTerminatedError","MaxSessionsReachedError","Error","resolveLock","Promise","resolve","sessionId","Math","session","SessionNotFoundError","Array","s","activeSessions","clearInterval","setInterval","cleaned","closePromises","createConnectionPool"],"mappings":"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"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base registry providing shared CRUD, caching, and discovery logic.
|
|
3
|
+
*
|
|
4
|
+
* This abstract generic class extracts the common patterns from `ToolRegistry`
|
|
5
|
+
* and `SkillRegistry` into a single reusable base. Subclasses only need to
|
|
6
|
+
* implement item-specific parsing, file filtering, and error construction.
|
|
7
|
+
*
|
|
8
|
+
* @template T - The registry item type (must have a `name` property)
|
|
9
|
+
* @module registry
|
|
10
|
+
*/
|
|
11
|
+
import { DiscoveryCache } from '../cache/DiscoveryCache.js';
|
|
12
|
+
import type { Logger } from '../logger/StructuredLogger.js';
|
|
13
|
+
/**
|
|
14
|
+
* Configuration options for creating a `BaseRegistry` instance.
|
|
15
|
+
*/
|
|
16
|
+
export interface BaseRegistryOptions {
|
|
17
|
+
/** Optional logger for diagnostics. */
|
|
18
|
+
logger?: Logger;
|
|
19
|
+
/** Optional cache for lookups. */
|
|
20
|
+
cache?: DiscoveryCache<{
|
|
21
|
+
name: string;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Directory paths to search for items.
|
|
25
|
+
*/
|
|
26
|
+
searchDirs?: string[];
|
|
27
|
+
/**
|
|
28
|
+
* Enable lazy discovery (discover on first access instead of startup).
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
lazyDiscovery?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Abstract base registry for managing named items with discovery and caching.
|
|
35
|
+
*
|
|
36
|
+
* Provides shared CRUD operations, filesystem discovery with deduplication,
|
|
37
|
+
* and optional LRU caching. Subclasses implement item-specific parsing logic
|
|
38
|
+
* and error construction.
|
|
39
|
+
*
|
|
40
|
+
* @template T - The registry item type (must have a `name` property)
|
|
41
|
+
*/
|
|
42
|
+
export declare abstract class BaseRegistry<T extends {
|
|
43
|
+
name: string;
|
|
44
|
+
}> {
|
|
45
|
+
/** Internal storage for items indexed by name. */
|
|
46
|
+
protected _items: Map<string, T>;
|
|
47
|
+
/** Logger for diagnostics. */
|
|
48
|
+
protected _logger: Logger;
|
|
49
|
+
/** Optional cache for lookups. */
|
|
50
|
+
protected _cache: DiscoveryCache<T>;
|
|
51
|
+
/** Directory paths to search for items. */
|
|
52
|
+
protected _searchDirs: string[];
|
|
53
|
+
/** Whether discovery has been performed. */
|
|
54
|
+
protected _discovered: boolean;
|
|
55
|
+
/** Promise for in-progress discovery (null if not in progress). */
|
|
56
|
+
protected _discoveryPromise: Promise<number> | null;
|
|
57
|
+
/** File extensions to match during discovery. */
|
|
58
|
+
protected abstract readonly _fileExtensions: string[];
|
|
59
|
+
/**
|
|
60
|
+
* Creates an error for invalid item data.
|
|
61
|
+
* @param reason - The reason for the validation failure
|
|
62
|
+
*/
|
|
63
|
+
protected abstract _createInvalidError(reason: string): Error;
|
|
64
|
+
/**
|
|
65
|
+
* Creates an error for duplicate items.
|
|
66
|
+
* @param name - The name of the duplicate item
|
|
67
|
+
*/
|
|
68
|
+
protected abstract _createDuplicateError(name: string): Error;
|
|
69
|
+
/**
|
|
70
|
+
* Creates an error for items not found.
|
|
71
|
+
* @param name - The name of the missing item
|
|
72
|
+
* @param action - The action that was attempted
|
|
73
|
+
*/
|
|
74
|
+
protected abstract _createNotFoundError(name: string, action: string): Error;
|
|
75
|
+
/**
|
|
76
|
+
* Parses frontmatter content into a partial item.
|
|
77
|
+
* @param content - The file content to parse
|
|
78
|
+
* @returns A partial item, with an `_error` property if parsing failed
|
|
79
|
+
*/
|
|
80
|
+
protected abstract _parseFrontmatter(content: string): Partial<T> & {
|
|
81
|
+
_error?: string;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Determines whether a file should be skipped during discovery.
|
|
85
|
+
* @param fileName - The name of the file to check
|
|
86
|
+
* @returns true if the file should be skipped
|
|
87
|
+
*/
|
|
88
|
+
protected abstract _shouldSkipFile(fileName: string): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Constructs a complete item from parsed frontmatter data.
|
|
91
|
+
* Returns null if the parsed data is insufficient.
|
|
92
|
+
* @param parsed - The parsed frontmatter data
|
|
93
|
+
* @returns A complete item, or null if data is insufficient
|
|
94
|
+
*/
|
|
95
|
+
protected abstract _buildItem(parsed: Partial<T>): T | null;
|
|
96
|
+
/**
|
|
97
|
+
* The entity name used in log messages (e.g., 'tool', 'skill').
|
|
98
|
+
*/
|
|
99
|
+
protected abstract readonly _entityName: string;
|
|
100
|
+
constructor(options: BaseRegistryOptions & Record<string, unknown>);
|
|
101
|
+
/**
|
|
102
|
+
* Internal logging method.
|
|
103
|
+
* @param message - The message to log
|
|
104
|
+
* @param meta - Optional metadata
|
|
105
|
+
*/
|
|
106
|
+
protected log(message: string, meta?: Record<string, unknown>): void;
|
|
107
|
+
/**
|
|
108
|
+
* Adds an item to the registry.
|
|
109
|
+
*
|
|
110
|
+
* @param item - The item to add
|
|
111
|
+
* @throws If item already exists or name is invalid
|
|
112
|
+
*/
|
|
113
|
+
add(item: T): void;
|
|
114
|
+
/**
|
|
115
|
+
* Removes an item from the registry.
|
|
116
|
+
*
|
|
117
|
+
* @param name - The name of the item to remove
|
|
118
|
+
* @throws If item not found
|
|
119
|
+
*/
|
|
120
|
+
remove(name: string): void;
|
|
121
|
+
/**
|
|
122
|
+
* Updates an existing item with partial data.
|
|
123
|
+
*
|
|
124
|
+
* @param name - The name of the item to update
|
|
125
|
+
* @param updates - Partial item data with fields to update
|
|
126
|
+
* @throws If item not found
|
|
127
|
+
*/
|
|
128
|
+
update(name: string, updates: Partial<T>): void;
|
|
129
|
+
/**
|
|
130
|
+
* Gets an item by name.
|
|
131
|
+
*
|
|
132
|
+
* @param name - The name of the item to get
|
|
133
|
+
* @returns The item if found, undefined otherwise
|
|
134
|
+
*/
|
|
135
|
+
get(name: string): T | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* Gets all items as an array.
|
|
138
|
+
*
|
|
139
|
+
* Uses cache if available for performance.
|
|
140
|
+
*
|
|
141
|
+
* @returns An array of all registered items
|
|
142
|
+
*/
|
|
143
|
+
getAll(): T[];
|
|
144
|
+
/**
|
|
145
|
+
* Checks if an item exists in the registry.
|
|
146
|
+
*
|
|
147
|
+
* @param name - The name of the item to check
|
|
148
|
+
* @returns true if the item exists, false otherwise
|
|
149
|
+
*/
|
|
150
|
+
has(name: string): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Gets all item names as an array.
|
|
153
|
+
*
|
|
154
|
+
* @returns An array of item names
|
|
155
|
+
*/
|
|
156
|
+
getNames(): string[];
|
|
157
|
+
/**
|
|
158
|
+
* Clears all items from the registry.
|
|
159
|
+
*/
|
|
160
|
+
clear(): void;
|
|
161
|
+
/**
|
|
162
|
+
* Gets the number of items in the registry.
|
|
163
|
+
*
|
|
164
|
+
* @returns The count of registered items
|
|
165
|
+
*/
|
|
166
|
+
size(): number;
|
|
167
|
+
/**
|
|
168
|
+
* Asynchronously discovers items from the configured directories.
|
|
169
|
+
*
|
|
170
|
+
* Multiple concurrent calls share the same discovery promise.
|
|
171
|
+
* Subsequent calls return cached results if discovery has already completed.
|
|
172
|
+
*
|
|
173
|
+
* @returns A Promise resolving to the number of items discovered
|
|
174
|
+
*/
|
|
175
|
+
discoverAsync(): Promise<number>;
|
|
176
|
+
/**
|
|
177
|
+
* Performs the actual discovery operation.
|
|
178
|
+
*
|
|
179
|
+
* Scans configured directories for item files, parses their frontmatter,
|
|
180
|
+
* and adds valid items to the registry.
|
|
181
|
+
*
|
|
182
|
+
* @returns A Promise resolving to the number of items discovered
|
|
183
|
+
*/
|
|
184
|
+
protected _performDiscovery(): Promise<number>;
|
|
185
|
+
/**
|
|
186
|
+
* Parses YAML frontmatter from file content.
|
|
187
|
+
*
|
|
188
|
+
* This is a shared utility for subclasses that parse YAML frontmatter.
|
|
189
|
+
*
|
|
190
|
+
* @param content - The file content to parse
|
|
191
|
+
* @returns The parsed YAML object, or null if no frontmatter found
|
|
192
|
+
*/
|
|
193
|
+
protected _extractFrontmatter(content: string): Record<string, unknown> | null;
|
|
194
|
+
/**
|
|
195
|
+
* Sets items from an external source.
|
|
196
|
+
*
|
|
197
|
+
* Clears existing items and adds new ones from the provided array.
|
|
198
|
+
*
|
|
199
|
+
* @param items - Array of items from an external source
|
|
200
|
+
*/
|
|
201
|
+
setAll(items: T[]): void;
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=BaseRegistry.d.ts.map
|