servcraft 0.1.0 → 0.1.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.
Files changed (217) hide show
  1. package/.claude/settings.local.json +30 -0
  2. package/.github/CODEOWNERS +18 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +46 -0
  4. package/.github/dependabot.yml +59 -0
  5. package/.github/workflows/ci.yml +188 -0
  6. package/.github/workflows/release.yml +195 -0
  7. package/AUDIT.md +602 -0
  8. package/LICENSE +21 -0
  9. package/README.md +1102 -1
  10. package/dist/cli/index.cjs +2026 -2168
  11. package/dist/cli/index.cjs.map +1 -1
  12. package/dist/cli/index.js +2026 -2168
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/index.cjs +595 -616
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +114 -52
  17. package/dist/index.d.ts +114 -52
  18. package/dist/index.js +595 -616
  19. package/dist/index.js.map +1 -1
  20. package/docs/CLI-001_MULTI_DB_PLAN.md +546 -0
  21. package/docs/DATABASE_MULTI_ORM.md +399 -0
  22. package/docs/PHASE1_BREAKDOWN.md +346 -0
  23. package/docs/PROGRESS.md +550 -0
  24. package/docs/modules/ANALYTICS.md +226 -0
  25. package/docs/modules/API-VERSIONING.md +252 -0
  26. package/docs/modules/AUDIT.md +192 -0
  27. package/docs/modules/AUTH.md +431 -0
  28. package/docs/modules/CACHE.md +346 -0
  29. package/docs/modules/EMAIL.md +254 -0
  30. package/docs/modules/FEATURE-FLAG.md +291 -0
  31. package/docs/modules/I18N.md +294 -0
  32. package/docs/modules/MEDIA-PROCESSING.md +281 -0
  33. package/docs/modules/MFA.md +266 -0
  34. package/docs/modules/NOTIFICATION.md +311 -0
  35. package/docs/modules/OAUTH.md +237 -0
  36. package/docs/modules/PAYMENT.md +804 -0
  37. package/docs/modules/QUEUE.md +540 -0
  38. package/docs/modules/RATE-LIMIT.md +339 -0
  39. package/docs/modules/SEARCH.md +288 -0
  40. package/docs/modules/SECURITY.md +327 -0
  41. package/docs/modules/SESSION.md +382 -0
  42. package/docs/modules/SWAGGER.md +305 -0
  43. package/docs/modules/UPLOAD.md +296 -0
  44. package/docs/modules/USER.md +505 -0
  45. package/docs/modules/VALIDATION.md +294 -0
  46. package/docs/modules/WEBHOOK.md +270 -0
  47. package/docs/modules/WEBSOCKET.md +691 -0
  48. package/package.json +53 -38
  49. package/prisma/schema.prisma +395 -1
  50. package/src/cli/commands/add-module.ts +520 -87
  51. package/src/cli/commands/db.ts +3 -4
  52. package/src/cli/commands/docs.ts +256 -6
  53. package/src/cli/commands/generate.ts +12 -19
  54. package/src/cli/commands/init.ts +384 -214
  55. package/src/cli/index.ts +0 -4
  56. package/src/cli/templates/repository.ts +6 -1
  57. package/src/cli/templates/routes.ts +6 -21
  58. package/src/cli/utils/docs-generator.ts +6 -7
  59. package/src/cli/utils/env-manager.ts +717 -0
  60. package/src/cli/utils/field-parser.ts +16 -7
  61. package/src/cli/utils/interactive-prompt.ts +223 -0
  62. package/src/cli/utils/template-manager.ts +346 -0
  63. package/src/config/database.config.ts +183 -0
  64. package/src/config/env.ts +0 -10
  65. package/src/config/index.ts +0 -14
  66. package/src/core/server.ts +1 -1
  67. package/src/database/adapters/mongoose.adapter.ts +132 -0
  68. package/src/database/adapters/prisma.adapter.ts +118 -0
  69. package/src/database/connection.ts +190 -0
  70. package/src/database/interfaces/database.interface.ts +85 -0
  71. package/src/database/interfaces/index.ts +7 -0
  72. package/src/database/interfaces/repository.interface.ts +129 -0
  73. package/src/database/models/mongoose/index.ts +7 -0
  74. package/src/database/models/mongoose/payment.schema.ts +347 -0
  75. package/src/database/models/mongoose/user.schema.ts +154 -0
  76. package/src/database/prisma.ts +1 -4
  77. package/src/database/redis.ts +101 -0
  78. package/src/database/repositories/mongoose/index.ts +7 -0
  79. package/src/database/repositories/mongoose/payment.repository.ts +380 -0
  80. package/src/database/repositories/mongoose/user.repository.ts +255 -0
  81. package/src/database/seed.ts +6 -1
  82. package/src/index.ts +9 -20
  83. package/src/middleware/security.ts +2 -6
  84. package/src/modules/analytics/analytics.routes.ts +80 -0
  85. package/src/modules/analytics/analytics.service.ts +364 -0
  86. package/src/modules/analytics/index.ts +18 -0
  87. package/src/modules/analytics/types.ts +180 -0
  88. package/src/modules/api-versioning/index.ts +15 -0
  89. package/src/modules/api-versioning/types.ts +86 -0
  90. package/src/modules/api-versioning/versioning.middleware.ts +120 -0
  91. package/src/modules/api-versioning/versioning.routes.ts +54 -0
  92. package/src/modules/api-versioning/versioning.service.ts +189 -0
  93. package/src/modules/audit/audit.repository.ts +206 -0
  94. package/src/modules/audit/audit.service.ts +27 -59
  95. package/src/modules/auth/auth.controller.ts +2 -2
  96. package/src/modules/auth/auth.middleware.ts +3 -9
  97. package/src/modules/auth/auth.routes.ts +10 -107
  98. package/src/modules/auth/auth.service.ts +126 -23
  99. package/src/modules/auth/index.ts +3 -4
  100. package/src/modules/cache/cache.service.ts +367 -0
  101. package/src/modules/cache/index.ts +10 -0
  102. package/src/modules/cache/types.ts +44 -0
  103. package/src/modules/email/email.service.ts +3 -10
  104. package/src/modules/email/templates.ts +2 -8
  105. package/src/modules/feature-flag/feature-flag.repository.ts +303 -0
  106. package/src/modules/feature-flag/feature-flag.routes.ts +247 -0
  107. package/src/modules/feature-flag/feature-flag.service.ts +566 -0
  108. package/src/modules/feature-flag/index.ts +20 -0
  109. package/src/modules/feature-flag/types.ts +192 -0
  110. package/src/modules/i18n/i18n.middleware.ts +186 -0
  111. package/src/modules/i18n/i18n.routes.ts +191 -0
  112. package/src/modules/i18n/i18n.service.ts +456 -0
  113. package/src/modules/i18n/index.ts +18 -0
  114. package/src/modules/i18n/types.ts +118 -0
  115. package/src/modules/media-processing/index.ts +17 -0
  116. package/src/modules/media-processing/media-processing.routes.ts +111 -0
  117. package/src/modules/media-processing/media-processing.service.ts +245 -0
  118. package/src/modules/media-processing/types.ts +156 -0
  119. package/src/modules/mfa/index.ts +20 -0
  120. package/src/modules/mfa/mfa.repository.ts +206 -0
  121. package/src/modules/mfa/mfa.routes.ts +595 -0
  122. package/src/modules/mfa/mfa.service.ts +572 -0
  123. package/src/modules/mfa/totp.ts +150 -0
  124. package/src/modules/mfa/types.ts +57 -0
  125. package/src/modules/notification/index.ts +20 -0
  126. package/src/modules/notification/notification.repository.ts +356 -0
  127. package/src/modules/notification/notification.service.ts +483 -0
  128. package/src/modules/notification/types.ts +119 -0
  129. package/src/modules/oauth/index.ts +20 -0
  130. package/src/modules/oauth/oauth.repository.ts +219 -0
  131. package/src/modules/oauth/oauth.routes.ts +446 -0
  132. package/src/modules/oauth/oauth.service.ts +293 -0
  133. package/src/modules/oauth/providers/apple.provider.ts +250 -0
  134. package/src/modules/oauth/providers/facebook.provider.ts +181 -0
  135. package/src/modules/oauth/providers/github.provider.ts +248 -0
  136. package/src/modules/oauth/providers/google.provider.ts +189 -0
  137. package/src/modules/oauth/providers/twitter.provider.ts +214 -0
  138. package/src/modules/oauth/types.ts +94 -0
  139. package/src/modules/payment/index.ts +19 -0
  140. package/src/modules/payment/payment.repository.ts +733 -0
  141. package/src/modules/payment/payment.routes.ts +390 -0
  142. package/src/modules/payment/payment.service.ts +354 -0
  143. package/src/modules/payment/providers/mobile-money.provider.ts +274 -0
  144. package/src/modules/payment/providers/paypal.provider.ts +190 -0
  145. package/src/modules/payment/providers/stripe.provider.ts +215 -0
  146. package/src/modules/payment/types.ts +140 -0
  147. package/src/modules/queue/cron.ts +438 -0
  148. package/src/modules/queue/index.ts +87 -0
  149. package/src/modules/queue/queue.routes.ts +600 -0
  150. package/src/modules/queue/queue.service.ts +842 -0
  151. package/src/modules/queue/types.ts +222 -0
  152. package/src/modules/queue/workers.ts +366 -0
  153. package/src/modules/rate-limit/index.ts +59 -0
  154. package/src/modules/rate-limit/rate-limit.middleware.ts +134 -0
  155. package/src/modules/rate-limit/rate-limit.routes.ts +269 -0
  156. package/src/modules/rate-limit/rate-limit.service.ts +348 -0
  157. package/src/modules/rate-limit/stores/memory.store.ts +165 -0
  158. package/src/modules/rate-limit/stores/redis.store.ts +322 -0
  159. package/src/modules/rate-limit/types.ts +153 -0
  160. package/src/modules/search/adapters/elasticsearch.adapter.ts +326 -0
  161. package/src/modules/search/adapters/meilisearch.adapter.ts +261 -0
  162. package/src/modules/search/adapters/memory.adapter.ts +278 -0
  163. package/src/modules/search/index.ts +21 -0
  164. package/src/modules/search/search.service.ts +234 -0
  165. package/src/modules/search/types.ts +214 -0
  166. package/src/modules/security/index.ts +40 -0
  167. package/src/modules/security/sanitize.ts +223 -0
  168. package/src/modules/security/security-audit.service.ts +388 -0
  169. package/src/modules/security/security.middleware.ts +398 -0
  170. package/src/modules/session/index.ts +3 -0
  171. package/src/modules/session/session.repository.ts +159 -0
  172. package/src/modules/session/session.service.ts +340 -0
  173. package/src/modules/session/types.ts +38 -0
  174. package/src/modules/swagger/index.ts +7 -1
  175. package/src/modules/swagger/schema-builder.ts +16 -4
  176. package/src/modules/swagger/swagger.service.ts +9 -10
  177. package/src/modules/swagger/types.ts +0 -2
  178. package/src/modules/upload/index.ts +14 -0
  179. package/src/modules/upload/types.ts +83 -0
  180. package/src/modules/upload/upload.repository.ts +199 -0
  181. package/src/modules/upload/upload.routes.ts +311 -0
  182. package/src/modules/upload/upload.service.ts +448 -0
  183. package/src/modules/user/index.ts +3 -3
  184. package/src/modules/user/user.controller.ts +15 -9
  185. package/src/modules/user/user.repository.ts +237 -113
  186. package/src/modules/user/user.routes.ts +39 -164
  187. package/src/modules/user/user.service.ts +4 -3
  188. package/src/modules/validation/validator.ts +12 -17
  189. package/src/modules/webhook/index.ts +91 -0
  190. package/src/modules/webhook/retry.ts +196 -0
  191. package/src/modules/webhook/signature.ts +135 -0
  192. package/src/modules/webhook/types.ts +181 -0
  193. package/src/modules/webhook/webhook.repository.ts +358 -0
  194. package/src/modules/webhook/webhook.routes.ts +442 -0
  195. package/src/modules/webhook/webhook.service.ts +457 -0
  196. package/src/modules/websocket/features.ts +504 -0
  197. package/src/modules/websocket/index.ts +106 -0
  198. package/src/modules/websocket/middlewares.ts +298 -0
  199. package/src/modules/websocket/types.ts +181 -0
  200. package/src/modules/websocket/websocket.service.ts +692 -0
  201. package/src/utils/errors.ts +7 -0
  202. package/src/utils/pagination.ts +4 -1
  203. package/tests/helpers/db-check.ts +79 -0
  204. package/tests/integration/auth-redis.test.ts +94 -0
  205. package/tests/integration/cache-redis.test.ts +387 -0
  206. package/tests/integration/mongoose-repositories.test.ts +410 -0
  207. package/tests/integration/payment-prisma.test.ts +637 -0
  208. package/tests/integration/queue-bullmq.test.ts +417 -0
  209. package/tests/integration/user-prisma.test.ts +441 -0
  210. package/tests/integration/websocket-socketio.test.ts +552 -0
  211. package/tests/setup.ts +11 -9
  212. package/vitest.config.ts +3 -8
  213. package/npm-cache/_cacache/content-v2/sha512/1c/d0/03440d500a0487621aad1d6402978340698976602046db8e24fa03c01ee6c022c69b0582f969042d9442ee876ac35c038e960dd427d1e622fa24b8eb7dba +0 -0
  214. package/npm-cache/_cacache/content-v2/sha512/42/55/28b493ca491833e5aab0e9c3108d29ab3f36c248ca88f45d4630674fce9130959e56ae308797ac2b6328fa7f09a610b9550ed09cb971d039876d293fc69d +0 -0
  215. package/npm-cache/_cacache/content-v2/sha512/e0/12/f360dc9315ee5f17844a0c8c233ee6bf7c30837c4a02ea0d56c61c7f7ab21c0e958e50ed2c57c59f983c762b93056778c9009b2398ffc26def0183999b13 +0 -0
  216. package/npm-cache/_cacache/content-v2/sha512/ed/b0/fae1161902898f4c913c67d7f6cdf6be0665aec3b389b9c4f4f0a101ca1da59badf1b59c4e0030f5223023b8d63cfe501c46a32c20c895d4fb3f11ca2232 +0 -0
  217. package/npm-cache/_cacache/index-v5/58/94/c2cba79e0f16b4c10e95a87e32255741149e8222cc314a476aab67c39cc0 +0 -5
