@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,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized utility exports
|
|
3
|
+
* Provides both legacy organization and new feature-based organization
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// FEATURE-BASED ORGANIZATION (Removed - migrated to domains/ and services/)
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Features now organized in:
|
|
10
|
+
// - src/domains/ for domain-specific logic
|
|
11
|
+
// - src/services/ for shared infrastructure
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// LEGACY ORGANIZATION (Backward Compatibility)
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Direct exports for backward compatibility - @deprecated
|
|
17
|
+
|
|
18
|
+
export * from './cache-storage.js';
|
|
19
|
+
export * from './database-errors.js';
|
|
20
|
+
// Database utilities
|
|
21
|
+
export * from './drizzle-storage.js';
|
|
22
|
+
// Error handling
|
|
23
|
+
export * from './error-handler.js';
|
|
24
|
+
// File operations
|
|
25
|
+
export * from './file-operations.js';
|
|
26
|
+
// JSONC utilities
|
|
27
|
+
export * from './jsonc.js';
|
|
28
|
+
// Logger utilities
|
|
29
|
+
export * from './logger.js';
|
|
30
|
+
export * from './memory-storage.js';
|
|
31
|
+
// Path utilities
|
|
32
|
+
export * from './paths.js';
|
|
33
|
+
// Security utilities
|
|
34
|
+
export * from './security.js';
|
|
35
|
+
export * from './simplified-errors.js';
|
|
36
|
+
// Target configuration
|
|
37
|
+
export * from './target-config.js';
|
|
38
|
+
|
|
39
|
+
// Search and indexing - moved to services/search/
|
|
40
|
+
|
|
41
|
+
// Command builder
|
|
42
|
+
export * from './command-builder.js';
|
|
43
|
+
// Console UI utilities
|
|
44
|
+
export * from './console-ui.js';
|
|
45
|
+
// Prompt utilities
|
|
46
|
+
export * from './prompts.js';
|
|
47
|
+
// Secret utilities
|
|
48
|
+
export * from './secret-utils.js';
|
|
49
|
+
// Template engine
|
|
50
|
+
export * from './template-engine.js';
|
|
51
|
+
|
|
52
|
+
// Embeddings and TF-IDF - moved to services/search/
|
|
53
|
+
|
|
54
|
+
// Help utilities
|
|
55
|
+
export * from './help.js';
|
|
56
|
+
|
|
57
|
+
// Target utilities
|
|
58
|
+
export * from './target-utils.js';
|
|
59
|
+
|
|
60
|
+
// Migration examples - removed (obsolete)
|
|
61
|
+
// Test utilities - removed (obsolete)
|
|
62
|
+
|
|
63
|
+
// Shared utilities
|
|
64
|
+
export * from '../shared/index.js';
|
|
65
|
+
|
|
66
|
+
// Base indexer - moved to services/search/
|
|
67
|
+
|
|
68
|
+
// LanceDB vector storage
|
|
69
|
+
export * from './lancedb-vector-storage.js';
|
|
70
|
+
// Separated storage
|
|
71
|
+
export * from './separated-storage.js';
|
|
72
|
+
// Settings utilities
|
|
73
|
+
export * from './settings.js';
|
|
74
|
+
|
|
75
|
+
// Target config types
|
|
76
|
+
export * from './target-config.js';
|
|
77
|
+
// Vector storage
|
|
78
|
+
export * from './vector-storage.js';
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONC (JSON with Comments) utilities
|
|
3
|
+
* Provides functions to parse and stringify JSONC files while preserving comments
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parse JSONC content (JSON with Comments)
|
|
10
|
+
* @param content - The JSONC string to parse
|
|
11
|
+
* @returns The parsed JavaScript object
|
|
12
|
+
*/
|
|
13
|
+
export function parseJSONC(content: string): unknown {
|
|
14
|
+
try {
|
|
15
|
+
// Remove single-line comments (//) but not inside strings
|
|
16
|
+
let cleaned = removeComments(content);
|
|
17
|
+
|
|
18
|
+
// Remove trailing commas before closing brackets/braces
|
|
19
|
+
cleaned = cleaned.replace(/,(\s*[}\]])/g, '$1');
|
|
20
|
+
|
|
21
|
+
return JSON.parse(cleaned);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Failed to parse JSONC: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Remove comments from JSON content while preserving strings
|
|
31
|
+
*/
|
|
32
|
+
function removeComments(content: string): string {
|
|
33
|
+
let result = '';
|
|
34
|
+
let inString = false;
|
|
35
|
+
let inSingleLineComment = false;
|
|
36
|
+
let inMultiLineComment = false;
|
|
37
|
+
let escapeNext = false;
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < content.length; i++) {
|
|
40
|
+
const char = content[i];
|
|
41
|
+
const nextChar = content[i + 1];
|
|
42
|
+
|
|
43
|
+
if (escapeNext) {
|
|
44
|
+
result += char;
|
|
45
|
+
escapeNext = false;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (char === '\\' && inString) {
|
|
50
|
+
result += char;
|
|
51
|
+
escapeNext = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (inString) {
|
|
56
|
+
if (char === '"') {
|
|
57
|
+
inString = false;
|
|
58
|
+
}
|
|
59
|
+
result += char;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (inSingleLineComment) {
|
|
64
|
+
if (char === '\n') {
|
|
65
|
+
inSingleLineComment = false;
|
|
66
|
+
result += char;
|
|
67
|
+
}
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (inMultiLineComment) {
|
|
72
|
+
if (char === '*' && nextChar === '/') {
|
|
73
|
+
inMultiLineComment = false;
|
|
74
|
+
i++; // Skip the '/'
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (char === '"') {
|
|
80
|
+
inString = true;
|
|
81
|
+
result += char;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (char === '/' && nextChar === '/') {
|
|
86
|
+
inSingleLineComment = true;
|
|
87
|
+
i++; // Skip the second '/'
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (char === '/' && nextChar === '*') {
|
|
92
|
+
inMultiLineComment = true;
|
|
93
|
+
i++; // Skip the '*'
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
result += char;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Stringify an object to JSON format with optional schema
|
|
105
|
+
* @param obj - The object to stringify
|
|
106
|
+
* @param schema - Optional schema URL to include
|
|
107
|
+
* @param indent - Indentation spaces (default: 2)
|
|
108
|
+
* @returns The formatted JSON string
|
|
109
|
+
*/
|
|
110
|
+
export function stringifyJSONC(obj: Record<string, unknown>, schema?: string, indent = 2): string {
|
|
111
|
+
const config = { ...obj };
|
|
112
|
+
|
|
113
|
+
// Add schema if provided and not already present
|
|
114
|
+
if (schema && !config.$schema) {
|
|
115
|
+
config.$schema = schema;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const json = JSON.stringify(config, null, indent);
|
|
119
|
+
|
|
120
|
+
// Add helpful comments for MCP configuration
|
|
121
|
+
if (config.mcp && Object.keys(config.mcp).length > 0) {
|
|
122
|
+
return json.replace(
|
|
123
|
+
/(\s*)"mcp": {/,
|
|
124
|
+
`$1// MCP (Model Context Protocol) server configuration
|
|
125
|
+
$1// See https://modelcontextprotocol.io for more information
|
|
126
|
+
$1"mcp": {`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return json;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Read and parse a JSONC file
|
|
135
|
+
* @param filePath - Path to the JSONC file
|
|
136
|
+
* @returns The parsed object
|
|
137
|
+
*/
|
|
138
|
+
export async function readJSONCFile(filePath: string): Promise<any> {
|
|
139
|
+
const content = await readFile(filePath, 'utf8');
|
|
140
|
+
return parseJSONC(content);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Write an object to a JSONC file
|
|
145
|
+
* @param filePath - Path to the JSONC file
|
|
146
|
+
* @param obj - The object to write
|
|
147
|
+
* @param schema - Optional schema URL
|
|
148
|
+
* @param indent - Indentation spaces
|
|
149
|
+
*/
|
|
150
|
+
export async function writeJSONCFile(
|
|
151
|
+
filePath: string,
|
|
152
|
+
obj: Record<string, unknown>,
|
|
153
|
+
schema?: string,
|
|
154
|
+
indent = 2
|
|
155
|
+
): Promise<void> {
|
|
156
|
+
const content = stringifyJSONC(obj, schema, indent);
|
|
157
|
+
await writeFile(filePath, content, 'utf8');
|
|
158
|
+
}
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized logging utility for Sylphx Flow
|
|
3
|
+
* Provides structured logging with different levels and output formats
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
10
|
+
|
|
11
|
+
export interface LogEntry {
|
|
12
|
+
id: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
level: LogLevel;
|
|
15
|
+
message: string;
|
|
16
|
+
context?: Record<string, unknown>;
|
|
17
|
+
error?: {
|
|
18
|
+
name: string;
|
|
19
|
+
message: string;
|
|
20
|
+
stack?: string;
|
|
21
|
+
code?: string;
|
|
22
|
+
};
|
|
23
|
+
module?: string;
|
|
24
|
+
function?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface LoggerConfig {
|
|
28
|
+
level: LogLevel;
|
|
29
|
+
format: 'json' | 'pretty' | 'simple';
|
|
30
|
+
includeTimestamp: boolean;
|
|
31
|
+
includeContext: boolean;
|
|
32
|
+
colors: boolean;
|
|
33
|
+
module?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const LEVEL_PRIORITY: Record<LogLevel, number> = {
|
|
37
|
+
debug: 0,
|
|
38
|
+
info: 1,
|
|
39
|
+
warn: 2,
|
|
40
|
+
error: 3,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const LEVEL_COLORS: Record<LogLevel, (text: string) => string> = {
|
|
44
|
+
debug: chalk.gray,
|
|
45
|
+
info: chalk.blue,
|
|
46
|
+
warn: chalk.yellow,
|
|
47
|
+
error: chalk.red,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const LEVEL_SYMBOLS: Record<LogLevel, string> = {
|
|
51
|
+
debug: '🔍',
|
|
52
|
+
info: 'ℹ',
|
|
53
|
+
warn: '⚠',
|
|
54
|
+
error: '✗',
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Logger interface for dependency injection and testing
|
|
59
|
+
*/
|
|
60
|
+
export interface Logger {
|
|
61
|
+
child(context: Record<string, unknown>): Logger;
|
|
62
|
+
module(moduleName: string): Logger;
|
|
63
|
+
setLevel(level: LogLevel): void;
|
|
64
|
+
updateConfig(config: Partial<LoggerConfig>): void;
|
|
65
|
+
debug(message: string, context?: Record<string, unknown>): void;
|
|
66
|
+
info(message: string, context?: Record<string, unknown>): void;
|
|
67
|
+
warn(message: string, context?: Record<string, unknown>): void;
|
|
68
|
+
error(message: string, error?: Error, context?: Record<string, unknown>): void;
|
|
69
|
+
time<T>(fn: () => Promise<T>, label: string, context?: Record<string, unknown>): Promise<T>;
|
|
70
|
+
timeSync<T>(fn: () => T, label: string, context?: Record<string, unknown>): T;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Internal state for logger instance
|
|
75
|
+
*/
|
|
76
|
+
interface LoggerState {
|
|
77
|
+
config: LoggerConfig;
|
|
78
|
+
context?: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Options for creating a logger instance
|
|
83
|
+
*/
|
|
84
|
+
interface CreateLoggerOptions {
|
|
85
|
+
config?: Partial<LoggerConfig>;
|
|
86
|
+
context?: Record<string, unknown>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create a logger instance with the specified configuration and context
|
|
91
|
+
*/
|
|
92
|
+
export function createLogger(options: Partial<LoggerConfig> | CreateLoggerOptions = {}): Logger {
|
|
93
|
+
// Handle both old style (config object) and new style (options with config and context)
|
|
94
|
+
const isOptionsStyle = 'config' in options || 'context' in options;
|
|
95
|
+
const config = isOptionsStyle
|
|
96
|
+
? (options as CreateLoggerOptions).config || {}
|
|
97
|
+
: (options as Partial<LoggerConfig>);
|
|
98
|
+
const initialContext = isOptionsStyle ? (options as CreateLoggerOptions).context : undefined;
|
|
99
|
+
|
|
100
|
+
const state: LoggerState = {
|
|
101
|
+
config: {
|
|
102
|
+
level: 'info',
|
|
103
|
+
format: 'pretty',
|
|
104
|
+
includeTimestamp: true,
|
|
105
|
+
includeContext: true,
|
|
106
|
+
colors: true,
|
|
107
|
+
...config,
|
|
108
|
+
},
|
|
109
|
+
context: initialContext,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if a log level should be output
|
|
114
|
+
*/
|
|
115
|
+
const shouldLog = (level: LogLevel): boolean => {
|
|
116
|
+
return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[state.config.level];
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Create a log entry
|
|
121
|
+
*/
|
|
122
|
+
const createLogEntry = (
|
|
123
|
+
level: LogLevel,
|
|
124
|
+
message: string,
|
|
125
|
+
error?: Error,
|
|
126
|
+
additionalContext?: Record<string, unknown>
|
|
127
|
+
): LogEntry => {
|
|
128
|
+
const entry: LogEntry = {
|
|
129
|
+
id: randomUUID(),
|
|
130
|
+
timestamp: new Date().toISOString(),
|
|
131
|
+
level,
|
|
132
|
+
message,
|
|
133
|
+
module: state.context?.module,
|
|
134
|
+
function: state.context?.function,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Merge contexts
|
|
138
|
+
if (state.config.includeContext) {
|
|
139
|
+
entry.context = { ...state.context, ...additionalContext };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Add error information if provided
|
|
143
|
+
if (error) {
|
|
144
|
+
entry.error = {
|
|
145
|
+
name: error.name,
|
|
146
|
+
message: error.message,
|
|
147
|
+
stack: error.stack,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Add error code if it's a CLIError
|
|
151
|
+
if ('code' in error && typeof error.code === 'string') {
|
|
152
|
+
entry.error.code = error.code;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return entry;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Format a log entry for output
|
|
161
|
+
*/
|
|
162
|
+
const formatEntry = (entry: LogEntry): string => {
|
|
163
|
+
switch (state.config.format) {
|
|
164
|
+
case 'json':
|
|
165
|
+
return JSON.stringify(entry);
|
|
166
|
+
|
|
167
|
+
case 'simple': {
|
|
168
|
+
const levelStr = entry.level.toUpperCase().padEnd(5);
|
|
169
|
+
const moduleStr = entry.module ? `[${entry.module}] ` : '';
|
|
170
|
+
return `${levelStr} ${moduleStr}${entry.message}`;
|
|
171
|
+
}
|
|
172
|
+
default: {
|
|
173
|
+
const parts: string[] = [];
|
|
174
|
+
|
|
175
|
+
// Timestamp
|
|
176
|
+
if (state.config.includeTimestamp) {
|
|
177
|
+
const time = new Date(entry.timestamp).toLocaleTimeString();
|
|
178
|
+
parts.push(chalk.gray(time));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Level symbol and name
|
|
182
|
+
const colorFn = state.config.colors ? LEVEL_COLORS[entry.level] : (s: string) => s;
|
|
183
|
+
parts.push(
|
|
184
|
+
`${colorFn(LEVEL_SYMBOLS[entry.level])} ${colorFn(entry.level.toUpperCase().padEnd(5))}`
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Module
|
|
188
|
+
if (entry.module) {
|
|
189
|
+
parts.push(chalk.cyan(`[${entry.module}]`));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Function
|
|
193
|
+
if (entry.function) {
|
|
194
|
+
parts.push(chalk.gray(`${entry.function}()`));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Message
|
|
198
|
+
parts.push(entry.message);
|
|
199
|
+
|
|
200
|
+
let result = parts.join(' ');
|
|
201
|
+
|
|
202
|
+
// Context
|
|
203
|
+
if (entry.context && Object.keys(entry.context).length > 0) {
|
|
204
|
+
const contextStr = JSON.stringify(entry.context, null, 2);
|
|
205
|
+
result += `\n${chalk.gray(' Context: ')}${chalk.gray(contextStr)}`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Error details
|
|
209
|
+
if (entry.error) {
|
|
210
|
+
result += `\n${chalk.red(' Error: ')}${chalk.red(entry.error.message)}`;
|
|
211
|
+
if (entry.error.code) {
|
|
212
|
+
result += `\n${chalk.red(' Code: ')}${chalk.red(entry.error.code)}`;
|
|
213
|
+
}
|
|
214
|
+
if (entry.error.stack) {
|
|
215
|
+
result += `\n${chalk.gray(entry.error.stack)}`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Internal logging method
|
|
226
|
+
*/
|
|
227
|
+
const logInternal = (
|
|
228
|
+
level: LogLevel,
|
|
229
|
+
message: string,
|
|
230
|
+
error?: Error,
|
|
231
|
+
additionalContext?: Record<string, any>
|
|
232
|
+
): void => {
|
|
233
|
+
if (!shouldLog(level)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const entry = createLogEntry(level, message, error, additionalContext);
|
|
238
|
+
const formatted = formatEntry(entry);
|
|
239
|
+
|
|
240
|
+
// Output to appropriate stream
|
|
241
|
+
if (level === 'error') {
|
|
242
|
+
console.error(formatted);
|
|
243
|
+
} else {
|
|
244
|
+
console.log(formatted);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Create a child logger with additional context
|
|
250
|
+
*/
|
|
251
|
+
const child = (context: Record<string, unknown>): Logger => {
|
|
252
|
+
return createLogger({
|
|
253
|
+
config: state.config,
|
|
254
|
+
context: { ...state.context, ...context },
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Create a logger for a specific module
|
|
260
|
+
*/
|
|
261
|
+
const module = (moduleName: string): Logger => {
|
|
262
|
+
return child({ module: moduleName });
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Set the log level
|
|
267
|
+
*/
|
|
268
|
+
const setLevel = (level: LogLevel): void => {
|
|
269
|
+
state.config.level = level;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Update logger configuration
|
|
274
|
+
*/
|
|
275
|
+
const updateConfig = (config: Partial<LoggerConfig>): void => {
|
|
276
|
+
state.config = { ...state.config, ...config };
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Debug level logging
|
|
281
|
+
*/
|
|
282
|
+
const debug = (message: string, context?: Record<string, unknown>): void => {
|
|
283
|
+
logInternal('debug', message, undefined, context);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Info level logging
|
|
288
|
+
*/
|
|
289
|
+
const info = (message: string, context?: Record<string, unknown>): void => {
|
|
290
|
+
logInternal('info', message, undefined, context);
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Warning level logging
|
|
295
|
+
*/
|
|
296
|
+
const warn = (message: string, context?: Record<string, unknown>): void => {
|
|
297
|
+
logInternal('warn', message, undefined, context);
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Error level logging
|
|
302
|
+
*/
|
|
303
|
+
const error = (message: string, errorObj?: Error, context?: Record<string, unknown>): void => {
|
|
304
|
+
logInternal('error', message, errorObj, context);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Log function execution with timing
|
|
309
|
+
*/
|
|
310
|
+
const time = async <T>(
|
|
311
|
+
fn: () => Promise<T>,
|
|
312
|
+
label: string,
|
|
313
|
+
context?: Record<string, unknown>
|
|
314
|
+
): Promise<T> => {
|
|
315
|
+
const start = Date.now();
|
|
316
|
+
debug(`Starting ${label}`, context);
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
const result = await fn();
|
|
320
|
+
const duration = Date.now() - start;
|
|
321
|
+
info(`Completed ${label}`, { ...context, duration: `${duration}ms` });
|
|
322
|
+
return result;
|
|
323
|
+
} catch (caughtError) {
|
|
324
|
+
const duration = Date.now() - start;
|
|
325
|
+
error(`Failed ${label}`, caughtError as Error, { ...context, duration: `${duration}ms` });
|
|
326
|
+
throw caughtError;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Log function execution (sync) with timing
|
|
332
|
+
*/
|
|
333
|
+
const timeSync = <T>(fn: () => T, label: string, context?: Record<string, unknown>): T => {
|
|
334
|
+
const start = Date.now();
|
|
335
|
+
debug(`Starting ${label}`, context);
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
const result = fn();
|
|
339
|
+
const duration = Date.now() - start;
|
|
340
|
+
info(`Completed ${label}`, { ...context, duration: `${duration}ms` });
|
|
341
|
+
return result;
|
|
342
|
+
} catch (caughtError) {
|
|
343
|
+
const duration = Date.now() - start;
|
|
344
|
+
error(`Failed ${label}`, caughtError as Error, { ...context, duration: `${duration}ms` });
|
|
345
|
+
throw caughtError;
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
child,
|
|
351
|
+
module,
|
|
352
|
+
setLevel,
|
|
353
|
+
updateConfig,
|
|
354
|
+
debug,
|
|
355
|
+
info,
|
|
356
|
+
warn,
|
|
357
|
+
error,
|
|
358
|
+
time,
|
|
359
|
+
timeSync,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Default logger instance
|
|
364
|
+
export const logger = createLogger();
|
|
365
|
+
|
|
366
|
+
// Environment-based configuration
|
|
367
|
+
if (process.env.NODE_ENV === 'production') {
|
|
368
|
+
logger.updateConfig({
|
|
369
|
+
level: 'info',
|
|
370
|
+
format: 'json',
|
|
371
|
+
colors: false,
|
|
372
|
+
});
|
|
373
|
+
} else if (process.env.DEBUG) {
|
|
374
|
+
logger.updateConfig({
|
|
375
|
+
level: 'debug',
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Export convenience functions
|
|
380
|
+
export const log = {
|
|
381
|
+
debug: (message: string, context?: Record<string, unknown>) => logger.debug(message, context),
|
|
382
|
+
info: (message: string, context?: Record<string, unknown>) => logger.info(message, context),
|
|
383
|
+
warn: (message: string, context?: Record<string, unknown>) => logger.warn(message, context),
|
|
384
|
+
error: (message: string, error?: Error, context?: Record<string, unknown>) =>
|
|
385
|
+
logger.error(message, error, context),
|
|
386
|
+
time: <T>(fn: () => Promise<T>, label: string, context?: Record<string, unknown>) =>
|
|
387
|
+
logger.time(fn, label, context),
|
|
388
|
+
timeSync: <T>(fn: () => T, label: string, context?: Record<string, unknown>) =>
|
|
389
|
+
logger.timeSync(fn, label, context),
|
|
390
|
+
child: (context: Record<string, unknown>) => logger.child(context),
|
|
391
|
+
module: (moduleName: string) => logger.module(moduleName),
|
|
392
|
+
setLevel: (level: LogLevel) => logger.setLevel(level),
|
|
393
|
+
updateConfig: (config: Partial<LoggerConfig>) => logger.updateConfig(config),
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
export default logger;
|