gthinking 1.2.1 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/.eslintrc.js +34 -0
  2. package/ANALYSIS_SUMMARY.md +363 -0
  3. package/README.md +230 -245
  4. package/dist/analysis/analysis-engine.d.ts +63 -0
  5. package/dist/analysis/analysis-engine.d.ts.map +1 -0
  6. package/dist/analysis/analysis-engine.js +322 -0
  7. package/dist/analysis/analysis-engine.js.map +1 -0
  8. package/dist/core/config.d.ts +1419 -0
  9. package/dist/core/config.d.ts.map +1 -0
  10. package/dist/core/config.js +361 -0
  11. package/dist/core/config.js.map +1 -0
  12. package/dist/core/engine.d.ts +176 -0
  13. package/dist/core/engine.d.ts.map +1 -0
  14. package/dist/core/engine.js +604 -0
  15. package/dist/core/engine.js.map +1 -0
  16. package/dist/core/errors.d.ts +153 -0
  17. package/dist/core/errors.d.ts.map +1 -0
  18. package/dist/core/errors.js +287 -0
  19. package/dist/core/errors.js.map +1 -0
  20. package/dist/core/index.d.ts +7 -0
  21. package/dist/core/index.d.ts.map +1 -0
  22. package/dist/{types.js → core/index.js} +8 -4
  23. package/dist/core/index.js.map +1 -0
  24. package/dist/core/pipeline.d.ts +121 -0
  25. package/dist/core/pipeline.d.ts.map +1 -0
  26. package/dist/core/pipeline.js +289 -0
  27. package/dist/core/pipeline.js.map +1 -0
  28. package/dist/core/rate-limiter.d.ts +58 -0
  29. package/dist/core/rate-limiter.d.ts.map +1 -0
  30. package/dist/core/rate-limiter.js +133 -0
  31. package/dist/core/rate-limiter.js.map +1 -0
  32. package/dist/core/session-manager.d.ts +96 -0
  33. package/dist/core/session-manager.d.ts.map +1 -0
  34. package/dist/core/session-manager.js +223 -0
  35. package/dist/core/session-manager.js.map +1 -0
  36. package/dist/creativity/creativity-engine.d.ts +6 -0
  37. package/dist/creativity/creativity-engine.d.ts.map +1 -0
  38. package/dist/creativity/creativity-engine.js +17 -0
  39. package/dist/creativity/creativity-engine.js.map +1 -0
  40. package/dist/index.d.ts +24 -32
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +130 -104
  43. package/dist/index.js.map +1 -1
  44. package/dist/learning/learning-engine.d.ts +6 -0
  45. package/dist/learning/learning-engine.d.ts.map +1 -0
  46. package/dist/learning/learning-engine.js +17 -0
  47. package/dist/learning/learning-engine.js.map +1 -0
  48. package/dist/llm/index.d.ts +10 -0
  49. package/dist/llm/index.d.ts.map +1 -0
  50. package/dist/llm/index.js +26 -0
  51. package/dist/llm/index.js.map +1 -0
  52. package/dist/llm/llm-service.d.ts +109 -0
  53. package/dist/llm/llm-service.d.ts.map +1 -0
  54. package/dist/llm/llm-service.js +224 -0
  55. package/dist/llm/llm-service.js.map +1 -0
  56. package/dist/llm/providers/base.d.ts +85 -0
  57. package/dist/llm/providers/base.d.ts.map +1 -0
  58. package/dist/llm/providers/base.js +57 -0
  59. package/dist/llm/providers/base.js.map +1 -0
  60. package/dist/llm/providers/cli.d.ts +23 -0
  61. package/dist/llm/providers/cli.d.ts.map +1 -0
  62. package/dist/llm/providers/cli.js +158 -0
  63. package/dist/llm/providers/cli.js.map +1 -0
  64. package/dist/llm/providers/gemini.d.ts +30 -0
  65. package/dist/llm/providers/gemini.d.ts.map +1 -0
  66. package/dist/llm/providers/gemini.js +168 -0
  67. package/dist/llm/providers/gemini.js.map +1 -0
  68. package/dist/llm/sanitization.d.ts +50 -0
  69. package/dist/llm/sanitization.d.ts.map +1 -0
  70. package/dist/llm/sanitization.js +149 -0
  71. package/dist/llm/sanitization.js.map +1 -0
  72. package/dist/{server.d.ts.map → mcp/server.d.ts.map} +1 -1
  73. package/dist/mcp/server.js +108 -0
  74. package/dist/mcp/server.js.map +1 -0
  75. package/dist/planning/planning-engine.d.ts +6 -0
  76. package/dist/planning/planning-engine.d.ts.map +1 -0
  77. package/dist/planning/planning-engine.js +17 -0
  78. package/dist/planning/planning-engine.js.map +1 -0
  79. package/dist/reasoning/reasoning-engine.d.ts +6 -0
  80. package/dist/reasoning/reasoning-engine.d.ts.map +1 -0
  81. package/dist/reasoning/reasoning-engine.js +17 -0
  82. package/dist/reasoning/reasoning-engine.js.map +1 -0
  83. package/dist/search/search-engine.d.ts +99 -0
  84. package/dist/search/search-engine.d.ts.map +1 -0
  85. package/dist/search/search-engine.js +271 -0
  86. package/dist/search/search-engine.js.map +1 -0
  87. package/dist/synthesis/synthesis-engine.d.ts +6 -0
  88. package/dist/synthesis/synthesis-engine.d.ts.map +1 -0
  89. package/dist/synthesis/synthesis-engine.js +17 -0
  90. package/dist/synthesis/synthesis-engine.js.map +1 -0
  91. package/dist/types/analysis.d.ts +1534 -49
  92. package/dist/types/analysis.d.ts.map +1 -1
  93. package/dist/types/analysis.js +250 -0
  94. package/dist/types/analysis.js.map +1 -1
  95. package/dist/types/core.d.ts +257 -30
  96. package/dist/types/core.d.ts.map +1 -1
  97. package/dist/types/core.js +148 -18
  98. package/dist/types/core.js.map +1 -1
  99. package/dist/types/creativity.d.ts +2871 -56
  100. package/dist/types/creativity.d.ts.map +1 -1
  101. package/dist/types/creativity.js +195 -0
  102. package/dist/types/creativity.js.map +1 -1
  103. package/dist/types/index.d.ts +6 -2
  104. package/dist/types/index.d.ts.map +1 -1
  105. package/dist/types/index.js +17 -2
  106. package/dist/types/index.js.map +1 -1
  107. package/dist/types/learning.d.ts +851 -61
  108. package/dist/types/learning.d.ts.map +1 -1
  109. package/dist/types/learning.js +155 -0
  110. package/dist/types/learning.js.map +1 -1
  111. package/dist/types/planning.d.ts +2223 -71
  112. package/dist/types/planning.d.ts.map +1 -1
  113. package/dist/types/planning.js +190 -0
  114. package/dist/types/planning.js.map +1 -1
  115. package/dist/types/reasoning.d.ts +2209 -72
  116. package/dist/types/reasoning.d.ts.map +1 -1
  117. package/dist/types/reasoning.js +200 -1
  118. package/dist/types/reasoning.js.map +1 -1
  119. package/dist/types/search.d.ts +981 -53
  120. package/dist/types/search.d.ts.map +1 -1
  121. package/dist/types/search.js +137 -0
  122. package/dist/types/search.js.map +1 -1
  123. package/dist/types/synthesis.d.ts +583 -37
  124. package/dist/types/synthesis.d.ts.map +1 -1
  125. package/dist/types/synthesis.js +138 -0
  126. package/dist/types/synthesis.js.map +1 -1
  127. package/dist/utils/cache.d.ts +144 -0
  128. package/dist/utils/cache.d.ts.map +1 -0
  129. package/dist/utils/cache.js +288 -0
  130. package/dist/utils/cache.js.map +1 -0
  131. package/dist/utils/id-generator.d.ts +89 -0
  132. package/dist/utils/id-generator.d.ts.map +1 -0
  133. package/dist/utils/id-generator.js +132 -0
  134. package/dist/utils/id-generator.js.map +1 -0
  135. package/dist/utils/index.d.ts +11 -0
  136. package/dist/utils/index.d.ts.map +1 -0
  137. package/dist/utils/index.js +33 -0
  138. package/dist/utils/index.js.map +1 -0
  139. package/dist/utils/logger.d.ts +142 -0
  140. package/dist/utils/logger.d.ts.map +1 -0
  141. package/dist/utils/logger.js +248 -0
  142. package/dist/utils/logger.js.map +1 -0
  143. package/dist/utils/metrics.d.ts +149 -0
  144. package/dist/utils/metrics.d.ts.map +1 -0
  145. package/dist/utils/metrics.js +296 -0
  146. package/dist/utils/metrics.js.map +1 -0
  147. package/dist/utils/timer.d.ts +7 -0
  148. package/dist/utils/timer.d.ts.map +1 -0
  149. package/dist/utils/timer.js +17 -0
  150. package/dist/utils/timer.js.map +1 -0
  151. package/dist/utils/validation.d.ts +147 -0
  152. package/dist/utils/validation.d.ts.map +1 -0
  153. package/dist/utils/validation.js +275 -0
  154. package/dist/utils/validation.js.map +1 -0
  155. package/docs/API.md +411 -0
  156. package/docs/ARCHITECTURE.md +271 -0
  157. package/docs/CHANGELOG.md +283 -0
  158. package/jest.config.js +28 -0
  159. package/package.json +43 -30
  160. package/src/analysis/analysis-engine.ts +383 -0
  161. package/src/core/config.ts +406 -0
  162. package/src/core/engine.ts +785 -0
  163. package/src/core/errors.ts +349 -0
  164. package/src/core/index.ts +12 -0
  165. package/src/core/pipeline.ts +424 -0
  166. package/src/core/rate-limiter.ts +155 -0
  167. package/src/core/session-manager.ts +269 -0
  168. package/src/creativity/creativity-engine.ts +14 -0
  169. package/src/index.ts +178 -0
  170. package/src/learning/learning-engine.ts +14 -0
  171. package/src/llm/index.ts +10 -0
  172. package/src/llm/llm-service.ts +285 -0
  173. package/src/llm/providers/base.ts +146 -0
  174. package/src/llm/providers/cli.ts +186 -0
  175. package/src/llm/providers/gemini.ts +201 -0
  176. package/src/llm/sanitization.ts +178 -0
  177. package/src/mcp/server.ts +117 -0
  178. package/src/planning/planning-engine.ts +14 -0
  179. package/src/reasoning/reasoning-engine.ts +14 -0
  180. package/src/search/search-engine.ts +333 -0
  181. package/src/synthesis/synthesis-engine.ts +14 -0
  182. package/src/types/analysis.ts +337 -0
  183. package/src/types/core.ts +342 -0
  184. package/src/types/creativity.ts +268 -0
  185. package/src/types/index.ts +31 -0
  186. package/src/types/learning.ts +215 -0
  187. package/src/types/planning.ts +251 -0
  188. package/src/types/reasoning.ts +288 -0
  189. package/src/types/search.ts +192 -0
  190. package/src/types/synthesis.ts +187 -0
  191. package/src/utils/cache.ts +363 -0
  192. package/src/utils/id-generator.ts +135 -0
  193. package/src/utils/index.ts +22 -0
  194. package/src/utils/logger.ts +290 -0
  195. package/src/utils/metrics.ts +380 -0
  196. package/src/utils/timer.ts +15 -0
  197. package/src/utils/validation.ts +297 -0
  198. package/tests/setup.ts +22 -0
  199. package/tests/unit/cache.test.ts +189 -0
  200. package/tests/unit/engine.test.ts +179 -0
  201. package/tests/unit/validation.test.ts +218 -0
  202. package/tsconfig.json +17 -12
  203. package/GEMINI.md +0 -68
  204. package/analysis.ts +0 -1063
  205. package/creativity.ts +0 -1055
  206. package/dist/analysis.d.ts +0 -54
  207. package/dist/analysis.d.ts.map +0 -1
  208. package/dist/analysis.js +0 -866
  209. package/dist/analysis.js.map +0 -1
  210. package/dist/creativity.d.ts +0 -81
  211. package/dist/creativity.d.ts.map +0 -1
  212. package/dist/creativity.js +0 -828
  213. package/dist/creativity.js.map +0 -1
  214. package/dist/engine.d.ts +0 -90
  215. package/dist/engine.d.ts.map +0 -1
  216. package/dist/engine.js +0 -677
  217. package/dist/engine.js.map +0 -1
  218. package/dist/examples.d.ts +0 -7
  219. package/dist/examples.d.ts.map +0 -1
  220. package/dist/examples.js +0 -506
  221. package/dist/examples.js.map +0 -1
  222. package/dist/learning.d.ts +0 -72
  223. package/dist/learning.d.ts.map +0 -1
  224. package/dist/learning.js +0 -615
  225. package/dist/learning.js.map +0 -1
  226. package/dist/llm-service.d.ts +0 -21
  227. package/dist/llm-service.d.ts.map +0 -1
  228. package/dist/llm-service.js +0 -100
  229. package/dist/llm-service.js.map +0 -1
  230. package/dist/planning.d.ts +0 -58
  231. package/dist/planning.d.ts.map +0 -1
  232. package/dist/planning.js +0 -824
  233. package/dist/planning.js.map +0 -1
  234. package/dist/reasoning.d.ts +0 -73
  235. package/dist/reasoning.d.ts.map +0 -1
  236. package/dist/reasoning.js +0 -845
  237. package/dist/reasoning.js.map +0 -1
  238. package/dist/search-discovery.d.ts +0 -73
  239. package/dist/search-discovery.d.ts.map +0 -1
  240. package/dist/search-discovery.js +0 -548
  241. package/dist/search-discovery.js.map +0 -1
  242. package/dist/server.js +0 -113
  243. package/dist/server.js.map +0 -1
  244. package/dist/types/engine.d.ts +0 -55
  245. package/dist/types/engine.d.ts.map +0 -1
  246. package/dist/types/engine.js +0 -3
  247. package/dist/types/engine.js.map +0 -1
  248. package/dist/types.d.ts +0 -6
  249. package/dist/types.d.ts.map +0 -1
  250. package/dist/types.js.map +0 -1
  251. package/engine.ts +0 -947
  252. package/examples.ts +0 -717
  253. package/index.ts +0 -106
  254. package/learning.ts +0 -779
  255. package/llm-service.ts +0 -120
  256. package/planning.ts +0 -1028
  257. package/reasoning.ts +0 -1079
  258. package/search-discovery.ts +0 -700
  259. package/server.ts +0 -115
  260. package/types/analysis.ts +0 -69
  261. package/types/core.ts +0 -90
  262. package/types/creativity.ts +0 -72
  263. package/types/engine.ts +0 -60
  264. package/types/index.ts +0 -9
  265. package/types/learning.ts +0 -69
  266. package/types/planning.ts +0 -85
  267. package/types/reasoning.ts +0 -92
  268. package/types/search.ts +0 -58
  269. package/types/synthesis.ts +0 -42
  270. package/types.ts +0 -6
  271. /package/dist/{server.d.ts → mcp/server.d.ts} +0 -0
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Logger Utility for gthinking v2.0.0
3
+ * Structured logging with Winston
4
+ */
5
+
6
+ import winston from 'winston';
7
+
8
+ /**
9
+ * Log levels
10
+ */
11
+ export const LogLevel = {
12
+ ERROR: 'error',
13
+ WARN: 'warn',
14
+ INFO: 'info',
15
+ DEBUG: 'debug',
16
+ } as const;
17
+
18
+ export type LogLevelType = typeof LogLevel[keyof typeof LogLevel];
19
+
20
+ /**
21
+ * Logger configuration interface
22
+ */
23
+ export interface LoggerConfig {
24
+ level: LogLevelType;
25
+ enableConsole: boolean;
26
+ enableFile: boolean;
27
+ logDir?: string;
28
+ maxFiles?: number;
29
+ maxSize?: string;
30
+ }
31
+
32
+ /**
33
+ * Default logger configuration
34
+ */
35
+ const defaultConfig: LoggerConfig = {
36
+ level: 'info',
37
+ enableConsole: true,
38
+ enableFile: false,
39
+ };
40
+
41
+ /**
42
+ * Create a Winston logger instance
43
+ * @param config - Logger configuration
44
+ * @returns Winston logger instance
45
+ */
46
+ export function createLogger(config: Partial<LoggerConfig> = {}): winston.Logger {
47
+ const mergedConfig = { ...defaultConfig, ...config };
48
+
49
+ const transports: winston.transport[] = [];
50
+
51
+ if (mergedConfig.enableConsole) {
52
+ transports.push(new winston.transports.Console({
53
+ format: winston.format.combine(
54
+ winston.format.colorize(),
55
+ winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
56
+ winston.format.printf(({ level, message, timestamp, ...metadata }) => {
57
+ let msg = `${timestamp} [${level}]: ${message}`;
58
+ if (Object.keys(metadata).length > 0) {
59
+ msg += ` ${JSON.stringify(metadata)}`;
60
+ }
61
+ return msg;
62
+ })
63
+ ),
64
+ }));
65
+ }
66
+
67
+ if (mergedConfig.enableFile && mergedConfig.logDir) {
68
+ transports.push(new winston.transports.File({
69
+ filename: `${mergedConfig.logDir}/error.log`,
70
+ level: 'error',
71
+ maxFiles: mergedConfig.maxFiles || 5,
72
+ maxsize: (mergedConfig.maxSize || '10m') as any,
73
+ format: winston.format.combine(
74
+ winston.format.timestamp(),
75
+ winston.format.json()
76
+ ),
77
+ }));
78
+
79
+ transports.push(new winston.transports.File({
80
+ filename: `${mergedConfig.logDir}/combined.log`,
81
+ maxFiles: mergedConfig.maxFiles || 5,
82
+ maxsize: (mergedConfig.maxSize || '10m') as any,
83
+ format: winston.format.combine(
84
+ winston.format.timestamp(),
85
+ winston.format.json()
86
+ ),
87
+ }));
88
+ }
89
+
90
+ return winston.createLogger({
91
+ level: mergedConfig.level,
92
+ defaultMeta: { service: 'gthinking' },
93
+ transports,
94
+ exitOnError: false,
95
+ });
96
+ }
97
+
98
+ // Create default logger instance
99
+ let defaultLogger: winston.Logger = createLogger();
100
+
101
+ /**
102
+ * Set the default logger
103
+ * @param logger - The logger to set as default
104
+ */
105
+ export function setDefaultLogger(logger: winston.Logger): void {
106
+ defaultLogger = logger;
107
+ }
108
+
109
+ /**
110
+ * Get the default logger
111
+ * @returns The default logger instance
112
+ */
113
+ export function getDefaultLogger(): winston.Logger {
114
+ return defaultLogger;
115
+ }
116
+
117
+ /**
118
+ * Log an error message
119
+ * @param message - The error message
120
+ * @param metadata - Additional metadata
121
+ */
122
+ export function error(message: string, metadata: Record<string, unknown> = {}): void {
123
+ defaultLogger.error(message, metadata);
124
+ }
125
+
126
+ /**
127
+ * Log a warning message
128
+ * @param message - The warning message
129
+ * @param metadata - Additional metadata
130
+ */
131
+ export function warn(message: string, metadata: Record<string, unknown> = {}): void {
132
+ defaultLogger.warn(message, metadata);
133
+ }
134
+
135
+ /**
136
+ * Log an info message
137
+ * @param message - The info message
138
+ * @param metadata - Additional metadata
139
+ */
140
+ export function info(message: string, metadata: Record<string, unknown> = {}): void {
141
+ defaultLogger.info(message, metadata);
142
+ }
143
+
144
+ /**
145
+ * Log a debug message
146
+ * @param message - The debug message
147
+ * @param metadata - Additional metadata
148
+ */
149
+ export function debug(message: string, metadata: Record<string, unknown> = {}): void {
150
+ defaultLogger.debug(message, metadata);
151
+ }
152
+
153
+ /**
154
+ * Create a child logger with default metadata
155
+ * @param defaultMetadata - Default metadata to include
156
+ * @returns Child logger
157
+ */
158
+ export function createChildLogger(defaultMetadata: Record<string, unknown>): winston.Logger {
159
+ return defaultLogger.child(defaultMetadata);
160
+ }
161
+
162
+ /**
163
+ * Logger class for component-specific logging
164
+ */
165
+ export class Logger {
166
+ private logger: winston.Logger;
167
+ private component: string;
168
+
169
+ constructor(component: string, config?: Partial<LoggerConfig>) {
170
+ this.component = component;
171
+ this.logger = createLogger(config);
172
+ }
173
+
174
+ /**
175
+ * Log an error message
176
+ */
177
+ public error(message: string, metadata: Record<string, unknown> = {}): void {
178
+ this.logger.error(message, { component: this.component, ...metadata });
179
+ }
180
+
181
+ /**
182
+ * Log a warning message
183
+ */
184
+ public warn(message: string, metadata: Record<string, unknown> = {}): void {
185
+ this.logger.warn(message, { component: this.component, ...metadata });
186
+ }
187
+
188
+ /**
189
+ * Log an info message
190
+ */
191
+ public info(message: string, metadata: Record<string, unknown> = {}): void {
192
+ this.logger.info(message, { component: this.component, ...metadata });
193
+ }
194
+
195
+ /**
196
+ * Log a debug message
197
+ */
198
+ public debug(message: string, metadata: Record<string, unknown> = {}): void {
199
+ this.logger.debug(message, { component: this.component, ...metadata });
200
+ }
201
+
202
+ /**
203
+ * Create a child logger
204
+ */
205
+ public child(metadata: Record<string, unknown>): winston.Logger {
206
+ return this.logger.child({ component: this.component, ...metadata });
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Performance logger for tracking operation performance
212
+ */
213
+ export class PerformanceLogger {
214
+ private logger: winston.Logger;
215
+ private timings: Map<string, number> = new Map();
216
+
217
+ constructor() {
218
+ this.logger = createLogger({ level: 'debug' });
219
+ }
220
+
221
+ /**
222
+ * Start timing an operation
223
+ * @param operation - The operation name
224
+ */
225
+ public start(operation: string): void {
226
+ this.timings.set(operation, Date.now());
227
+ }
228
+
229
+ /**
230
+ * End timing an operation and log the duration
231
+ * @param operation - The operation name
232
+ * @param metadata - Additional metadata
233
+ */
234
+ public end(operation: string, metadata: Record<string, unknown> = {}): number {
235
+ const startTime = this.timings.get(operation);
236
+ if (!startTime) {
237
+ this.logger.warn(`No start time found for operation: ${operation}`);
238
+ return 0;
239
+ }
240
+
241
+ const duration = Date.now() - startTime;
242
+ this.timings.delete(operation);
243
+
244
+ this.logger.debug(`Operation ${operation} completed`, {
245
+ operation,
246
+ duration,
247
+ ...metadata,
248
+ });
249
+
250
+ return duration;
251
+ }
252
+
253
+ /**
254
+ * Log a performance metric
255
+ * @param metric - The metric name
256
+ * @param value - The metric value
257
+ * @param metadata - Additional metadata
258
+ */
259
+ public logMetric(metric: string, value: number, metadata: Record<string, unknown> = {}): void {
260
+ this.logger.debug(`Performance metric: ${metric}`, {
261
+ metric,
262
+ value,
263
+ ...metadata,
264
+ });
265
+ }
266
+
267
+ /**
268
+ * Clear all timings
269
+ */
270
+ public clear(): void {
271
+ this.timings.clear();
272
+ }
273
+ }
274
+
275
+ // Export a singleton performance logger
276
+ export const performanceLogger = new PerformanceLogger();
277
+
278
+ /**
279
+ * Default logger instance
280
+ */
281
+ export const logger = defaultLogger;
282
+
283
+ /**
284
+ * Create a component logger
285
+ * @param component - Component name
286
+ * @returns Logger instance
287
+ */
288
+ export function createComponentLogger(component: string): Logger {
289
+ return new Logger(component);
290
+ }
@@ -0,0 +1,380 @@
1
+ /**
2
+ * Metrics Utility for gthinking v2.0.0
3
+ * Performance metrics and monitoring
4
+ */
5
+
6
+ import { EventEmitter } from 'events';
7
+ import { Logger } from './logger';
8
+
9
+ const logger = new Logger('Metrics');
10
+
11
+ /**
12
+ * Metric types
13
+ */
14
+ export type MetricType = 'counter' | 'gauge' | 'histogram' | 'timer';
15
+
16
+ /**
17
+ * Metric value type
18
+ */
19
+ export type MetricValue = number | Record<string, number>;
20
+
21
+ /**
22
+ * Metric interface
23
+ */
24
+ export interface Metric {
25
+ name: string;
26
+ type: MetricType;
27
+ value: MetricValue;
28
+ labels: Record<string, string>;
29
+ timestamp: number;
30
+ }
31
+
32
+ /**
33
+ * Timer metric interface
34
+ */
35
+ export interface TimerMetric {
36
+ startTime: number;
37
+ endTime?: number;
38
+ duration?: number;
39
+ }
40
+
41
+ /**
42
+ * Histogram bucket interface
43
+ */
44
+ export interface HistogramBucket {
45
+ upperBound: number;
46
+ count: number;
47
+ }
48
+
49
+ /**
50
+ * Metrics collector class
51
+ */
52
+ export class MetricsCollector extends EventEmitter {
53
+ private metrics: Map<string, Metric> = new Map();
54
+ private timers: Map<string, TimerMetric> = new Map();
55
+ private histograms: Map<string, number[]> = new Map();
56
+ private enabled = true;
57
+
58
+ /**
59
+ * Enable or disable metrics collection
60
+ * @param enabled - Whether to enable metrics
61
+ */
62
+ public setEnabled(enabled: boolean): void {
63
+ this.enabled = enabled;
64
+ }
65
+
66
+ /**
67
+ * Record a counter metric
68
+ * @param name - Metric name
69
+ * @param value - Counter value (default: 1)
70
+ * @param labels - Metric labels
71
+ */
72
+ public counter(name: string, value = 1, labels: Record<string, string> = {}): void {
73
+ if (!this.enabled) return;
74
+
75
+ const key = this.getMetricKey(name, labels);
76
+ const existing = this.metrics.get(key);
77
+
78
+ if (existing && existing.type === 'counter') {
79
+ existing.value = (existing.value as number) + value;
80
+ existing.timestamp = Date.now();
81
+ } else {
82
+ this.metrics.set(key, {
83
+ name,
84
+ type: 'counter',
85
+ value,
86
+ labels,
87
+ timestamp: Date.now(),
88
+ });
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Record a gauge metric
94
+ * @param name - Metric name
95
+ * @param value - Gauge value
96
+ * @param labels - Metric labels
97
+ */
98
+ public gauge(name: string, value: number, labels: Record<string, string> = {}): void {
99
+ if (!this.enabled) return;
100
+
101
+ const key = this.getMetricKey(name, labels);
102
+ this.metrics.set(key, {
103
+ name,
104
+ type: 'gauge',
105
+ value,
106
+ labels,
107
+ timestamp: Date.now(),
108
+ });
109
+ }
110
+
111
+ /**
112
+ * Start a timer
113
+ * @param name - Timer name
114
+ * @param labels - Timer labels
115
+ */
116
+ public startTimer(name: string, labels: Record<string, string> = {}): string {
117
+ const key = this.getMetricKey(name, labels);
118
+ this.timers.set(key, { startTime: Date.now() });
119
+ return key;
120
+ }
121
+
122
+ /**
123
+ * End a timer and record the duration
124
+ * @param key - Timer key returned by startTimer
125
+ * @returns Duration in milliseconds
126
+ */
127
+ public endTimer(key: string): number {
128
+ const timer = this.timers.get(key);
129
+ if (!timer) {
130
+ logger.warn(`Timer not found: ${key}`);
131
+ return 0;
132
+ }
133
+
134
+ timer.endTime = Date.now();
135
+ timer.duration = timer.endTime - timer.startTime;
136
+ this.timers.delete(key);
137
+
138
+ // Record as histogram
139
+ this.histogram(key, timer.duration);
140
+
141
+ return timer.duration;
142
+ }
143
+
144
+ /**
145
+ * Record a histogram value
146
+ * @param name - Histogram name
147
+ * @param value - Value to record
148
+ * @param labels - Histogram labels
149
+ */
150
+ public histogram(name: string, value: number, labels: Record<string, string> = {}): void {
151
+ if (!this.enabled) return;
152
+
153
+ const key = this.getMetricKey(name, labels);
154
+ const existing = this.histograms.get(key);
155
+
156
+ if (existing) {
157
+ existing.push(value);
158
+ } else {
159
+ this.histograms.set(key, [value]);
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Get all metrics
165
+ * @returns Array of all metrics
166
+ */
167
+ public getMetrics(): Metric[] {
168
+ return Array.from(this.metrics.values());
169
+ }
170
+
171
+ /**
172
+ * Get metrics by name
173
+ * @param name - Metric name
174
+ * @returns Array of metrics with the given name
175
+ */
176
+ public getMetricsByName(name: string): Metric[] {
177
+ return this.getMetrics().filter(m => m.name === name);
178
+ }
179
+
180
+ /**
181
+ * Get histogram statistics
182
+ * @param name - Histogram name
183
+ * @param labels - Histogram labels
184
+ * @returns Histogram statistics
185
+ */
186
+ public getHistogramStats(name: string, labels: Record<string, string> = {}): {
187
+ count: number;
188
+ sum: number;
189
+ min: number;
190
+ max: number;
191
+ mean: number;
192
+ p50: number;
193
+ p95: number;
194
+ p99: number;
195
+ } | null {
196
+ const key = this.getMetricKey(name, labels);
197
+ const values = this.histograms.get(key);
198
+
199
+ if (!values || values.length === 0) {
200
+ return null;
201
+ }
202
+
203
+ const sorted = [...values].sort((a, b) => a - b);
204
+ const count = sorted.length;
205
+ const sum = sorted.reduce((a, b) => a + b, 0);
206
+ const min = sorted[0];
207
+ const max = sorted[count - 1];
208
+ const mean = sum / count;
209
+
210
+ const percentile = (p: number): number => {
211
+ const index = Math.ceil((p / 100) * count) - 1;
212
+ return sorted[Math.max(0, index)];
213
+ };
214
+
215
+ return {
216
+ count,
217
+ sum,
218
+ min,
219
+ max,
220
+ mean,
221
+ p50: percentile(50),
222
+ p95: percentile(95),
223
+ p99: percentile(99),
224
+ };
225
+ }
226
+
227
+ /**
228
+ * Clear all metrics
229
+ */
230
+ public clear(): void {
231
+ this.metrics.clear();
232
+ this.timers.clear();
233
+ this.histograms.clear();
234
+ }
235
+
236
+ /**
237
+ * Export metrics in Prometheus format
238
+ * @returns Metrics in Prometheus format
239
+ */
240
+ public exportPrometheus(): string {
241
+ const lines: string[] = [];
242
+
243
+ for (const metric of this.metrics.values()) {
244
+ const labels = Object.entries(metric.labels)
245
+ .map(([k, v]) => `${k}="${v}"`)
246
+ .join(',');
247
+
248
+ const labelStr = labels ? `{${labels}}` : '';
249
+ lines.push(`${metric.name}${labelStr} ${metric.value}`);
250
+ }
251
+
252
+ for (const [key, values] of this.histograms.entries()) {
253
+ const [name, ...labelParts] = key.split(':');
254
+ const labels = labelParts.join(':');
255
+ const stats = this.getHistogramStats(name, labels ? JSON.parse(labels) : {});
256
+
257
+ if (stats) {
258
+ lines.push(`${name}_count${labels ? `{${labels}}` : ''} ${stats.count}`);
259
+ lines.push(`${name}_sum${labels ? `{${labels}}` : ''} ${stats.sum}`);
260
+ }
261
+ }
262
+
263
+ return lines.join('\n');
264
+ }
265
+
266
+ /**
267
+ * Export metrics as JSON
268
+ * @returns Metrics as JSON object
269
+ */
270
+ public exportJSON(): Record<string, unknown> {
271
+ const result: Record<string, unknown> = {
272
+ counters: {},
273
+ gauges: {},
274
+ histograms: {},
275
+ timestamps: {},
276
+ };
277
+
278
+ for (const metric of this.metrics.values()) {
279
+ const key = this.getMetricKey(metric.name, metric.labels);
280
+
281
+ if (metric.type === 'counter') {
282
+ (result.counters as Record<string, number>)[key] = metric.value as number;
283
+ } else if (metric.type === 'gauge') {
284
+ (result.gauges as Record<string, number>)[key] = metric.value as number;
285
+ }
286
+
287
+ (result.timestamps as Record<string, number>)[key] = metric.timestamp;
288
+ }
289
+
290
+ for (const [key, values] of this.histograms.entries()) {
291
+ const [name, ...labelParts] = key.split(':');
292
+ const labels = labelParts.join(':');
293
+ const stats = this.getHistogramStats(name, labels ? JSON.parse(labels) : {});
294
+
295
+ if (stats) {
296
+ (result.histograms as Record<string, unknown>)[key] = stats;
297
+ }
298
+ }
299
+
300
+ return result;
301
+ }
302
+
303
+ /**
304
+ * Generate a metric key from name and labels
305
+ * @param name - Metric name
306
+ * @param labels - Metric labels
307
+ * @returns Metric key
308
+ */
309
+ private getMetricKey(name: string, labels: Record<string, string>): string {
310
+ if (Object.keys(labels).length === 0) {
311
+ return name;
312
+ }
313
+ return `${name}:${JSON.stringify(labels)}`;
314
+ }
315
+ }
316
+
317
+ // Export a singleton instance
318
+ export const metricsCollector = new MetricsCollector();
319
+
320
+ /**
321
+ * Decorator for timing function execution
322
+ * @param metricName - The metric name to use
323
+ * @returns Method decorator
324
+ */
325
+ export function timed(metricName?: string) {
326
+ return function (
327
+ target: unknown,
328
+ propertyKey: string,
329
+ descriptor: PropertyDescriptor
330
+ ): void {
331
+ const originalMethod = descriptor.value;
332
+ const name = metricName || `${target.constructor.name}_${propertyKey}`;
333
+
334
+ descriptor.value = function (...args: unknown[]) {
335
+ const timerKey = metricsCollector.startTimer(name);
336
+
337
+ try {
338
+ const result = originalMethod.apply(this, args);
339
+
340
+ if (result instanceof Promise) {
341
+ return result.finally(() => {
342
+ metricsCollector.endTimer(timerKey);
343
+ });
344
+ } else {
345
+ metricsCollector.endTimer(timerKey);
346
+ return result;
347
+ }
348
+ } catch (error) {
349
+ metricsCollector.endTimer(timerKey);
350
+ throw error;
351
+ }
352
+ };
353
+ };
354
+ }
355
+
356
+ /**
357
+ * Track operation metrics
358
+ * @param operation - Operation name
359
+ * @param stage - Operation stage
360
+ * @param fn - Function to track
361
+ * @returns Function result
362
+ */
363
+ export async function trackOperation<T>(
364
+ operation: string,
365
+ stage: string,
366
+ fn: () => Promise<T>
367
+ ): Promise<T> {
368
+ const timerKey = metricsCollector.startTimer(`operation_${operation}`, { stage });
369
+
370
+ try {
371
+ const result = await fn();
372
+ metricsCollector.counter(`operation_${operation}_success`, 1, { stage });
373
+ return result;
374
+ } catch (error) {
375
+ metricsCollector.counter(`operation_${operation}_error`, 1, { stage });
376
+ throw error;
377
+ } finally {
378
+ metricsCollector.endTimer(timerKey);
379
+ }
380
+ }
@@ -0,0 +1,15 @@
1
+ export class Timer {
2
+ private start: number;
3
+
4
+ constructor() {
5
+ this.start = Date.now();
6
+ }
7
+
8
+ public elapsed(): number {
9
+ return Date.now() - this.start;
10
+ }
11
+
12
+ public reset(): void {
13
+ this.start = Date.now();
14
+ }
15
+ }