@bloomneo/appkit 1.2.9
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 +21 -0
- package/README.md +902 -0
- package/bin/appkit.js +71 -0
- package/bin/commands/generate.js +1050 -0
- package/bin/templates/backend/README.md.template +39 -0
- package/bin/templates/backend/api.http.template +0 -0
- package/bin/templates/backend/docs/APPKIT_CLI.md +507 -0
- package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +61 -0
- package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +2539 -0
- package/bin/templates/backend/package.json.template +34 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.http.template +29 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.route.ts.template +36 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.service.ts.template +88 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.types.ts.template +18 -0
- package/bin/templates/backend/src/api/lib/api-router.ts.template +84 -0
- package/bin/templates/backend/src/api/server.ts.template +188 -0
- package/bin/templates/backend/tsconfig.api.json.template +24 -0
- package/bin/templates/backend/tsconfig.json.template +40 -0
- package/bin/templates/feature/feature.http.template +63 -0
- package/bin/templates/feature/feature.route.ts.template +36 -0
- package/bin/templates/feature/feature.service.ts.template +81 -0
- package/bin/templates/feature/feature.types.ts.template +23 -0
- package/bin/templates/feature-db/feature.http.template +63 -0
- package/bin/templates/feature-db/feature.model.ts.template +74 -0
- package/bin/templates/feature-db/feature.route.ts.template +58 -0
- package/bin/templates/feature-db/feature.service.ts.template +231 -0
- package/bin/templates/feature-db/feature.types.ts.template +25 -0
- package/bin/templates/feature-db/schema-addition.prisma.template +9 -0
- package/bin/templates/feature-db/seeding/README.md.template +57 -0
- package/bin/templates/feature-db/seeding/feature.seed.js.template +67 -0
- package/bin/templates/feature-user/schema-addition.prisma.template +19 -0
- package/bin/templates/feature-user/user.http.template +157 -0
- package/bin/templates/feature-user/user.model.ts.template +244 -0
- package/bin/templates/feature-user/user.route.ts.template +379 -0
- package/bin/templates/feature-user/user.seed.js.template +182 -0
- package/bin/templates/feature-user/user.service.ts.template +426 -0
- package/bin/templates/feature-user/user.types.ts.template +127 -0
- package/dist/auth/auth.d.ts +182 -0
- package/dist/auth/auth.d.ts.map +1 -0
- package/dist/auth/auth.js +477 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/defaults.d.ts +104 -0
- package/dist/auth/defaults.d.ts.map +1 -0
- package/dist/auth/defaults.js +374 -0
- package/dist/auth/defaults.js.map +1 -0
- package/dist/auth/index.d.ts +70 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +94 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/cache/cache.d.ts +118 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +249 -0
- package/dist/cache/cache.js.map +1 -0
- package/dist/cache/defaults.d.ts +63 -0
- package/dist/cache/defaults.d.ts.map +1 -0
- package/dist/cache/defaults.js +193 -0
- package/dist/cache/defaults.js.map +1 -0
- package/dist/cache/index.d.ts +101 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +203 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/strategies/memory.d.ts +138 -0
- package/dist/cache/strategies/memory.d.ts.map +1 -0
- package/dist/cache/strategies/memory.js +348 -0
- package/dist/cache/strategies/memory.js.map +1 -0
- package/dist/cache/strategies/redis.d.ts +105 -0
- package/dist/cache/strategies/redis.d.ts.map +1 -0
- package/dist/cache/strategies/redis.js +318 -0
- package/dist/cache/strategies/redis.js.map +1 -0
- package/dist/config/config.d.ts +62 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +107 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/defaults.d.ts +44 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +217 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +105 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +163 -0
- package/dist/config/index.js.map +1 -0
- package/dist/database/adapters/mongoose.d.ts +106 -0
- package/dist/database/adapters/mongoose.d.ts.map +1 -0
- package/dist/database/adapters/mongoose.js +480 -0
- package/dist/database/adapters/mongoose.js.map +1 -0
- package/dist/database/adapters/prisma.d.ts +106 -0
- package/dist/database/adapters/prisma.d.ts.map +1 -0
- package/dist/database/adapters/prisma.js +494 -0
- package/dist/database/adapters/prisma.js.map +1 -0
- package/dist/database/defaults.d.ts +87 -0
- package/dist/database/defaults.d.ts.map +1 -0
- package/dist/database/defaults.js +271 -0
- package/dist/database/defaults.js.map +1 -0
- package/dist/database/index.d.ts +137 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/index.js +490 -0
- package/dist/database/index.js.map +1 -0
- package/dist/email/defaults.d.ts +100 -0
- package/dist/email/defaults.d.ts.map +1 -0
- package/dist/email/defaults.js +400 -0
- package/dist/email/defaults.js.map +1 -0
- package/dist/email/email.d.ts +139 -0
- package/dist/email/email.d.ts.map +1 -0
- package/dist/email/email.js +316 -0
- package/dist/email/email.js.map +1 -0
- package/dist/email/index.d.ts +176 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +251 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/strategies/console.d.ts +90 -0
- package/dist/email/strategies/console.d.ts.map +1 -0
- package/dist/email/strategies/console.js +268 -0
- package/dist/email/strategies/console.js.map +1 -0
- package/dist/email/strategies/resend.d.ts +84 -0
- package/dist/email/strategies/resend.d.ts.map +1 -0
- package/dist/email/strategies/resend.js +266 -0
- package/dist/email/strategies/resend.js.map +1 -0
- package/dist/email/strategies/smtp.d.ts +77 -0
- package/dist/email/strategies/smtp.d.ts.map +1 -0
- package/dist/email/strategies/smtp.js +286 -0
- package/dist/email/strategies/smtp.js.map +1 -0
- package/dist/error/defaults.d.ts +40 -0
- package/dist/error/defaults.d.ts.map +1 -0
- package/dist/error/defaults.js +75 -0
- package/dist/error/defaults.js.map +1 -0
- package/dist/error/error.d.ts +140 -0
- package/dist/error/error.d.ts.map +1 -0
- package/dist/error/error.js +200 -0
- package/dist/error/error.js.map +1 -0
- package/dist/error/index.d.ts +145 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +145 -0
- package/dist/error/index.js.map +1 -0
- package/dist/event/defaults.d.ts +111 -0
- package/dist/event/defaults.d.ts.map +1 -0
- package/dist/event/defaults.js +378 -0
- package/dist/event/defaults.js.map +1 -0
- package/dist/event/event.d.ts +171 -0
- package/dist/event/event.d.ts.map +1 -0
- package/dist/event/event.js +391 -0
- package/dist/event/event.js.map +1 -0
- package/dist/event/index.d.ts +173 -0
- package/dist/event/index.d.ts.map +1 -0
- package/dist/event/index.js +302 -0
- package/dist/event/index.js.map +1 -0
- package/dist/event/strategies/memory.d.ts +122 -0
- package/dist/event/strategies/memory.d.ts.map +1 -0
- package/dist/event/strategies/memory.js +331 -0
- package/dist/event/strategies/memory.js.map +1 -0
- package/dist/event/strategies/redis.d.ts +115 -0
- package/dist/event/strategies/redis.d.ts.map +1 -0
- package/dist/event/strategies/redis.js +434 -0
- package/dist/event/strategies/redis.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/logger/defaults.d.ts +67 -0
- package/dist/logger/defaults.d.ts.map +1 -0
- package/dist/logger/defaults.js +213 -0
- package/dist/logger/defaults.js.map +1 -0
- package/dist/logger/index.d.ts +84 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +101 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/logger.d.ts +165 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/dist/logger/logger.js +843 -0
- package/dist/logger/logger.js.map +1 -0
- package/dist/logger/transports/console.d.ts +102 -0
- package/dist/logger/transports/console.d.ts.map +1 -0
- package/dist/logger/transports/console.js +276 -0
- package/dist/logger/transports/console.js.map +1 -0
- package/dist/logger/transports/database.d.ts +153 -0
- package/dist/logger/transports/database.d.ts.map +1 -0
- package/dist/logger/transports/database.js +539 -0
- package/dist/logger/transports/database.js.map +1 -0
- package/dist/logger/transports/file.d.ts +146 -0
- package/dist/logger/transports/file.d.ts.map +1 -0
- package/dist/logger/transports/file.js +464 -0
- package/dist/logger/transports/file.js.map +1 -0
- package/dist/logger/transports/http.d.ts +128 -0
- package/dist/logger/transports/http.d.ts.map +1 -0
- package/dist/logger/transports/http.js +401 -0
- package/dist/logger/transports/http.js.map +1 -0
- package/dist/logger/transports/webhook.d.ts +152 -0
- package/dist/logger/transports/webhook.d.ts.map +1 -0
- package/dist/logger/transports/webhook.js +485 -0
- package/dist/logger/transports/webhook.js.map +1 -0
- package/dist/queue/defaults.d.ts +66 -0
- package/dist/queue/defaults.d.ts.map +1 -0
- package/dist/queue/defaults.js +205 -0
- package/dist/queue/defaults.js.map +1 -0
- package/dist/queue/index.d.ts +124 -0
- package/dist/queue/index.d.ts.map +1 -0
- package/dist/queue/index.js +116 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/queue.d.ts +156 -0
- package/dist/queue/queue.d.ts.map +1 -0
- package/dist/queue/queue.js +387 -0
- package/dist/queue/queue.js.map +1 -0
- package/dist/queue/transports/database.d.ts +165 -0
- package/dist/queue/transports/database.d.ts.map +1 -0
- package/dist/queue/transports/database.js +595 -0
- package/dist/queue/transports/database.js.map +1 -0
- package/dist/queue/transports/memory.d.ts +143 -0
- package/dist/queue/transports/memory.d.ts.map +1 -0
- package/dist/queue/transports/memory.js +415 -0
- package/dist/queue/transports/memory.js.map +1 -0
- package/dist/queue/transports/redis.d.ts +203 -0
- package/dist/queue/transports/redis.d.ts.map +1 -0
- package/dist/queue/transports/redis.js +744 -0
- package/dist/queue/transports/redis.js.map +1 -0
- package/dist/security/defaults.d.ts +64 -0
- package/dist/security/defaults.d.ts.map +1 -0
- package/dist/security/defaults.js +159 -0
- package/dist/security/defaults.js.map +1 -0
- package/dist/security/index.d.ts +110 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +160 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/security.d.ts +138 -0
- package/dist/security/security.d.ts.map +1 -0
- package/dist/security/security.js +419 -0
- package/dist/security/security.js.map +1 -0
- package/dist/storage/defaults.d.ts +79 -0
- package/dist/storage/defaults.d.ts.map +1 -0
- package/dist/storage/defaults.js +358 -0
- package/dist/storage/defaults.js.map +1 -0
- package/dist/storage/index.d.ts +153 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +242 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/storage.d.ts +151 -0
- package/dist/storage/storage.d.ts.map +1 -0
- package/dist/storage/storage.js +439 -0
- package/dist/storage/storage.js.map +1 -0
- package/dist/storage/strategies/local.d.ts +117 -0
- package/dist/storage/strategies/local.d.ts.map +1 -0
- package/dist/storage/strategies/local.js +368 -0
- package/dist/storage/strategies/local.js.map +1 -0
- package/dist/storage/strategies/r2.d.ts +130 -0
- package/dist/storage/strategies/r2.d.ts.map +1 -0
- package/dist/storage/strategies/r2.js +470 -0
- package/dist/storage/strategies/r2.js.map +1 -0
- package/dist/storage/strategies/s3.d.ts +121 -0
- package/dist/storage/strategies/s3.d.ts.map +1 -0
- package/dist/storage/strategies/s3.js +461 -0
- package/dist/storage/strategies/s3.js.map +1 -0
- package/dist/util/defaults.d.ts +77 -0
- package/dist/util/defaults.d.ts.map +1 -0
- package/dist/util/defaults.js +193 -0
- package/dist/util/defaults.js.map +1 -0
- package/dist/util/index.d.ts +97 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +165 -0
- package/dist/util/index.js.map +1 -0
- package/dist/util/util.d.ts +145 -0
- package/dist/util/util.d.ts.map +1 -0
- package/dist/util/util.js +481 -0
- package/dist/util/util.js.map +1 -0
- package/package.json +234 -0
|
@@ -0,0 +1,843 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core logger class with visual error display and simplified transport management
|
|
3
|
+
* @module @bloomneo/appkit/logger
|
|
4
|
+
* @file src/logger/logger.ts
|
|
5
|
+
*
|
|
6
|
+
* @llm-rule WHEN: Building logger instances - called via loggerClass.get(), not directly
|
|
7
|
+
* @llm-rule AVOID: Creating LoggerClass directly - always use loggerClass.get() for proper setup
|
|
8
|
+
* @llm-rule NOTE: Enhanced error() method provides automatic visual formatting for better DX
|
|
9
|
+
*/
|
|
10
|
+
import { ConsoleTransport } from './transports/console.js';
|
|
11
|
+
import { FileTransport } from './transports/file.js';
|
|
12
|
+
import { DatabaseTransport } from './transports/database.js';
|
|
13
|
+
import { HttpTransport } from './transports/http.js';
|
|
14
|
+
import { WebhookTransport } from './transports/webhook.js';
|
|
15
|
+
import { existsSync } from 'fs';
|
|
16
|
+
import { join } from 'path';
|
|
17
|
+
const LOG_LEVELS = {
|
|
18
|
+
error: 0,
|
|
19
|
+
warn: 1,
|
|
20
|
+
info: 2,
|
|
21
|
+
debug: 3,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Logger class with automatic transport management and enhanced error() method
|
|
25
|
+
*/
|
|
26
|
+
export class LoggerClass {
|
|
27
|
+
level;
|
|
28
|
+
levelValue;
|
|
29
|
+
defaultMeta;
|
|
30
|
+
config;
|
|
31
|
+
transports = new Map();
|
|
32
|
+
pendingWrites = [];
|
|
33
|
+
constructor(config) {
|
|
34
|
+
this.config = config;
|
|
35
|
+
this.level = config.level;
|
|
36
|
+
this.levelValue = LOG_LEVELS[this.level];
|
|
37
|
+
this.defaultMeta = {
|
|
38
|
+
service: config.service.name,
|
|
39
|
+
version: config.service.version,
|
|
40
|
+
environment: config.service.environment,
|
|
41
|
+
};
|
|
42
|
+
this.initializeTransports();
|
|
43
|
+
}
|
|
44
|
+
initializeTransports() {
|
|
45
|
+
const { transports } = this.config;
|
|
46
|
+
if (transports.console) {
|
|
47
|
+
this.transports.set('console', new ConsoleTransport(this.config));
|
|
48
|
+
}
|
|
49
|
+
if (transports.file) {
|
|
50
|
+
try {
|
|
51
|
+
this.transports.set('file', new FileTransport(this.config));
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error('File transport initialization failed:', error.message);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (transports.database && this.config.database.url) {
|
|
58
|
+
try {
|
|
59
|
+
this.transports.set('database', new DatabaseTransport(this.config));
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error('Database transport initialization failed:', error.message);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (transports.http && this.config.http.url) {
|
|
66
|
+
try {
|
|
67
|
+
this.transports.set('http', new HttpTransport(this.config));
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error('HTTP transport initialization failed:', error.message);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (transports.webhook && this.config.webhook.url) {
|
|
74
|
+
try {
|
|
75
|
+
this.transports.set('webhook', new WebhookTransport(this.config));
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
console.error('Webhook transport initialization failed:', error.message);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (this.transports.size === 0) {
|
|
82
|
+
console.warn('No transports initialized, falling back to console');
|
|
83
|
+
this.transports.set('console', new ConsoleTransport(this.config));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Log informational message
|
|
88
|
+
* @llm-rule WHEN: Normal application events, user actions, business logic flow
|
|
89
|
+
* @llm-rule AVOID: Sensitive data in meta - passwords, tokens, full card numbers
|
|
90
|
+
*/
|
|
91
|
+
info(message, meta = {}) {
|
|
92
|
+
this.log('info', message, meta);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Enhanced error logging with automatic visual formatting
|
|
96
|
+
* @llm-rule WHEN: Exceptions, failures, critical issues requiring attention
|
|
97
|
+
* @llm-rule AVOID: Using for warnings - errors should indicate actual problems
|
|
98
|
+
* @llm-rule NOTE: Automatically provides visual formatting in development with smart diagnostics
|
|
99
|
+
*/
|
|
100
|
+
error(message, meta = {}) {
|
|
101
|
+
const enhancedMeta = {
|
|
102
|
+
...meta,
|
|
103
|
+
_location: this.getCaller()
|
|
104
|
+
};
|
|
105
|
+
// Detect error type and provide visual formatting if appropriate
|
|
106
|
+
if (this.shouldShowVisual()) {
|
|
107
|
+
this.renderVisualError(message, enhancedMeta);
|
|
108
|
+
}
|
|
109
|
+
// Always log structured data for production/debugging
|
|
110
|
+
this.log('error', message, enhancedMeta);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Log warning message
|
|
114
|
+
* @llm-rule WHEN: Potential issues, deprecated usage, performance concerns
|
|
115
|
+
* @llm-rule AVOID: Using for normal recoverable errors - use error() for those
|
|
116
|
+
*/
|
|
117
|
+
warn(message, meta = {}) {
|
|
118
|
+
this.log('warn', message, meta);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Log debug message
|
|
122
|
+
* @llm-rule WHEN: Development debugging, detailed flow information
|
|
123
|
+
* @llm-rule AVOID: Production debug spam - automatically filtered in production
|
|
124
|
+
*/
|
|
125
|
+
debug(message, meta = {}) {
|
|
126
|
+
this.log('debug', message, meta);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Create child logger with additional context
|
|
130
|
+
* @llm-rule WHEN: Adding component context or request-specific data
|
|
131
|
+
* @llm-rule AVOID: Creating many child loggers - reuse component loggers
|
|
132
|
+
*/
|
|
133
|
+
child(bindings) {
|
|
134
|
+
const child = Object.create(this);
|
|
135
|
+
child.defaultMeta = { ...this.defaultMeta, ...bindings };
|
|
136
|
+
return child;
|
|
137
|
+
}
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// VISUAL ERROR FORMATTING METHODS
|
|
140
|
+
// ============================================================================
|
|
141
|
+
shouldShowVisual() {
|
|
142
|
+
// Show visual output in development or when explicitly enabled
|
|
143
|
+
return this.config.service.environment === 'development' ||
|
|
144
|
+
this.config.minimal === false ||
|
|
145
|
+
process.env.VOILA_VISUAL_ERRORS === 'true';
|
|
146
|
+
}
|
|
147
|
+
renderVisualError(message, meta) {
|
|
148
|
+
const errorType = this.detectErrorType(message, meta);
|
|
149
|
+
const title = this.getErrorTitle(errorType);
|
|
150
|
+
const diagnostics = this.generateDiagnostics(message, meta, errorType);
|
|
151
|
+
const colors = {
|
|
152
|
+
reset: '\x1b[0m',
|
|
153
|
+
red: '\x1b[31m',
|
|
154
|
+
green: '\x1b[32m',
|
|
155
|
+
yellow: '\x1b[33m',
|
|
156
|
+
cyan: '\x1b[36m',
|
|
157
|
+
gray: '\x1b[90m'
|
|
158
|
+
};
|
|
159
|
+
console.log();
|
|
160
|
+
console.log(`${colors.red}╭─ ${title}${colors.reset}`);
|
|
161
|
+
if (meta.feature) {
|
|
162
|
+
console.log(`${colors.red}│${colors.reset} Feature: ${colors.cyan}${meta.feature}${colors.reset}`);
|
|
163
|
+
}
|
|
164
|
+
if (meta.file) {
|
|
165
|
+
console.log(`${colors.red}│${colors.reset} File: ${colors.yellow}${meta.file}${colors.reset}`);
|
|
166
|
+
}
|
|
167
|
+
console.log(`${colors.red}│${colors.reset}`);
|
|
168
|
+
// ✅ Enhanced error message with wrapping
|
|
169
|
+
const errorPrefix = `${colors.red}❌ ERROR:${colors.reset}`;
|
|
170
|
+
const wrappedMessage = this.wrapErrorMessage(message, errorPrefix);
|
|
171
|
+
wrappedMessage.forEach((line, index) => {
|
|
172
|
+
if (index === 0) {
|
|
173
|
+
console.log(`${colors.red}│${colors.reset} ${line}`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
console.log(`${colors.red}│${colors.reset}${line}`); // Continuation lines
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
// Show location/position if available
|
|
180
|
+
if (meta._location) {
|
|
181
|
+
console.log(`${colors.red}│${colors.reset} ${colors.cyan}📍 FROM:${colors.reset} ${meta._location}`);
|
|
182
|
+
}
|
|
183
|
+
// Extract and show line/position for syntax errors with wrapping
|
|
184
|
+
const lineMatch = message.match(/at line (\d+):(\d+)/i) || message.match(/line (\d+)/i);
|
|
185
|
+
if (lineMatch) {
|
|
186
|
+
console.log(`${colors.red}│${colors.reset} ${colors.cyan}📍 LINE:${colors.reset} ${lineMatch[1]}${lineMatch[2] ? `:${lineMatch[2]}` : ''}`);
|
|
187
|
+
}
|
|
188
|
+
// Show code context for syntax errors if available
|
|
189
|
+
if (message.includes('Unexpected token') || message.includes('SyntaxError')) {
|
|
190
|
+
const tokenMatch = message.match(/'([^']+)'/);
|
|
191
|
+
if (tokenMatch) {
|
|
192
|
+
console.log(`${colors.red}│${colors.reset} ${colors.cyan}📍 NEAR:${colors.reset} ...${tokenMatch[1]}...`);
|
|
193
|
+
console.log(`${colors.red}│${colors.reset} ${colors.red}^${colors.reset}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
console.log(`${colors.red}│${colors.reset}`);
|
|
197
|
+
// Show smart diagnostics
|
|
198
|
+
diagnostics.forEach(diagnostic => {
|
|
199
|
+
const icon = diagnostic.type === 'error' ? '✗' :
|
|
200
|
+
diagnostic.type === 'warning' ? '⚠' :
|
|
201
|
+
diagnostic.type === 'success' ? '✓' : 'ℹ';
|
|
202
|
+
const color = diagnostic.type === 'error' ? colors.red :
|
|
203
|
+
diagnostic.type === 'warning' ? colors.yellow :
|
|
204
|
+
diagnostic.type === 'success' ? colors.green : colors.cyan;
|
|
205
|
+
// Wrap diagnostic messages too
|
|
206
|
+
const diagnosticPrefix = `${color}${icon}${colors.reset}`;
|
|
207
|
+
const wrappedDiagnostic = this.wrapErrorMessage(diagnostic.message, diagnosticPrefix);
|
|
208
|
+
wrappedDiagnostic.forEach((line, index) => {
|
|
209
|
+
if (index === 0) {
|
|
210
|
+
console.log(`${colors.red}│${colors.reset} ${line}`);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
console.log(`${colors.red}│${colors.reset}${line}`);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
if (diagnostic.fix) {
|
|
217
|
+
const fixPrefix = `${colors.green}✓${colors.reset}`;
|
|
218
|
+
const wrappedFix = this.wrapErrorMessage(diagnostic.fix, fixPrefix);
|
|
219
|
+
wrappedFix.forEach((line, index) => {
|
|
220
|
+
if (index === 0) {
|
|
221
|
+
console.log(`${colors.red}│${colors.reset} ${line}`);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
console.log(`${colors.red}│${colors.reset}${line}`);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
const solutions = this.getSolutions(errorType, message);
|
|
230
|
+
if (solutions.length > 0) {
|
|
231
|
+
console.log(`${colors.red}│${colors.reset}`);
|
|
232
|
+
solutions.forEach(solution => {
|
|
233
|
+
const solutionPrefix = `${colors.green}✓${colors.reset}`;
|
|
234
|
+
const wrappedSolution = this.wrapErrorMessage(solution, solutionPrefix);
|
|
235
|
+
wrappedSolution.forEach((line, index) => {
|
|
236
|
+
if (index === 0) {
|
|
237
|
+
console.log(`${colors.red}│${colors.reset} ${line}`);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
console.log(`${colors.red}│${colors.reset}${line}`);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
// Enhanced fix message based on error type
|
|
246
|
+
const fixMessage = this.getFixMessage(errorType, message);
|
|
247
|
+
console.log(`${colors.red}╰─ FIX: ${colors.green}${fixMessage}${colors.reset}`);
|
|
248
|
+
console.log();
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Wrap long error messages with proper indentation and path handling
|
|
252
|
+
* @param message - The message to wrap
|
|
253
|
+
* @param prefix - The prefix (like "❌ ERROR:" or "✗") to account for in width
|
|
254
|
+
*/
|
|
255
|
+
wrapErrorMessage(message, prefix = '') {
|
|
256
|
+
const maxWidth = 85; // Terminal width accounting for borders
|
|
257
|
+
const indent = ' '; // 9 spaces for continuation lines
|
|
258
|
+
const prefixLength = this.stripAnsiCodes(prefix).length; // Account for prefix in first line
|
|
259
|
+
const firstLineMaxWidth = maxWidth - prefixLength - 1; // -1 for space after prefix
|
|
260
|
+
const continuationMaxWidth = maxWidth - indent.length;
|
|
261
|
+
const lines = [];
|
|
262
|
+
let currentLine = '';
|
|
263
|
+
let isFirstLine = true;
|
|
264
|
+
// Handle very long paths/URLs by splitting them intelligently
|
|
265
|
+
const processedMessage = this.preprocessLongPaths(message);
|
|
266
|
+
const words = processedMessage.split(' ');
|
|
267
|
+
for (const word of words) {
|
|
268
|
+
const currentMaxWidth = isFirstLine ? firstLineMaxWidth : continuationMaxWidth;
|
|
269
|
+
const proposedLine = currentLine + (currentLine ? ' ' : '') + word;
|
|
270
|
+
if (proposedLine.length > currentMaxWidth) {
|
|
271
|
+
// Current line would be too long
|
|
272
|
+
if (currentLine) {
|
|
273
|
+
// Save current line
|
|
274
|
+
if (isFirstLine) {
|
|
275
|
+
lines.push(`${prefix}${currentLine ? ' ' + currentLine : ''}`);
|
|
276
|
+
isFirstLine = false;
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
lines.push(`${indent}${currentLine}`);
|
|
280
|
+
}
|
|
281
|
+
currentLine = word;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
// Single word is too long, force it on its own line
|
|
285
|
+
if (isFirstLine) {
|
|
286
|
+
lines.push(`${prefix}${word ? ' ' + word : ''}`);
|
|
287
|
+
isFirstLine = false;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
lines.push(`${indent}${word}`);
|
|
291
|
+
}
|
|
292
|
+
currentLine = '';
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
currentLine = proposedLine;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Add remaining content
|
|
300
|
+
if (currentLine) {
|
|
301
|
+
if (isFirstLine) {
|
|
302
|
+
lines.push(`${prefix}${currentLine ? ' ' + currentLine : ''}`);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
lines.push(`${indent}${currentLine}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Handle empty prefix case
|
|
309
|
+
if (lines.length === 0) {
|
|
310
|
+
lines.push(prefix);
|
|
311
|
+
}
|
|
312
|
+
return lines;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Preprocess message to handle very long paths and URLs intelligently
|
|
316
|
+
*/
|
|
317
|
+
preprocessLongPaths(message) {
|
|
318
|
+
// Split very long file paths at natural breakpoints
|
|
319
|
+
return message.replace(/([\/\\][^\/\\:\s]{30,})/g, (match) => {
|
|
320
|
+
// Break long paths at directory separators
|
|
321
|
+
if (match.length > 60) {
|
|
322
|
+
const parts = match.split(/([\/\\])/);
|
|
323
|
+
let result = '';
|
|
324
|
+
let currentSegment = '';
|
|
325
|
+
for (const part of parts) {
|
|
326
|
+
if ((currentSegment + part).length > 50 && currentSegment) {
|
|
327
|
+
result += currentSegment + '\n' + part;
|
|
328
|
+
currentSegment = '';
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
currentSegment += part;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
result += currentSegment;
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
return match;
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Strip ANSI color codes to get actual text length
|
|
342
|
+
*/
|
|
343
|
+
stripAnsiCodes(text) {
|
|
344
|
+
return text.replace(/\x1b\[[0-9;]*m/g, '');
|
|
345
|
+
}
|
|
346
|
+
detectErrorType(message, meta) {
|
|
347
|
+
// Check meta for explicit category first
|
|
348
|
+
if (meta.category) {
|
|
349
|
+
return meta.category;
|
|
350
|
+
}
|
|
351
|
+
// Handle only the MAJOR ones we have smart diagnostics for
|
|
352
|
+
// Enhanced detection for import vs syntax errors
|
|
353
|
+
if (message.includes('Cannot find module')) {
|
|
354
|
+
return this.detectImportVsSyntax(message, meta);
|
|
355
|
+
}
|
|
356
|
+
// Direct syntax error detection
|
|
357
|
+
if (message.includes('SyntaxError') ||
|
|
358
|
+
message.includes('Unexpected token') ||
|
|
359
|
+
message.includes('Unexpected end of input')) {
|
|
360
|
+
return 'syntax';
|
|
361
|
+
}
|
|
362
|
+
// Startup/infrastructure errors
|
|
363
|
+
if (message.includes('EADDRINUSE') ||
|
|
364
|
+
message.includes('CONTRACT') ||
|
|
365
|
+
message.includes('port') ||
|
|
366
|
+
message.includes('startup')) {
|
|
367
|
+
return 'startup';
|
|
368
|
+
}
|
|
369
|
+
// Route registration errors
|
|
370
|
+
if (message.includes('ROUTE') || message.includes('registration')) {
|
|
371
|
+
return 'route';
|
|
372
|
+
}
|
|
373
|
+
// For everything else - just show the raw error with general handling
|
|
374
|
+
return 'general';
|
|
375
|
+
}
|
|
376
|
+
detectImportVsSyntax(message, meta) {
|
|
377
|
+
const modulePath = message.match(/Cannot find module '([^']+)'/)?.[1];
|
|
378
|
+
if (modulePath && meta.feature) {
|
|
379
|
+
// Try to resolve the actual file path
|
|
380
|
+
const basePath = process.cwd();
|
|
381
|
+
const featurePath = join(basePath, 'src', 'features', meta.feature);
|
|
382
|
+
// Build possible file paths
|
|
383
|
+
const possiblePaths = [
|
|
384
|
+
join(featurePath, modulePath + '.ts'),
|
|
385
|
+
join(featurePath, modulePath + '.js'),
|
|
386
|
+
join(featurePath, modulePath, 'index.ts'),
|
|
387
|
+
join(featurePath, modulePath, 'index.js'),
|
|
388
|
+
// Also try relative to the file that's importing
|
|
389
|
+
modulePath + '.ts',
|
|
390
|
+
modulePath + '.js'
|
|
391
|
+
];
|
|
392
|
+
// Check if any of these files exist
|
|
393
|
+
for (const filePath of possiblePaths) {
|
|
394
|
+
try {
|
|
395
|
+
if (existsSync(filePath)) {
|
|
396
|
+
// File exists but import failed - likely syntax error preventing import
|
|
397
|
+
return 'syntax';
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
// Ignore file system errors, continue checking
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// File doesn't exist - true import error
|
|
406
|
+
return 'import';
|
|
407
|
+
}
|
|
408
|
+
getErrorTitle(errorType) {
|
|
409
|
+
const titles = {
|
|
410
|
+
import: 'IMPORT ERROR',
|
|
411
|
+
syntax: 'SYNTAX ERROR',
|
|
412
|
+
startup: 'STARTUP ERROR',
|
|
413
|
+
contract: 'CONTRACT ERROR',
|
|
414
|
+
route: 'ROUTE ERROR',
|
|
415
|
+
filesystem: 'FILE SYSTEM ERROR',
|
|
416
|
+
general: 'ERROR'
|
|
417
|
+
};
|
|
418
|
+
return titles[errorType] || 'ERROR';
|
|
419
|
+
}
|
|
420
|
+
generateDiagnostics(message, meta, errorType) {
|
|
421
|
+
const diagnostics = [];
|
|
422
|
+
// Only provide smart diagnostics for errors we understand well
|
|
423
|
+
switch (errorType) {
|
|
424
|
+
case 'syntax':
|
|
425
|
+
if (message.includes('Unexpected token')) {
|
|
426
|
+
const tokenMatch = message.match(/Unexpected token '([^']+)'/);
|
|
427
|
+
if (tokenMatch) {
|
|
428
|
+
diagnostics.push({
|
|
429
|
+
type: 'error',
|
|
430
|
+
message: `Unexpected token: ${tokenMatch[1]}`,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (message.includes('Unexpected end of input')) {
|
|
435
|
+
diagnostics.push({
|
|
436
|
+
type: 'error',
|
|
437
|
+
message: 'Missing closing bracket, brace, or parenthesis',
|
|
438
|
+
fix: 'Check for unclosed {}, [], or () brackets'
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
// Line number extraction
|
|
442
|
+
const lineMatch = message.match(/line (\d+)/i);
|
|
443
|
+
if (lineMatch) {
|
|
444
|
+
diagnostics.push({
|
|
445
|
+
type: 'info',
|
|
446
|
+
message: `Error location: line ${lineMatch[1]}`
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
break;
|
|
450
|
+
case 'import':
|
|
451
|
+
const missingModule = message.match(/Cannot find module '([^']+)'/)?.[1];
|
|
452
|
+
if (missingModule) {
|
|
453
|
+
diagnostics.push({
|
|
454
|
+
type: 'error',
|
|
455
|
+
message: `Missing import: ${missingModule}`,
|
|
456
|
+
});
|
|
457
|
+
// Check for common typos
|
|
458
|
+
if (missingModule.includes('Service') && missingModule.endsWith('Servic')) {
|
|
459
|
+
diagnostics.push({
|
|
460
|
+
type: 'warning',
|
|
461
|
+
message: 'Looks like a typo: missing \'e\' at end?',
|
|
462
|
+
fix: `Try: ${missingModule}e`
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
if (missingModule.includes('/services/')) {
|
|
466
|
+
diagnostics.push({
|
|
467
|
+
type: 'info',
|
|
468
|
+
message: 'Check service file exists and name matches'
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
case 'startup':
|
|
474
|
+
if (message.includes('EADDRINUSE')) {
|
|
475
|
+
diagnostics.push({
|
|
476
|
+
type: 'error',
|
|
477
|
+
message: 'Port already in use',
|
|
478
|
+
fix: 'Change PORT environment variable or stop conflicting process'
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
if (message.includes('CONTRACT')) {
|
|
482
|
+
diagnostics.push({
|
|
483
|
+
type: 'error',
|
|
484
|
+
message: 'Contract validation failed',
|
|
485
|
+
fix: 'Run: npm run flux:check to see detailed contract issues'
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
break;
|
|
489
|
+
case 'route':
|
|
490
|
+
if (message.includes('export')) {
|
|
491
|
+
diagnostics.push({
|
|
492
|
+
type: 'error',
|
|
493
|
+
message: 'Route file must export a function as default',
|
|
494
|
+
fix: 'Add: export default router(...)'
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
break;
|
|
498
|
+
case 'general':
|
|
499
|
+
// For general errors, don't add diagnostics - just show the raw error
|
|
500
|
+
// This covers ReferenceError, TypeError, and all other JS errors
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
return diagnostics;
|
|
504
|
+
}
|
|
505
|
+
getFixMessage(errorType, message) {
|
|
506
|
+
// Pattern-based error analysis - extract meaning from the error message itself
|
|
507
|
+
// 1. SYNTAX ERRORS - Parse what's actually expected/unexpected
|
|
508
|
+
const expectedMatch = message.match(/Expected "(.+?)" but found/i);
|
|
509
|
+
if (expectedMatch) {
|
|
510
|
+
return `Add missing "${expectedMatch[1]}"`;
|
|
511
|
+
}
|
|
512
|
+
const unexpectedTokenMatch = message.match(/Unexpected token "(.+?)"/i) || message.match(/Unexpected "(.+?)"/i);
|
|
513
|
+
if (unexpectedTokenMatch) {
|
|
514
|
+
return `Remove or fix unexpected "${unexpectedTokenMatch[1]}"`;
|
|
515
|
+
}
|
|
516
|
+
if (message.match(/Unexpected end of input/i)) {
|
|
517
|
+
return 'Add missing closing bracket, brace, or parenthesis';
|
|
518
|
+
}
|
|
519
|
+
// 2. IMPORT/MODULE ERRORS - Extract the actual module path
|
|
520
|
+
const moduleMatch = message.match(/Cannot find module ['"']([^'"]+)['"']/);
|
|
521
|
+
if (moduleMatch) {
|
|
522
|
+
const modulePath = moduleMatch[1];
|
|
523
|
+
const fileName = modulePath.split('/').pop() || modulePath;
|
|
524
|
+
return `Check if file "${fileName}" exists at path: ${modulePath}`;
|
|
525
|
+
}
|
|
526
|
+
// 3. REFERENCE ERRORS - Extract the undefined variable/function
|
|
527
|
+
const refErrorMatch = message.match(/(\w+) is not defined/);
|
|
528
|
+
if (refErrorMatch) {
|
|
529
|
+
return `Define variable or import "${refErrorMatch[1]}"`;
|
|
530
|
+
}
|
|
531
|
+
// 4. TYPE ERRORS - Extract specific type issues
|
|
532
|
+
const notFunctionMatch = message.match(/(\w+) is not a function/);
|
|
533
|
+
if (notFunctionMatch) {
|
|
534
|
+
return `Check that "${notFunctionMatch[1]}" is actually a function`;
|
|
535
|
+
}
|
|
536
|
+
const cannotReadMatch = message.match(/Cannot read propert(?:y|ies) ['"]?(\w+)['"]? of/);
|
|
537
|
+
if (cannotReadMatch) {
|
|
538
|
+
return `Check object exists before accessing property "${cannotReadMatch[1]}"`;
|
|
539
|
+
}
|
|
540
|
+
const cannotAccessMatch = message.match(/Cannot access ['"]?(\w+)['"]? before initialization/);
|
|
541
|
+
if (cannotAccessMatch) {
|
|
542
|
+
return `Move "${cannotAccessMatch[1]}" declaration before usage`;
|
|
543
|
+
}
|
|
544
|
+
// 5. NETWORK/CONNECTION ERRORS - Extract connection details
|
|
545
|
+
const econnrefusedMatch = message.match(/ECONNREFUSED.*?(\d+)/);
|
|
546
|
+
if (econnrefusedMatch) {
|
|
547
|
+
return `Connection refused on port ${econnrefusedMatch[1]} - check if service is running`;
|
|
548
|
+
}
|
|
549
|
+
if (message.match(/EADDRINUSE.*?(\d+)/)) {
|
|
550
|
+
const portMatch = message.match(/(\d+)/);
|
|
551
|
+
return portMatch ? `Port ${portMatch[1]} already in use - choose different port` : 'Port already in use';
|
|
552
|
+
}
|
|
553
|
+
// 6. FILE SYSTEM ERRORS - Extract file/directory info
|
|
554
|
+
const enoentMatch = message.match(/ENOENT.*?['"]([^'"]+)['"]/) || message.match(/no such file or directory.*?['"]([^'"]+)['"]/);
|
|
555
|
+
if (enoentMatch) {
|
|
556
|
+
return `File not found: ${enoentMatch[1]}`;
|
|
557
|
+
}
|
|
558
|
+
const eaccesMatch = message.match(/EACCES.*?['"]([^'"]+)['"]/) || message.match(/permission denied.*?['"]([^'"]+)['"]/);
|
|
559
|
+
if (eaccesMatch) {
|
|
560
|
+
return `Permission denied accessing: ${eaccesMatch[1]}`;
|
|
561
|
+
}
|
|
562
|
+
// 7. COMPILATION/TRANSFORM ERRORS - Extract compilation context
|
|
563
|
+
if (message.match(/Transform failed|compilation failed/i)) {
|
|
564
|
+
const lineMatch = message.match(/line (\d+)/i) || message.match(/:(\d+):/);
|
|
565
|
+
return lineMatch ? `Fix compilation error at line ${lineMatch[1]}` : 'Fix compilation/syntax errors';
|
|
566
|
+
}
|
|
567
|
+
// 8. PROMISE/ASYNC ERRORS
|
|
568
|
+
if (message.match(/UnhandledPromiseRejection/i)) {
|
|
569
|
+
return 'Add .catch() handler or try/catch block for async operation';
|
|
570
|
+
}
|
|
571
|
+
// 9. EXPORT/IMPORT ERRORS - Extract export context
|
|
572
|
+
if (message.match(/default.*?export/i) || message.match(/export.*?default/i)) {
|
|
573
|
+
return 'Fix export default statement syntax';
|
|
574
|
+
}
|
|
575
|
+
// 10. LINE-SPECIFIC ERRORS - Extract line numbers when available
|
|
576
|
+
const lineNumberMatch = message.match(/(?:line |:)(\d+)(?::(\d+))?/i);
|
|
577
|
+
if (lineNumberMatch) {
|
|
578
|
+
const line = lineNumberMatch[1];
|
|
579
|
+
const column = lineNumberMatch[2];
|
|
580
|
+
const location = column ? `line ${line}, column ${column}` : `line ${line}`;
|
|
581
|
+
if (message.match(/syntax|unexpected|expected/i)) {
|
|
582
|
+
return `Fix syntax error at ${location}`;
|
|
583
|
+
}
|
|
584
|
+
return `Check code at ${location}`;
|
|
585
|
+
}
|
|
586
|
+
// 11. GENERIC PATTERNS - Fallback pattern matching
|
|
587
|
+
if (message.match(/not found|missing/i)) {
|
|
588
|
+
return 'Check that required files/resources exist';
|
|
589
|
+
}
|
|
590
|
+
if (message.match(/timeout|timed out/i)) {
|
|
591
|
+
return 'Increase timeout or check network connectivity';
|
|
592
|
+
}
|
|
593
|
+
if (message.match(/invalid|malformed/i)) {
|
|
594
|
+
return 'Check data format and syntax';
|
|
595
|
+
}
|
|
596
|
+
if (message.match(/denied|forbidden/i)) {
|
|
597
|
+
return 'Check permissions and access rights';
|
|
598
|
+
}
|
|
599
|
+
// 12. FRAMEWORK-AGNOSTIC FALLBACKS by error type
|
|
600
|
+
switch (errorType) {
|
|
601
|
+
case 'import':
|
|
602
|
+
return 'Verify import paths and file existence';
|
|
603
|
+
case 'syntax':
|
|
604
|
+
return 'Fix JavaScript/TypeScript syntax errors';
|
|
605
|
+
case 'startup':
|
|
606
|
+
return 'Fix configuration or dependency issues';
|
|
607
|
+
case 'route':
|
|
608
|
+
return 'Check export statements and file structure';
|
|
609
|
+
case 'general':
|
|
610
|
+
return 'Review error details above';
|
|
611
|
+
default:
|
|
612
|
+
return 'Fix the error described above';
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Get contextual solutions based on error message patterns
|
|
617
|
+
*/
|
|
618
|
+
getSolutions(errorType, message) {
|
|
619
|
+
const solutions = [];
|
|
620
|
+
// Extract solutions based on actual error content
|
|
621
|
+
if (message.match(/Expected "(.+?)" but found/)) {
|
|
622
|
+
solutions.push('Check for missing brackets, parentheses, or braces');
|
|
623
|
+
solutions.push('Verify function call syntax');
|
|
624
|
+
}
|
|
625
|
+
if (message.match(/Cannot find module/)) {
|
|
626
|
+
solutions.push('Check if file exists at the specified path');
|
|
627
|
+
solutions.push('Verify import path spelling');
|
|
628
|
+
solutions.push('Check file extensions (.js, .ts)');
|
|
629
|
+
}
|
|
630
|
+
if (message.match(/is not defined/)) {
|
|
631
|
+
solutions.push('Import the required module or variable');
|
|
632
|
+
solutions.push('Check variable name spelling');
|
|
633
|
+
solutions.push('Verify variable is declared before use');
|
|
634
|
+
}
|
|
635
|
+
if (message.match(/is not a function/)) {
|
|
636
|
+
solutions.push('Check that imported item is actually a function');
|
|
637
|
+
solutions.push('Verify function is exported correctly');
|
|
638
|
+
}
|
|
639
|
+
if (message.match(/EADDRINUSE/)) {
|
|
640
|
+
solutions.push('Change to a different port number');
|
|
641
|
+
solutions.push('Stop the process using this port');
|
|
642
|
+
solutions.push('Check for other running servers');
|
|
643
|
+
}
|
|
644
|
+
if (message.match(/line \d+/i)) {
|
|
645
|
+
const lineMatch = message.match(/line (\d+)/i);
|
|
646
|
+
if (lineMatch) {
|
|
647
|
+
solutions.push(`Check syntax around line ${lineMatch[1]}`);
|
|
648
|
+
solutions.push('Look for missing commas or semicolons');
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
// Add generic solutions if none were found
|
|
652
|
+
if (solutions.length === 0) {
|
|
653
|
+
const defaultSolutions = {
|
|
654
|
+
import: ['Verify file paths and existence', 'Check import syntax'],
|
|
655
|
+
syntax: ['Fix JavaScript/TypeScript syntax', 'Check for missing punctuation'],
|
|
656
|
+
startup: ['Review configuration files', 'Check dependencies'],
|
|
657
|
+
route: ['Verify export statements', 'Check file structure'],
|
|
658
|
+
general: ['Review error message details', 'Check recent code changes']
|
|
659
|
+
};
|
|
660
|
+
solutions.push(...(defaultSolutions[errorType] || defaultSolutions.general));
|
|
661
|
+
}
|
|
662
|
+
return solutions;
|
|
663
|
+
}
|
|
664
|
+
// ============================================================================
|
|
665
|
+
// EXISTING HELPER METHODS
|
|
666
|
+
// ============================================================================
|
|
667
|
+
getCaller() {
|
|
668
|
+
const stack = new Error().stack;
|
|
669
|
+
if (!stack)
|
|
670
|
+
return 'unknown';
|
|
671
|
+
const lines = stack.split('\n');
|
|
672
|
+
const callerLine = lines[4] || '';
|
|
673
|
+
const match = callerLine.match(/\((.+):(\d+):(\d+)\)/) ||
|
|
674
|
+
callerLine.match(/at\s+(.+):(\d+):(\d+)/);
|
|
675
|
+
if (match) {
|
|
676
|
+
const [, filePath, lineNumber] = match;
|
|
677
|
+
const cleanPath = filePath.replace(process.cwd() + '/', '');
|
|
678
|
+
return `${cleanPath}:${lineNumber}`;
|
|
679
|
+
}
|
|
680
|
+
return 'unknown';
|
|
681
|
+
}
|
|
682
|
+
log(level, message, meta) {
|
|
683
|
+
if (LOG_LEVELS[level] > this.levelValue) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
const entry = {
|
|
687
|
+
timestamp: new Date().toISOString(),
|
|
688
|
+
level,
|
|
689
|
+
message: message || '',
|
|
690
|
+
...this.defaultMeta,
|
|
691
|
+
...meta,
|
|
692
|
+
};
|
|
693
|
+
this.writeToTransports(entry);
|
|
694
|
+
}
|
|
695
|
+
writeToTransports(entry) {
|
|
696
|
+
const writePromises = [];
|
|
697
|
+
for (const [name, transport] of this.transports) {
|
|
698
|
+
try {
|
|
699
|
+
if (transport.shouldLog && !transport.shouldLog(entry.level, this.level)) {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
const result = transport.write(entry);
|
|
703
|
+
if (result && typeof result.then === 'function') {
|
|
704
|
+
writePromises.push(result.catch((error) => {
|
|
705
|
+
console.error(`Transport ${name} write failed:`, error.message);
|
|
706
|
+
}));
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
catch (error) {
|
|
710
|
+
console.error(`Transport ${name} write error:`, error.message);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
this.pendingWrites = writePromises;
|
|
714
|
+
}
|
|
715
|
+
// ============================================================================
|
|
716
|
+
// PUBLIC UTILITY METHODS
|
|
717
|
+
// ============================================================================
|
|
718
|
+
/**
|
|
719
|
+
* Flush pending logs to all transports
|
|
720
|
+
* @llm-rule WHEN: App shutdown or ensuring logs are persisted before critical operations
|
|
721
|
+
* @llm-rule AVOID: Frequent flushing during normal operations - impacts performance
|
|
722
|
+
*/
|
|
723
|
+
async flush() {
|
|
724
|
+
if (this.pendingWrites.length > 0) {
|
|
725
|
+
try {
|
|
726
|
+
await Promise.all(this.pendingWrites);
|
|
727
|
+
}
|
|
728
|
+
catch {
|
|
729
|
+
// Errors already handled in writeToTransports
|
|
730
|
+
}
|
|
731
|
+
this.pendingWrites = [];
|
|
732
|
+
}
|
|
733
|
+
const flushPromises = [];
|
|
734
|
+
for (const [name, transport] of this.transports) {
|
|
735
|
+
if (transport.flush) {
|
|
736
|
+
try {
|
|
737
|
+
const result = transport.flush();
|
|
738
|
+
if (result) {
|
|
739
|
+
flushPromises.push(result.catch((error) => {
|
|
740
|
+
console.error(`Transport ${name} flush failed:`, error.message);
|
|
741
|
+
}));
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
catch (error) {
|
|
745
|
+
console.error(`Transport ${name} flush error:`, error.message);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
if (flushPromises.length > 0) {
|
|
750
|
+
await Promise.all(flushPromises);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Close logger and cleanup all transports
|
|
755
|
+
* @llm-rule WHEN: App shutdown or logger cleanup - ensures graceful resource cleanup
|
|
756
|
+
* @llm-rule AVOID: Calling during normal operations - this permanently closes the logger
|
|
757
|
+
*/
|
|
758
|
+
async close() {
|
|
759
|
+
await this.flush();
|
|
760
|
+
const closePromises = [];
|
|
761
|
+
for (const [name, transport] of this.transports) {
|
|
762
|
+
if (transport.close) {
|
|
763
|
+
try {
|
|
764
|
+
const result = transport.close();
|
|
765
|
+
if (result) {
|
|
766
|
+
closePromises.push(result.catch((error) => {
|
|
767
|
+
console.error(`Transport ${name} close failed:`, error.message);
|
|
768
|
+
}));
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
catch (error) {
|
|
772
|
+
console.error(`Transport ${name} close error:`, error.message);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (closePromises.length > 0) {
|
|
777
|
+
await Promise.all(closePromises);
|
|
778
|
+
}
|
|
779
|
+
this.transports.clear();
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Get list of active transport names
|
|
783
|
+
* @llm-rule WHEN: Debugging logger setup or checking which transports are running
|
|
784
|
+
* @llm-rule AVOID: Using for business logic - this is for debugging and monitoring only
|
|
785
|
+
*/
|
|
786
|
+
getActiveTransports() {
|
|
787
|
+
return Array.from(this.transports.keys());
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Check if specific transport is active
|
|
791
|
+
* @llm-rule WHEN: Conditionally logging based on transport availability
|
|
792
|
+
* @llm-rule AVOID: Complex transport detection - just log normally, transports auto-enable
|
|
793
|
+
*/
|
|
794
|
+
hasTransport(name) {
|
|
795
|
+
return this.transports.has(name);
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Set minimum log level dynamically
|
|
799
|
+
* @llm-rule WHEN: Runtime log level changes, debugging, or feature flags
|
|
800
|
+
* @llm-rule AVOID: Frequent level changes - impacts performance
|
|
801
|
+
*/
|
|
802
|
+
setLevel(level) {
|
|
803
|
+
this.level = level;
|
|
804
|
+
this.levelValue = LOG_LEVELS[level];
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Get current minimum log level
|
|
808
|
+
* @llm-rule WHEN: Debugging configuration or checking level settings
|
|
809
|
+
* @llm-rule AVOID: Using for filtering logic - logger handles level filtering automatically
|
|
810
|
+
*/
|
|
811
|
+
getLevel() {
|
|
812
|
+
return this.level;
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Check if specific log level would be written
|
|
816
|
+
* @llm-rule WHEN: Expensive log message preparation - check before building complex meta
|
|
817
|
+
* @llm-rule AVOID: Normal logging - level filtering is automatic and fast
|
|
818
|
+
*/
|
|
819
|
+
isLevelEnabled(level) {
|
|
820
|
+
return LOG_LEVELS[level] <= this.levelValue;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Get current logger configuration for debugging
|
|
824
|
+
* @llm-rule WHEN: Debugging logger setup, checking environment detection, or monitoring
|
|
825
|
+
* @llm-rule AVOID: Using for runtime business logic - configuration is set at startup
|
|
826
|
+
*/
|
|
827
|
+
getConfig() {
|
|
828
|
+
return {
|
|
829
|
+
level: this.level,
|
|
830
|
+
scope: this.config.scope,
|
|
831
|
+
minimal: this.config.minimal,
|
|
832
|
+
transports: this.getActiveTransports(),
|
|
833
|
+
service: this.config.service,
|
|
834
|
+
environment: {
|
|
835
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
836
|
+
hasDbUrl: !!process.env.DATABASE_URL,
|
|
837
|
+
hasHttpUrl: !!process.env.VOILA_LOGGER_HTTP_URL,
|
|
838
|
+
hasWebhookUrl: !!process.env.VOILA_LOGGER_WEBHOOK_URL,
|
|
839
|
+
},
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
//# sourceMappingURL=logger.js.map
|