@backendkit-labs/auto-learning 0.1.0

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.
@@ -0,0 +1,837 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var __decorateClass = (decorators, target, key, kind) => {
20
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
21
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
22
+ if (decorator = decorators[i])
23
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
24
+ if (kind && result) __defProp(target, key, result);
25
+ return result;
26
+ };
27
+ var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
28
+
29
+ // src/nestjs/index.ts
30
+ var nestjs_exports = {};
31
+ __export(nestjs_exports, {
32
+ AUTO_LEARNING_INSTANCE: () => AUTO_LEARNING_INSTANCE,
33
+ AUTO_LEARNING_OPTIONS: () => AUTO_LEARNING_OPTIONS,
34
+ AUTO_LEARN_METADATA: () => AUTO_LEARN_METADATA,
35
+ AutoLearn: () => AutoLearn,
36
+ AutoLearningModule: () => AutoLearningModule
37
+ });
38
+ module.exports = __toCommonJS(nestjs_exports);
39
+
40
+ // src/nestjs/auto-learning.module.ts
41
+ var import_common2 = require("@nestjs/common");
42
+ var import_core = require("@nestjs/core");
43
+
44
+ // src/core/pattern-registry/pattern-registry.ts
45
+ var import_result = require("@backendkit-labs/result");
46
+
47
+ // src/core/errors.ts
48
+ var storageError = (message, cause) => ({ tag: "STORAGE_ERROR", message, cause });
49
+ var anomalyDetectionFailed = (message) => ({ tag: "ANOMALY_DETECTION_FAILED", message });
50
+
51
+ // src/core/pattern-registry/pattern-registry.ts
52
+ var PatternRegistry = class {
53
+ constructor(storage, observability) {
54
+ this.storage = storage;
55
+ this.observability = observability;
56
+ }
57
+ storage;
58
+ observability;
59
+ record(pattern) {
60
+ const result = this.storage.savePattern(pattern);
61
+ if (!result.ok) {
62
+ this.observability.error("Failed to record pattern", {
63
+ error: result.error,
64
+ pattern: { method: pattern.method, path: pattern.path }
65
+ });
66
+ return (0, import_result.fail)(storageError("Failed to save pattern", result.error));
67
+ }
68
+ this.observability.incrementMetric("patterns.recorded", 1, {
69
+ method: pattern.method,
70
+ path: pattern.path
71
+ });
72
+ this.observability.histogramMetric("patterns.duration_ms", pattern.durationMs, {
73
+ method: pattern.method,
74
+ path: pattern.path
75
+ });
76
+ return (0, import_result.ok)(void 0);
77
+ }
78
+ getAggregates(windowMinutes) {
79
+ const result = this.storage.getAggregates(windowMinutes);
80
+ if (!result.ok) {
81
+ this.observability.error("Failed to get aggregates", { error: result.error });
82
+ return (0, import_result.fail)(storageError("Failed to get aggregates", result.error));
83
+ }
84
+ return (0, import_result.ok)(result.value);
85
+ }
86
+ getHistory(endpoint, method, limit) {
87
+ const now = /* @__PURE__ */ new Date();
88
+ const past = new Date(now.getTime() - 24 * 60 * 60 * 1e3);
89
+ const patterns = this.storage.getPatterns(past, now);
90
+ if (!patterns.ok) {
91
+ return (0, import_result.fail)(storageError("Failed to get history", patterns.error));
92
+ }
93
+ const filtered = patterns.value.filter((p) => p.path === endpoint && p.method === method).slice(-limit);
94
+ return (0, import_result.ok)(filtered);
95
+ }
96
+ getStats() {
97
+ const now = /* @__PURE__ */ new Date();
98
+ const past = /* @__PURE__ */ new Date(0);
99
+ const patterns = this.storage.getPatterns(past, now);
100
+ if (!patterns.ok) {
101
+ return (0, import_result.fail)(storageError("Failed to get stats", patterns.error));
102
+ }
103
+ const all = patterns.value;
104
+ if (all.length === 0) {
105
+ return (0, import_result.ok)({
106
+ totalPatterns: 0,
107
+ uniqueEndpoints: 0,
108
+ oldestPattern: now,
109
+ newestPattern: now
110
+ });
111
+ }
112
+ const uniqueEndpoints = new Set(all.map((p) => `${p.method}:${p.path}`));
113
+ const timestamps = all.map((p) => p.timestamp.getTime());
114
+ return (0, import_result.ok)({
115
+ totalPatterns: all.length,
116
+ uniqueEndpoints: uniqueEndpoints.size,
117
+ oldestPattern: new Date(Math.min(...timestamps)),
118
+ newestPattern: new Date(Math.max(...timestamps))
119
+ });
120
+ }
121
+ };
122
+
123
+ // src/core/anomaly-detector/types.ts
124
+ var DEFAULT_ANOMALY_CONFIG = {
125
+ latencyStdDevThreshold: 2.5,
126
+ errorRateThreshold: 0.05,
127
+ frequencyDeviationThreshold: 3,
128
+ enableUnknownEndpointDetection: true
129
+ };
130
+
131
+ // src/core/anomaly-detector/anomaly-detector.ts
132
+ var import_result2 = require("@backendkit-labs/result");
133
+ var import_uuid = require("uuid");
134
+ var AnomalyDetector = class {
135
+ config;
136
+ constructor(config) {
137
+ this.config = { ...DEFAULT_ANOMALY_CONFIG, ...config };
138
+ }
139
+ analyze(current, baseline) {
140
+ try {
141
+ const reports = [];
142
+ if (baseline.count > 0) {
143
+ const latencyDeviation = Math.abs(current.durationMs - baseline.avgDurationMs) / Math.max(this.stdDev(baseline), 1);
144
+ if (latencyDeviation > this.config.latencyStdDevThreshold) {
145
+ reports.push({
146
+ id: (0, import_uuid.v4)(),
147
+ endpoint: current.path,
148
+ method: current.method,
149
+ severity: this.calculateSeverity(latencyDeviation),
150
+ metric: "latency",
151
+ expectedValue: baseline.avgDurationMs,
152
+ actualValue: current.durationMs,
153
+ deviation: latencyDeviation,
154
+ detectedAt: /* @__PURE__ */ new Date()
155
+ });
156
+ }
157
+ }
158
+ if (current.statusCode >= 500 && baseline.errorCount >= 3) {
159
+ const currentErrorRate = 1;
160
+ if (currentErrorRate > baseline.errorRate * 2 && currentErrorRate > this.config.errorRateThreshold) {
161
+ reports.push({
162
+ id: (0, import_uuid.v4)(),
163
+ endpoint: current.path,
164
+ method: current.method,
165
+ severity: "high",
166
+ metric: "error_rate",
167
+ expectedValue: baseline.errorRate,
168
+ actualValue: currentErrorRate,
169
+ deviation: currentErrorRate / Math.max(baseline.errorRate, 1e-3),
170
+ detectedAt: /* @__PURE__ */ new Date()
171
+ });
172
+ }
173
+ }
174
+ return (0, import_result2.ok)(reports.length > 0 ? reports[0] : null);
175
+ } catch (e) {
176
+ return (0, import_result2.fail)(
177
+ anomalyDetectionFailed(
178
+ e instanceof Error ? e.message : "Unknown anomaly detection error"
179
+ )
180
+ );
181
+ }
182
+ }
183
+ batchAnalyze(windowPatterns, baselines) {
184
+ try {
185
+ const baselineMap = /* @__PURE__ */ new Map();
186
+ for (const b of baselines) {
187
+ baselineMap.set(`${b.method}:${b.path}`, b);
188
+ }
189
+ const reports = [];
190
+ for (const pattern of windowPatterns) {
191
+ const key = `${pattern.method}:${pattern.path}`;
192
+ const baseline = baselineMap.get(key);
193
+ if (!baseline) {
194
+ if (this.config.enableUnknownEndpointDetection) {
195
+ reports.push({
196
+ id: (0, import_uuid.v4)(),
197
+ endpoint: pattern.path,
198
+ method: pattern.method,
199
+ severity: "low",
200
+ metric: "unknown_endpoint",
201
+ expectedValue: 0,
202
+ actualValue: 1,
203
+ deviation: 1,
204
+ detectedAt: /* @__PURE__ */ new Date()
205
+ });
206
+ }
207
+ continue;
208
+ }
209
+ const result = this.analyze(pattern, baseline);
210
+ if (result.ok && result.value) {
211
+ reports.push(result.value);
212
+ }
213
+ }
214
+ return (0, import_result2.ok)(reports);
215
+ } catch (e) {
216
+ return (0, import_result2.fail)(
217
+ anomalyDetectionFailed(
218
+ e instanceof Error ? e.message : "Unknown batch analysis error"
219
+ )
220
+ );
221
+ }
222
+ }
223
+ calculateSeverity(deviation) {
224
+ if (deviation > 5) return "critical";
225
+ if (deviation > 4) return "high";
226
+ if (deviation > 3) return "medium";
227
+ return "low";
228
+ }
229
+ stdDev(baseline) {
230
+ return (baseline.p95Ms - baseline.p50Ms) / 2;
231
+ }
232
+ };
233
+
234
+ // src/core/config-tuner/types.ts
235
+ var DEFAULT_TUNER_CONFIG = {
236
+ minTimeoutMs: 1e3,
237
+ maxTimeoutMs: 3e4,
238
+ smoothingFactor: 0.3,
239
+ adjustmentStepMs: 500
240
+ };
241
+
242
+ // src/core/config-tuner/config-tuner.ts
243
+ var import_result3 = require("@backendkit-labs/result");
244
+ var DEFAULT_CONFIG = {
245
+ timeoutMs: 1e4,
246
+ maxRetries: 3,
247
+ circuitBreakerThreshold: 0.5,
248
+ circuitBreakerHalfOpenAfterMs: 3e4,
249
+ bulkheadMaxConcurrent: 10
250
+ };
251
+ var ConfigTuner = class {
252
+ constructor(storage, observability, tunerConfig) {
253
+ this.storage = storage;
254
+ this.observability = observability;
255
+ this.config = { ...DEFAULT_TUNER_CONFIG, ...tunerConfig };
256
+ const loaded = this.storage.loadConfig();
257
+ this.currentConfig = loaded.ok && loaded.value ? loaded.value : { ...DEFAULT_CONFIG };
258
+ }
259
+ storage;
260
+ observability;
261
+ currentConfig;
262
+ config;
263
+ listeners = [];
264
+ lastChangeAt = 0;
265
+ getCurrentConfig() {
266
+ return { ...this.currentConfig };
267
+ }
268
+ tune(aggregates, anomalies) {
269
+ if (aggregates.length === 0) {
270
+ return (0, import_result3.ok)(this.getCurrentConfig());
271
+ }
272
+ const newConfig = { ...this.currentConfig };
273
+ const changes = {};
274
+ const maxP95 = Math.max(...aggregates.map((a) => a.p95Ms));
275
+ const targetTimeout = Math.min(
276
+ Math.max(maxP95 * 2, this.config.minTimeoutMs),
277
+ this.config.maxTimeoutMs
278
+ );
279
+ if (Math.abs(targetTimeout - newConfig.timeoutMs) > this.config.adjustmentStepMs) {
280
+ newConfig.timeoutMs = this.smoothValue(
281
+ newConfig.timeoutMs,
282
+ targetTimeout
283
+ );
284
+ changes.timeoutMs = newConfig.timeoutMs;
285
+ }
286
+ const avgErrorRate = aggregates.reduce((sum, a) => sum + a.errorRate, 0) / aggregates.length;
287
+ if (avgErrorRate > 0.1) {
288
+ newConfig.maxRetries = Math.min(newConfig.maxRetries + 1, 5);
289
+ changes.maxRetries = newConfig.maxRetries;
290
+ } else if (avgErrorRate < 0.01 && newConfig.maxRetries > 1) {
291
+ newConfig.maxRetries = Math.max(newConfig.maxRetries - 1, 0);
292
+ changes.maxRetries = newConfig.maxRetries;
293
+ }
294
+ const criticalAnomalies = anomalies.filter(
295
+ (a) => a.severity === "critical" || a.severity === "high"
296
+ ).length;
297
+ if (criticalAnomalies > 0) {
298
+ newConfig.circuitBreakerThreshold = Math.max(
299
+ this.currentConfig.circuitBreakerThreshold - 0.1 * criticalAnomalies,
300
+ 0.1
301
+ );
302
+ changes.circuitBreakerThreshold = newConfig.circuitBreakerThreshold;
303
+ } else if (anomalies.length === 0) {
304
+ newConfig.circuitBreakerThreshold = Math.min(
305
+ this.currentConfig.circuitBreakerThreshold + 0.05,
306
+ 0.8
307
+ );
308
+ changes.circuitBreakerThreshold = newConfig.circuitBreakerThreshold;
309
+ }
310
+ if (Object.keys(changes).length > 0) {
311
+ const now = Date.now();
312
+ if (now - this.lastChangeAt > 6e4) {
313
+ this.currentConfig = newConfig;
314
+ this.lastChangeAt = now;
315
+ const saveResult = this.storage.saveConfig(newConfig);
316
+ if (!saveResult.ok) {
317
+ return (0, import_result3.fail)(storageError("Failed to save config", saveResult.error));
318
+ }
319
+ this.observability.info("Config tuned", { changes });
320
+ this.observability.incrementMetric("config.changes", 1);
321
+ for (const listener of this.listeners) {
322
+ listener(this.getCurrentConfig());
323
+ }
324
+ }
325
+ }
326
+ return (0, import_result3.ok)(this.getCurrentConfig());
327
+ }
328
+ reset() {
329
+ this.currentConfig = { ...DEFAULT_CONFIG };
330
+ const saveResult = this.storage.saveConfig(this.currentConfig);
331
+ if (!saveResult.ok) {
332
+ return (0, import_result3.fail)(storageError("Failed to reset config", saveResult.error));
333
+ }
334
+ this.observability.info("Config reset to defaults");
335
+ for (const listener of this.listeners) {
336
+ listener(this.getCurrentConfig());
337
+ }
338
+ return (0, import_result3.ok)(this.getCurrentConfig());
339
+ }
340
+ onConfigChange(callback) {
341
+ this.listeners.push(callback);
342
+ }
343
+ smoothValue(current, target) {
344
+ return current + (target - current) * this.config.smoothingFactor;
345
+ }
346
+ };
347
+
348
+ // src/core/feedback-loop/types.ts
349
+ var DEFAULT_LOOP_CONFIG = {
350
+ defaultIntervalMs: 6e4,
351
+ windowSizeMinutes: 5,
352
+ minSamplesBeforeTuning: 10,
353
+ cooldownBetweenChangesMs: 12e4
354
+ };
355
+
356
+ // src/core/feedback-loop/feedback-loop.ts
357
+ var import_result4 = require("@backendkit-labs/result");
358
+ var import_uuid2 = require("uuid");
359
+ var FeedbackLoop = class {
360
+ constructor(patternRegistry, anomalyDetector, configTuner, storage, observability, loopConfig) {
361
+ this.patternRegistry = patternRegistry;
362
+ this.anomalyDetector = anomalyDetector;
363
+ this.configTuner = configTuner;
364
+ this.storage = storage;
365
+ this.observability = observability;
366
+ this.config = { ...DEFAULT_LOOP_CONFIG, ...loopConfig };
367
+ }
368
+ patternRegistry;
369
+ anomalyDetector;
370
+ configTuner;
371
+ storage;
372
+ observability;
373
+ timerId = null;
374
+ config;
375
+ cycleListeners = [];
376
+ start(intervalMs) {
377
+ if (this.timerId !== null) {
378
+ this.observability.warn("Feedback loop already running, ignoring start");
379
+ return;
380
+ }
381
+ const interval = intervalMs ?? this.config.defaultIntervalMs;
382
+ this.observability.info("Feedback loop started", { intervalMs: interval });
383
+ this.timerId = setInterval(() => {
384
+ this.runOnce().then((result) => {
385
+ if (!result.ok) {
386
+ this.observability.error("Feedback loop cycle failed", {
387
+ error: result.error
388
+ });
389
+ }
390
+ });
391
+ }, interval);
392
+ }
393
+ stop() {
394
+ if (this.timerId === null) {
395
+ this.observability.warn("Feedback loop not running, ignoring stop");
396
+ return;
397
+ }
398
+ clearInterval(this.timerId);
399
+ this.timerId = null;
400
+ this.observability.info("Feedback loop stopped");
401
+ }
402
+ isRunning() {
403
+ return this.timerId !== null;
404
+ }
405
+ async runOnce() {
406
+ const cycleId = (0, import_uuid2.v4)();
407
+ const startTime = Date.now();
408
+ this.observability.debug("Feedback cycle started", { cycleId });
409
+ const patternsResult = this.storage.getPatterns(
410
+ new Date(Date.now() - this.config.windowSizeMinutes * 6e4),
411
+ /* @__PURE__ */ new Date()
412
+ );
413
+ if (!patternsResult.ok) {
414
+ return (0, import_result4.fail)(storageError("Failed to collect patterns", patternsResult.error));
415
+ }
416
+ const patterns = patternsResult.value;
417
+ if (patterns.length < this.config.minSamplesBeforeTuning) {
418
+ this.observability.debug("Skipping cycle: insufficient samples", {
419
+ actual: patterns.length,
420
+ required: this.config.minSamplesBeforeTuning
421
+ });
422
+ const skippedEvent = {
423
+ cycleId,
424
+ timestamp: /* @__PURE__ */ new Date(),
425
+ patternsProcessed: patterns.length,
426
+ anomaliesFound: 0,
427
+ configChanges: {},
428
+ durationMs: Date.now() - startTime
429
+ };
430
+ return (0, import_result4.ok)(skippedEvent);
431
+ }
432
+ const aggregatesResult = this.patternRegistry.getAggregates(
433
+ this.config.windowSizeMinutes
434
+ );
435
+ if (!aggregatesResult.ok) {
436
+ return (0, import_result4.fail)(aggregatesResult.error);
437
+ }
438
+ const aggregates = aggregatesResult.value;
439
+ const anomaliesResult = this.anomalyDetector.batchAnalyze(patterns, aggregates);
440
+ if (!anomaliesResult.ok) {
441
+ return (0, import_result4.fail)(anomaliesResult.error);
442
+ }
443
+ const anomalies = anomaliesResult.value;
444
+ for (const anomaly of anomalies) {
445
+ this.storage.saveAnomaly(anomaly);
446
+ }
447
+ if (anomalies.length > 0) {
448
+ this.observability.warn("Anomalies detected", {
449
+ count: anomalies.length,
450
+ severities: anomalies.map((a) => a.severity)
451
+ });
452
+ this.observability.incrementMetric("anomalies.detected", anomalies.length);
453
+ }
454
+ const tuneResult = this.configTuner.tune(aggregates, anomalies);
455
+ if (!tuneResult.ok) {
456
+ return (0, import_result4.fail)(tuneResult.error);
457
+ }
458
+ const newConfig = tuneResult.value;
459
+ const previousConfig = this.configTuner.getCurrentConfig();
460
+ const configChanges = {};
461
+ const configKeys = Object.keys(newConfig);
462
+ for (const key of configKeys) {
463
+ if (newConfig[key] !== previousConfig[key]) {
464
+ configChanges[key] = newConfig[key];
465
+ }
466
+ }
467
+ const cycleEvent = {
468
+ cycleId,
469
+ timestamp: /* @__PURE__ */ new Date(),
470
+ patternsProcessed: patterns.length,
471
+ anomaliesFound: anomalies.length,
472
+ configChanges,
473
+ durationMs: Date.now() - startTime
474
+ };
475
+ const saveResult = this.storage.saveCycleEvent(cycleEvent);
476
+ if (!saveResult.ok) {
477
+ this.observability.error("Failed to save cycle event", { error: saveResult.error });
478
+ return (0, import_result4.fail)(saveResult.error);
479
+ }
480
+ for (const listener of this.cycleListeners) {
481
+ listener(cycleEvent);
482
+ }
483
+ this.observability.info("Feedback cycle completed", {
484
+ cycleId,
485
+ patternsProcessed: cycleEvent.patternsProcessed,
486
+ anomaliesFound: cycleEvent.anomaliesFound,
487
+ durationMs: cycleEvent.durationMs
488
+ });
489
+ this.observability.histogramMetric("cycle.duration_ms", cycleEvent.durationMs);
490
+ this.observability.gaugeMetric("cycle.patterns_count", cycleEvent.patternsProcessed);
491
+ return (0, import_result4.ok)(cycleEvent);
492
+ }
493
+ onCycle(callback) {
494
+ this.cycleListeners.push(callback);
495
+ }
496
+ };
497
+
498
+ // src/core/persistence/in-memory-storage.ts
499
+ var import_result5 = require("@backendkit-labs/result");
500
+ var DEFAULT_CONFIG2 = {
501
+ timeoutMs: 1e4,
502
+ maxRetries: 3,
503
+ circuitBreakerThreshold: 0.5,
504
+ circuitBreakerHalfOpenAfterMs: 3e4,
505
+ bulkheadMaxConcurrent: 10
506
+ };
507
+ function percentile(sorted, p) {
508
+ if (sorted.length === 0) return 0;
509
+ const index = Math.ceil(p / 100 * sorted.length) - 1;
510
+ return sorted[Math.max(0, index)];
511
+ }
512
+ var InMemoryStorage = class {
513
+ patterns = [];
514
+ anomalies = [];
515
+ config = { ...DEFAULT_CONFIG2 };
516
+ cycles = [];
517
+ savePattern(pattern) {
518
+ try {
519
+ this.patterns.push(pattern);
520
+ return (0, import_result5.ok)(void 0);
521
+ } catch (e) {
522
+ return (0, import_result5.fail)(storageError("Failed to save pattern", e));
523
+ }
524
+ }
525
+ getPatterns(windowStart, windowEnd) {
526
+ try {
527
+ return (0, import_result5.ok)(
528
+ this.patterns.filter(
529
+ (p) => p.timestamp >= windowStart && p.timestamp <= windowEnd
530
+ )
531
+ );
532
+ } catch (e) {
533
+ return (0, import_result5.fail)(storageError("Failed to get patterns", e));
534
+ }
535
+ }
536
+ getAggregates(windowMinutes) {
537
+ try {
538
+ const cutoff = new Date(Date.now() - windowMinutes * 6e4);
539
+ const recent = this.patterns.filter((p) => p.timestamp >= cutoff);
540
+ const groups = /* @__PURE__ */ new Map();
541
+ for (const p of recent) {
542
+ const key = `${p.method}:${p.path}`;
543
+ if (!groups.has(key)) groups.set(key, []);
544
+ groups.get(key).push(p);
545
+ }
546
+ const aggregates = [];
547
+ for (const [key, items] of groups) {
548
+ const [method, path] = key.split(":");
549
+ const durations = items.map((i) => i.durationMs).sort((a, b) => a - b);
550
+ const errors = items.filter((i) => i.statusCode >= 500).length;
551
+ aggregates.push({
552
+ method,
553
+ path,
554
+ windowStart: cutoff,
555
+ windowEnd: /* @__PURE__ */ new Date(),
556
+ count: items.length,
557
+ avgDurationMs: durations.reduce((a, b) => a + b, 0) / durations.length,
558
+ p50Ms: percentile(durations, 50),
559
+ p95Ms: percentile(durations, 95),
560
+ p99Ms: percentile(durations, 99),
561
+ errorCount: errors,
562
+ errorRate: errors / items.length
563
+ });
564
+ }
565
+ return (0, import_result5.ok)(aggregates);
566
+ } catch (e) {
567
+ return (0, import_result5.fail)(storageError("Failed to get aggregates", e));
568
+ }
569
+ }
570
+ saveAnomaly(report) {
571
+ try {
572
+ this.anomalies.push(report);
573
+ return (0, import_result5.ok)(void 0);
574
+ } catch (e) {
575
+ return (0, import_result5.fail)(storageError("Failed to save anomaly", e));
576
+ }
577
+ }
578
+ getRecentAnomalies(limit) {
579
+ try {
580
+ return (0, import_result5.ok)(this.anomalies.slice(-limit).reverse());
581
+ } catch (e) {
582
+ return (0, import_result5.fail)(storageError("Failed to get recent anomalies", e));
583
+ }
584
+ }
585
+ saveConfig(config) {
586
+ try {
587
+ this.config = { ...config };
588
+ return (0, import_result5.ok)(void 0);
589
+ } catch (e) {
590
+ return (0, import_result5.fail)(storageError("Failed to save config", e));
591
+ }
592
+ }
593
+ loadConfig() {
594
+ try {
595
+ return (0, import_result5.ok)(this.config);
596
+ } catch (e) {
597
+ return (0, import_result5.fail)(storageError("Failed to load config", e));
598
+ }
599
+ }
600
+ saveCycleEvent(event) {
601
+ try {
602
+ this.cycles.push(event);
603
+ return (0, import_result5.ok)(void 0);
604
+ } catch (e) {
605
+ return (0, import_result5.fail)(storageError("Failed to save cycle event", e));
606
+ }
607
+ }
608
+ getLastCycleTime() {
609
+ try {
610
+ if (this.cycles.length === 0) return (0, import_result5.ok)(null);
611
+ return (0, import_result5.ok)(this.cycles[this.cycles.length - 1].timestamp);
612
+ } catch (e) {
613
+ return (0, import_result5.fail)(storageError("Failed to get last cycle time", e));
614
+ }
615
+ }
616
+ prune(before) {
617
+ try {
618
+ const beforeLen = this.patterns.length + this.anomalies.length;
619
+ this.patterns = this.patterns.filter((p) => p.timestamp >= before);
620
+ this.anomalies = this.anomalies.filter((a) => a.detectedAt >= before);
621
+ const pruned = beforeLen - (this.patterns.length + this.anomalies.length);
622
+ return (0, import_result5.ok)(pruned);
623
+ } catch (e) {
624
+ return (0, import_result5.fail)(storageError("Failed to prune", e));
625
+ }
626
+ }
627
+ };
628
+
629
+ // src/core/observability/noop-observability-adapter.ts
630
+ var NoopObservabilityAdapter = class {
631
+ info(_msg, _meta) {
632
+ }
633
+ warn(_msg, _meta) {
634
+ }
635
+ error(_msg, _meta) {
636
+ }
637
+ debug(_msg, _meta) {
638
+ }
639
+ incrementMetric(_name, _value, _tags) {
640
+ }
641
+ gaugeMetric(_name, _value, _tags) {
642
+ }
643
+ histogramMetric(_name, _value, _tags) {
644
+ }
645
+ };
646
+
647
+ // src/core/auto-learning-core.ts
648
+ var AutoLearningCore = class _AutoLearningCore {
649
+ constructor(patternRegistry, anomalyDetector, configTuner, feedbackLoop, storage, observability) {
650
+ this.patternRegistry = patternRegistry;
651
+ this.anomalyDetector = anomalyDetector;
652
+ this.configTuner = configTuner;
653
+ this.feedbackLoop = feedbackLoop;
654
+ this.storage = storage;
655
+ this.observability = observability;
656
+ }
657
+ patternRegistry;
658
+ anomalyDetector;
659
+ configTuner;
660
+ feedbackLoop;
661
+ storage;
662
+ observability;
663
+ static create(options) {
664
+ const storage = options?.storage ?? new InMemoryStorage();
665
+ const obs = options?.observability ?? new NoopObservabilityAdapter();
666
+ const registry = new PatternRegistry(storage, obs);
667
+ const detector = new AnomalyDetector(options?.anomalyConfig);
668
+ const tuner = new ConfigTuner(storage, obs, options?.tunerConfig);
669
+ const loop = new FeedbackLoop(registry, detector, tuner, storage, obs, options?.loopConfig);
670
+ return new _AutoLearningCore(registry, detector, tuner, loop, storage, obs);
671
+ }
672
+ recordPattern(pattern) {
673
+ return this.patternRegistry.record(pattern);
674
+ }
675
+ getCurrentConfig() {
676
+ return this.configTuner.getCurrentConfig();
677
+ }
678
+ startFeedbackLoop(intervalMs) {
679
+ this.feedbackLoop.start(intervalMs);
680
+ }
681
+ stopFeedbackLoop() {
682
+ this.feedbackLoop.stop();
683
+ }
684
+ isFeedbackLoopRunning() {
685
+ return this.feedbackLoop.isRunning();
686
+ }
687
+ async runOnce() {
688
+ return this.feedbackLoop.runOnce();
689
+ }
690
+ onConfigChange(callback) {
691
+ this.configTuner.onConfigChange(callback);
692
+ }
693
+ onCycle(callback) {
694
+ this.feedbackLoop.onCycle(callback);
695
+ }
696
+ };
697
+
698
+ // src/nestjs/auto-learning.interceptor.ts
699
+ var import_common = require("@nestjs/common");
700
+ var import_rxjs = require("rxjs");
701
+
702
+ // src/nestjs/auto-learning.constants.ts
703
+ var AUTO_LEARNING_OPTIONS = /* @__PURE__ */ Symbol("AUTO_LEARNING_OPTIONS");
704
+ var AUTO_LEARNING_INSTANCE = /* @__PURE__ */ Symbol("AUTO_LEARNING_INSTANCE");
705
+ var AUTO_LEARN_METADATA = "auto_learn_metadata";
706
+
707
+ // src/nestjs/auto-learning.interceptor.ts
708
+ var AutoLearningInterceptor = class {
709
+ constructor(reflector, core) {
710
+ this.reflector = reflector;
711
+ this.core = core;
712
+ }
713
+ reflector;
714
+ core;
715
+ intercept(context, next) {
716
+ const options = this.reflector.get(
717
+ AUTO_LEARN_METADATA,
718
+ context.getHandler()
719
+ );
720
+ if (!options) {
721
+ return next.handle();
722
+ }
723
+ const start = Date.now();
724
+ const req = context.switchToHttp().getRequest();
725
+ const { method, path } = this.extractRequestInfo(req);
726
+ return next.handle().pipe(
727
+ (0, import_rxjs.tap)(() => {
728
+ const duration = Date.now() - start;
729
+ const status = context.switchToHttp().getResponse().statusCode;
730
+ this.core.recordPattern({
731
+ method,
732
+ path,
733
+ statusCode: status,
734
+ durationMs: duration,
735
+ timestamp: /* @__PURE__ */ new Date(),
736
+ metadata: options.customMetadata ? options.customMetadata(req) : void 0
737
+ });
738
+ })
739
+ );
740
+ }
741
+ extractRequestInfo(req) {
742
+ const method = req.method ?? "UNKNOWN";
743
+ const path = req.route?.path ?? req.path ?? req.url ?? "/";
744
+ return { method, path };
745
+ }
746
+ };
747
+ AutoLearningInterceptor = __decorateClass([
748
+ (0, import_common.Injectable)(),
749
+ __decorateParam(1, (0, import_common.Inject)(AUTO_LEARNING_INSTANCE))
750
+ ], AutoLearningInterceptor);
751
+
752
+ // src/nestjs/backend-kit-observability-adapter.ts
753
+ var BackendKitObservabilityAdapter = class {
754
+ constructor(logger, metrics) {
755
+ this.logger = logger;
756
+ this.metrics = metrics;
757
+ }
758
+ logger;
759
+ metrics;
760
+ prefix = "auto_learning";
761
+ info(msg, meta) {
762
+ this.logger.log?.(`[AutoLearn] ${msg}`, meta);
763
+ }
764
+ warn(msg, meta) {
765
+ this.logger.warn?.(`[AutoLearn] ${msg}`, meta);
766
+ }
767
+ error(msg, meta) {
768
+ this.logger.error?.(`[AutoLearn] ${msg}`, meta);
769
+ }
770
+ debug(msg, meta) {
771
+ this.logger.debug?.(`[AutoLearn] ${msg}`, meta);
772
+ }
773
+ incrementMetric(name, value = 1, tags) {
774
+ this.metrics?.increment?.(`${this.prefix}.${name}`, value, tags);
775
+ }
776
+ gaugeMetric(name, value, tags) {
777
+ this.metrics?.gauge?.(`${this.prefix}.${name}`, value, tags);
778
+ }
779
+ histogramMetric(name, value, tags) {
780
+ this.metrics?.histogram?.(`${this.prefix}.${name}`, value, tags);
781
+ }
782
+ };
783
+
784
+ // src/nestjs/auto-learning.module.ts
785
+ var AutoLearningModule = class {
786
+ static forRoot(options = {}) {
787
+ const providers = [
788
+ {
789
+ provide: AUTO_LEARNING_OPTIONS,
790
+ useValue: options
791
+ },
792
+ {
793
+ provide: AUTO_LEARNING_INSTANCE,
794
+ useFactory: (opts) => {
795
+ let observability;
796
+ if (opts.observability?.logger) {
797
+ observability = new BackendKitObservabilityAdapter(
798
+ opts.observability.logger,
799
+ opts.observability.metrics
800
+ );
801
+ }
802
+ return AutoLearningCore.create({
803
+ ...opts.coreOptions,
804
+ observability
805
+ });
806
+ },
807
+ inject: [AUTO_LEARNING_OPTIONS]
808
+ },
809
+ {
810
+ provide: import_core.APP_INTERCEPTOR,
811
+ useClass: AutoLearningInterceptor
812
+ }
813
+ ];
814
+ return {
815
+ module: AutoLearningModule,
816
+ providers,
817
+ exports: [AUTO_LEARNING_INSTANCE],
818
+ global: true
819
+ };
820
+ }
821
+ };
822
+ AutoLearningModule = __decorateClass([
823
+ (0, import_common2.Module)({})
824
+ ], AutoLearningModule);
825
+
826
+ // src/nestjs/auto-learning.decorator.ts
827
+ var import_common3 = require("@nestjs/common");
828
+ var AutoLearn = (options) => (0, import_common3.SetMetadata)(AUTO_LEARN_METADATA, options ?? {});
829
+ // Annotate the CommonJS export names for ESM import in node:
830
+ 0 && (module.exports = {
831
+ AUTO_LEARNING_INSTANCE,
832
+ AUTO_LEARNING_OPTIONS,
833
+ AUTO_LEARN_METADATA,
834
+ AutoLearn,
835
+ AutoLearningModule
836
+ });
837
+ //# sourceMappingURL=index.cjs.map