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