@@ -0,0 +1,364 @@
1
+ import { logger } from '../../core/logger.js';
2
+ import type {
3
+ AnalyticsConfig,
4
+ Metric,
5
+ Counter,
6
+ Gauge,
7
+ Histogram,
8
+ AnalyticsEvent,
9
+ MetricQuery,
10
+ MetricResult,
11
+ } from './types.js';
12
+
13
+ /**
14
+ * Analytics Service
15
+ * Metrics collection and monitoring
16
+ */
17
+ export class AnalyticsService {
18
+ private config: AnalyticsConfig;
19
+ private counters = new Map<string, Counter>();
20
+ private gauges = new Map<string, Gauge>();
21
+ private histograms = new Map<string, Histogram>();
22
+ private events: AnalyticsEvent[] = [];
23
+ private metrics: Metric[] = [];
24
+
25
+ constructor(config: AnalyticsConfig = {}) {
26
+ this.config = {
27
+ enabled: true,
28
+ prefix: 'app',
29
+ defaultLabels: {},
30
+ prometheusEnabled: true,
31
+ flushInterval: 60000,
32
+ ...config,
33
+ };
34
+
35
+ if (this.config.flushInterval && this.config.flushInterval > 0) {
36
+ setInterval(() => this.flush(), this.config.flushInterval);
37
+ }
38
+
39
+ logger.info('Analytics service initialized');
40
+ }
41
+
42
+ /**
43
+ * Increment counter
44
+ */
45
+ incrementCounter(name: string, value = 1, labels: Record<string, string> = {}): void {
46
+ if (!this.config.enabled) return;
47
+
48
+ const fullName = this.getMetricName(name);
49
+ const key = this.getMetricKey(fullName, labels);
50
+
51
+ let counter = this.counters.get(key);
52
+ if (!counter) {
53
+ counter = {
54
+ name: fullName,
55
+ value: 0,
56
+ labels: { ...this.config.defaultLabels, ...labels },
57
+ };
58
+ this.counters.set(key, counter);
59
+ }
60
+
61
+ counter.value += value;
62
+
63
+ this.recordMetric({
64
+ name: fullName,
65
+ type: 'counter',
66
+ value: counter.value,
67
+ labels: counter.labels,
68
+ timestamp: new Date(),
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Set gauge value
74
+ */
75
+ setGauge(name: string, value: number, labels: Record<string, string> = {}): void {
76
+ if (!this.config.enabled) return;
77
+
78
+ const fullName = this.getMetricName(name);
79
+ const key = this.getMetricKey(fullName, labels);
80
+
81
+ const gauge: Gauge = {
82
+ name: fullName,
83
+ value,
84
+ labels: { ...this.config.defaultLabels, ...labels },
85
+ };
86
+
87
+ this.gauges.set(key, gauge);
88
+
89
+ this.recordMetric({
90
+ name: fullName,
91
+ type: 'gauge',
92
+ value,
93
+ labels: gauge.labels,
94
+ timestamp: new Date(),
95
+ });
96
+ }
97
+
98
+ /**
99
+ * Observe value for histogram
100
+ */
101
+ observeHistogram(
102
+ name: string,
103
+ value: number,
104
+ buckets: number[] = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
105
+ labels: Record<string, string> = {}
106
+ ): void {
107
+ if (!this.config.enabled) return;
108
+
109
+ const fullName = this.getMetricName(name);
110
+ const key = this.getMetricKey(fullName, labels);
111
+
112
+ let histogram = this.histograms.get(key);
113
+ if (!histogram) {
114
+ histogram = {
115
+ name: fullName,
116
+ buckets: new Map(),
117
+ sum: 0,
118
+ count: 0,
119
+ labels: { ...this.config.defaultLabels, ...labels },
120
+ };
121
+
122
+ // Initialize buckets
123
+ buckets.forEach((bucket) => histogram!.buckets.set(bucket, 0));
124
+ this.histograms.set(key, histogram);
125
+ }
126
+
127
+ // Update buckets
128
+ buckets.forEach((bucket) => {
129
+ if (value <= bucket) {
130
+ const current = histogram!.buckets.get(bucket) || 0;
131
+ histogram!.buckets.set(bucket, current + 1);
132
+ }
133
+ });
134
+
135
+ histogram.sum += value;
136
+ histogram.count += 1;
137
+
138
+ this.recordMetric({
139
+ name: fullName,
140
+ type: 'histogram',
141
+ value,
142
+ labels: histogram.labels,
143
+ timestamp: new Date(),
144
+ });
145
+ }
146
+
147
+ /**
148
+ * Track event
149
+ */
150
+ trackEvent(event: Omit<AnalyticsEvent, 'timestamp'>): void {
151
+ if (!this.config.enabled) return;
152
+
153
+ const fullEvent: AnalyticsEvent = {
154
+ ...event,
155
+ timestamp: new Date(),
156
+ };
157
+
158
+ this.events.push(fullEvent);
159
+
160
+ // Auto-increment counter for event
161
+ this.incrementCounter(`events_${event.name}`, 1, {
162
+ event: event.name,
163
+ });
164
+
165
+ logger.debug({ event: event.name }, 'Event tracked');
166
+ }
167
+
168
+ /**
169
+ * Query metrics
170
+ */
171
+ async queryMetrics(query: MetricQuery): Promise<MetricResult> {
172
+ let filtered = this.metrics.filter((m) => m.name === query.name);
173
+
174
+ // Filter by time range
175
+ if (query.startTime) {
176
+ filtered = filtered.filter((m) => m.timestamp >= query.startTime!);
177
+ }
178
+ if (query.endTime) {
179
+ filtered = filtered.filter((m) => m.timestamp <= query.endTime!);
180
+ }
181
+
182
+ // Filter by labels
183
+ if (query.labels) {
184
+ filtered = filtered.filter((m) => {
185
+ if (!m.labels) return false;
186
+ return Object.entries(query.labels!).every(([key, value]) => m.labels![key] === value);
187
+ });
188
+ }
189
+
190
+ // Group by labels
191
+ const grouped = new Map<string, Metric[]>();
192
+ if (query.groupBy && query.groupBy.length > 0) {
193
+ filtered.forEach((m) => {
194
+ const groupKey = query.groupBy!.map((label) => m.labels?.[label] || 'unknown').join(':');
195
+ if (!grouped.has(groupKey)) {
196
+ grouped.set(groupKey, []);
197
+ }
198
+ grouped.get(groupKey)!.push(m);
199
+ });
200
+ } else {
201
+ grouped.set('all', filtered);
202
+ }
203
+
204
+ // Aggregate
205
+ const data = Array.from(grouped.entries()).map(([_key, metrics]) => {
206
+ const values = metrics.map((m) => m.value);
207
+ let aggregated: number;
208
+
209
+ switch (query.aggregation) {
210
+ case 'sum':
211
+ aggregated = values.reduce((a, b) => a + b, 0);
212
+ break;
213
+ case 'avg':
214
+ aggregated = values.reduce((a, b) => a + b, 0) / values.length;
215
+ break;
216
+ case 'min':
217
+ aggregated = Math.min(...values);
218
+ break;
219
+ case 'max':
220
+ aggregated = Math.max(...values);
221
+ break;
222
+ case 'count':
223
+ aggregated = values.length;
224
+ break;
225
+ default:
226
+ aggregated = values[values.length - 1] || 0;
227
+ }
228
+
229
+ const lastMetric = metrics[metrics.length - 1];
230
+ return {
231
+ timestamp: lastMetric?.timestamp ?? new Date(),
232
+ value: aggregated,
233
+ labels: lastMetric?.labels ?? {},
234
+ };
235
+ });
236
+
237
+ const lastDataPoint = data[data.length - 1];
238
+ return {
239
+ name: query.name,
240
+ data,
241
+ aggregated: lastDataPoint?.value ?? 0,
242
+ };
243
+ }
244
+
245
+ /**
246
+ * Get Prometheus metrics
247
+ */
248
+ getPrometheusMetrics(): string {
249
+ const lines: string[] = [];
250
+
251
+ // Counters
252
+ this.counters.forEach((counter) => {
253
+ const labelsStr = this.formatLabels(counter.labels);
254
+ lines.push(`${counter.name}${labelsStr} ${counter.value}`);
255
+ });
256
+
257
+ // Gauges
258
+ this.gauges.forEach((gauge) => {
259
+ const labelsStr = this.formatLabels(gauge.labels);
260
+ lines.push(`${gauge.name}${labelsStr} ${gauge.value}`);
261
+ });
262
+
263
+ // Histograms
264
+ this.histograms.forEach((histogram) => {
265
+ const labelsStr = this.formatLabels(histogram.labels);
266
+
267
+ histogram.buckets.forEach((count, le) => {
268
+ lines.push(
269
+ `${histogram.name}_bucket${this.formatLabels({ ...histogram.labels, le: String(le) })} ${count}`
270
+ );
271
+ });
272
+
273
+ lines.push(`${histogram.name}_sum${labelsStr} ${histogram.sum}`);
274
+ lines.push(`${histogram.name}_count${labelsStr} ${histogram.count}`);
275
+ });
276
+
277
+ return lines.join('\n');
278
+ }
279
+
280
+ /**
281
+ * Get all counters
282
+ */
283
+ getCounters(): Counter[] {
284
+ return Array.from(this.counters.values());
285
+ }
286
+
287
+ /**
288
+ * Get all gauges
289
+ */
290
+ getGauges(): Gauge[] {
291
+ return Array.from(this.gauges.values());
292
+ }
293
+
294
+ /**
295
+ * Get recent events
296
+ */
297
+ getEvents(limit = 100): AnalyticsEvent[] {
298
+ return this.events.slice(-limit);
299
+ }
300
+
301
+ /**
302
+ * Clear all metrics
303
+ */
304
+ clear(): void {
305
+ this.counters.clear();
306
+ this.gauges.clear();
307
+ this.histograms.clear();
308
+ this.events = [];
309
+ this.metrics = [];
310
+ logger.info('Metrics cleared');
311
+ }
312
+
313
+ /**
314
+ * Flush metrics
315
+ */
316
+ private flush(): void {
317
+ // Keep only last 10000 metrics
318
+ if (this.metrics.length > 10000) {
319
+ this.metrics = this.metrics.slice(-10000);
320
+ }
321
+
322
+ // Keep only last 1000 events
323
+ if (this.events.length > 1000) {
324
+ this.events = this.events.slice(-1000);
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Record metric
330
+ */
331
+ private recordMetric(metric: Metric): void {
332
+ this.metrics.push(metric);
333
+ }
334
+
335
+ /**
336
+ * Get full metric name with prefix
337
+ */
338
+ private getMetricName(name: string): string {
339
+ return this.config.prefix ? `${this.config.prefix}_${name}` : name;
340
+ }
341
+
342
+ /**
343
+ * Get metric key for storage
344
+ */
345
+ private getMetricKey(name: string, labels: Record<string, string>): string {
346
+ const labelsStr = Object.entries(labels)
347
+ .sort(([a], [b]) => a.localeCompare(b))
348
+ .map(([k, v]) => `${k}:${v}`)
349
+ .join(',');
350
+ return `${name}{${labelsStr}}`;
351
+ }
352
+
353
+ /**
354
+ * Format labels for Prometheus
355
+ */
356
+ private formatLabels(labels: Record<string, string>): string {
357
+ const entries = Object.entries(labels);
358
+ if (entries.length === 0) return '';
359
+
360
+ const formatted = entries.map(([key, value]) => `${key}="${value}"`).join(',');
361
+
362
+ return `{${formatted}}`;
363
+ }
364
+ }
@@ -0,0 +1,18 @@
1
+ export { AnalyticsService } from './analytics.service.js';
2
+ export { createAnalyticsRoutes } from './analytics.routes.js';
3
+ export type {
4
+ AnalyticsConfig,
5
+ Metric,
6
+ MetricType,
7
+ Counter,
8
+ Gauge,
9
+ Histogram,
10
+ Summary,
11
+ AnalyticsEvent,
12
+ MetricQuery,
13
+ MetricResult,
14
+ MetricAggregation,
15
+ Dashboard,
16
+ Widget,
17
+ Alert,
18
+ } from './types.js';
@@ -0,0 +1,180 @@
1
+ export type MetricType = 'counter' | 'gauge' | 'histogram' | 'summary';
2
+ export type MetricAggregation = 'sum' | 'avg' | 'min' | 'max' | 'count';
3
+
4
+ export interface Metric {
5
+ /** Metric name */
6
+ name: string;
7
+ /** Metric type */
8
+ type: MetricType;
9
+ /** Metric value */
10
+ value: number;
11
+ /** Labels/tags */
12
+ labels?: Record<string, string>;
13
+ /** Timestamp */
14
+ timestamp: Date;
15
+ /** Unit */
16
+ unit?: string;
17
+ /** Help text */
18
+ help?: string;
19
+ }
20
+
21
+ export interface Counter {
22
+ /** Counter name */
23
+ name: string;
24
+ /** Current value */
25
+ value: number;
26
+ /** Labels */
27
+ labels: Record<string, string>;
28
+ /** Help text */
29
+ help?: string;
30
+ }
31
+
32
+ export interface Gauge {
33
+ /** Gauge name */
34
+ name: string;
35
+ /** Current value */
36
+ value: number;
37
+ /** Labels */
38
+ labels: Record<string, string>;
39
+ /** Help text */
40
+ help?: string;
41
+ }
42
+
43
+ export interface Histogram {
44
+ /** Histogram name */
45
+ name: string;
46
+ /** Buckets */
47
+ buckets: Map<number, number>;
48
+ /** Sum of all values */
49
+ sum: number;
50
+ /** Count of all values */
51
+ count: number;
52
+ /** Labels */
53
+ labels: Record<string, string>;
54
+ /** Help text */
55
+ help?: string;
56
+ }
57
+
58
+ export interface Summary {
59
+ /** Summary name */
60
+ name: string;
61
+ /** Quantiles (0.5, 0.9, 0.99, etc.) */
62
+ quantiles: Map<number, number>;
63
+ /** Sum of all values */
64
+ sum: number;
65
+ /** Count of all values */
66
+ count: number;
67
+ /** Labels */
68
+ labels: Record<string, string>;
69
+ /** Help text */
70
+ help?: string;
71
+ }
72
+
73
+ export interface AnalyticsEvent {
74
+ /** Event name */
75
+ name: string;
76
+ /** User ID */
77
+ userId?: string;
78
+ /** Session ID */
79
+ sessionId?: string;
80
+ /** Event properties */
81
+ properties?: Record<string, unknown>;
82
+ /** Timestamp */
83
+ timestamp: Date;
84
+ /** Context */
85
+ context?: {
86
+ ip?: string;
87
+ userAgent?: string;
88
+ referrer?: string;
89
+ page?: string;
90
+ };
91
+ }
92
+
93
+ export interface AnalyticsConfig {
94
+ /** Enable metrics collection */
95
+ enabled?: boolean;
96
+ /** Metrics prefix */
97
+ prefix?: string;
98
+ /** Default labels */
99
+ defaultLabels?: Record<string, string>;
100
+ /** Prometheus endpoint */
101
+ prometheusEnabled?: boolean;
102
+ /** Flush interval (ms) */
103
+ flushInterval?: number;
104
+ }
105
+
106
+ export interface MetricQuery {
107
+ /** Metric name */
108
+ name: string;
109
+ /** Start time */
110
+ startTime?: Date;
111
+ /** End time */
112
+ endTime?: Date;
113
+ /** Labels filter */
114
+ labels?: Record<string, string>;
115
+ /** Aggregation */
116
+ aggregation?: MetricAggregation;
117
+ /** Group by labels */
118
+ groupBy?: string[];
119
+ }
120
+
121
+ export interface MetricResult {
122
+ /** Metric name */
123
+ name: string;
124
+ /** Data points */
125
+ data: Array<{
126
+ timestamp: Date;
127
+ value: number;
128
+ labels?: Record<string, string>;
129
+ }>;
130
+ /** Aggregated value */
131
+ aggregated?: number;
132
+ }
133
+
134
+ export interface Dashboard {
135
+ /** Dashboard ID */
136
+ id: string;
137
+ /** Dashboard name */
138
+ name: string;
139
+ /** Widgets */
140
+ widgets: Widget[];
141
+ /** Created at */
142
+ createdAt: Date;
143
+ /** Updated at */
144
+ updatedAt: Date;
145
+ }
146
+
147
+ export interface Widget {
148
+ /** Widget ID */
149
+ id: string;
150
+ /** Widget type */
151
+ type: 'chart' | 'counter' | 'gauge' | 'table';
152
+ /** Widget title */
153
+ title: string;
154
+ /** Metric query */
155
+ query: MetricQuery;
156
+ /** Chart type (for chart widgets) */
157
+ chartType?: 'line' | 'bar' | 'pie' | 'area';
158
+ /** Position */
159
+ position: { x: number; y: number; w: number; h: number };
160
+ }
161
+
162
+ export interface Alert {
163
+ /** Alert ID */
164
+ id: string;
165
+ /** Alert name */
166
+ name: string;
167
+ /** Metric to monitor */
168
+ metric: string;
169
+ /** Condition */
170
+ condition: {
171
+ operator: 'gt' | 'lt' | 'eq' | 'gte' | 'lte';
172
+ threshold: number;
173
+ };
174
+ /** Alert status */
175
+ status: 'active' | 'inactive' | 'triggered';
176
+ /** Notification channels */
177
+ channels: string[];
178
+ /** Created at */
179
+ createdAt: Date;
180
+ }
@@ -0,0 +1,15 @@
1
+ export { VersioningService } from './versioning.service.js';
2
+ export { createVersioningMiddleware, versionedRoute } from './versioning.middleware.js';
3
+ export { createVersioningRoutes } from './versioning.routes.js';
4
+ export type { VersionedRequest } from './versioning.middleware.js';
5
+ export type {
6
+ VersioningConfig,
7
+ ApiVersion,
8
+ VersionStrategy,
9
+ DeprecationStatus,
10
+ VersionedRoute,
11
+ RouteHandler,
12
+ VersionDetectionResult,
13
+ VersionMigration,
14
+ VersionMiddlewareOptions,
15
+ } from './types.js';
@@ -0,0 +1,86 @@
1
+ export type VersionStrategy = 'url' | 'header' | 'query' | 'accept-header';
2
+ export type DeprecationStatus = 'active' | 'deprecated' | 'sunset';
3
+
4
+ export interface ApiVersion {
5
+ /** Version identifier (e.g., 'v1', 'v2') */
6
+ version: string;
7
+ /** Version status */
8
+ status: DeprecationStatus;
9
+ /** Release date */
10
+ releasedAt: Date;
11
+ /** Deprecation date */
12
+ deprecatedAt?: Date;
13
+ /** Sunset date (when it will be removed) */
14
+ sunsetAt?: Date;
15
+ /** Supported until date */
16
+ supportedUntil?: Date;
17
+ /** Is this the default version */
18
+ isDefault: boolean;
19
+ /** Changelog */
20
+ changelog?: string;
21
+ }
22
+
23
+ export interface VersioningConfig {
24
+ /** Versioning strategy */
25
+ strategy: VersionStrategy;
26
+ /** Default version */
27
+ defaultVersion: string;
28
+ /** Available versions */
29
+ versions: ApiVersion[];
30
+ /** Version header name */
31
+ headerName?: string;
32
+ /** Query parameter name */
33
+ queryParam?: string;
34
+ /** Strict mode (reject unknown versions) */
35
+ strict?: boolean;
36
+ /** Show deprecation warnings */
37
+ deprecationWarnings?: boolean;
38
+ }
39
+
40
+ export interface VersionedRoute {
41
+ /** Route path */
42
+ path: string;
43
+ /** HTTP method */
44
+ method: string;
45
+ /** Versions where this route exists */
46
+ versions: string[];
47
+ /** Version-specific handlers */
48
+ handlers: Map<string, RouteHandler>;
49
+ }
50
+
51
+ export type RouteHandler = (req: unknown, res: unknown) => Promise<void> | void;
52
+
53
+ export interface VersionDetectionResult {
54
+ /** Detected version */
55
+ version: string;
56
+ /** Detection source */
57
+ source: VersionStrategy;
58
+ /** Is valid version */
59
+ isValid: boolean;
60
+ /** Warning message */
61
+ warning?: string;
62
+ }
63
+
64
+ export interface VersionMigration {
65
+ /** From version */
66
+ from: string;
67
+ /** To version */
68
+ to: string;
69
+ /** Request transformer */
70
+ transformRequest?: (data: unknown) => unknown;
71
+ /** Response transformer */
72
+ transformResponse?: (data: unknown) => unknown;
73
+ /** Breaking changes description */
74
+ breakingChanges?: string[];
75
+ }
76
+
77
+ export interface VersionMiddlewareOptions {
78
+ /** Required version */
79
+ requiredVersion?: string;
80
+ /** Minimum version */
81
+ minVersion?: string;
82
+ /** Maximum version */
83
+ maxVersion?: string;
84
+ /** Allow deprecated versions */
85
+ allowDeprecated?: boolean;
86
+ }