apm-optima 1.0.4 → 2.0.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.
Files changed (177) hide show
  1. package/README.md +54 -23
  2. package/dashboard-out/404/index.html +1 -1
  3. package/dashboard-out/404.html +1 -1
  4. package/dashboard-out/__next.__PAGE__.txt +4 -4
  5. package/dashboard-out/__next._full.txt +17 -17
  6. package/dashboard-out/__next._head.txt +4 -4
  7. package/dashboard-out/__next._index.txt +10 -10
  8. package/dashboard-out/__next._tree.txt +2 -2
  9. package/dashboard-out/_next/static/chunks/{2xjcsu4pgp14_.js → 062oipeclh9bq.js} +1 -1
  10. package/dashboard-out/_next/static/chunks/0ncbimuip0-o7.css +3 -0
  11. package/dashboard-out/_next/static/chunks/0xtgenoch3q77.js +1 -0
  12. package/dashboard-out/_next/static/chunks/1xln1-s87x5yp.js +27 -0
  13. package/dashboard-out/_not-found/__next._full.txt +15 -15
  14. package/dashboard-out/_not-found/__next._head.txt +4 -4
  15. package/dashboard-out/_not-found/__next._index.txt +10 -10
  16. package/dashboard-out/_not-found/__next._not-found.__PAGE__.txt +2 -2
  17. package/dashboard-out/_not-found/__next._not-found.txt +3 -3
  18. package/dashboard-out/_not-found/__next._tree.txt +2 -2
  19. package/dashboard-out/_not-found/index.html +1 -1
  20. package/dashboard-out/_not-found/index.txt +15 -15
  21. package/dashboard-out/index.html +1 -1
  22. package/dashboard-out/index.txt +17 -17
  23. package/dist/adapters/session-export.handler.d.ts +4 -0
  24. package/dist/adapters/session-export.handler.js +29 -0
  25. package/dist/adapters/socket.options.d.ts +3 -0
  26. package/dist/adapters/socket.options.js +16 -0
  27. package/dist/config/config.manager.d.ts +80 -3
  28. package/dist/config/config.manager.js +269 -89
  29. package/dist/config/config.types.d.ts +89 -13
  30. package/dist/core/delivery/buckets-request.d.ts +2 -0
  31. package/dist/core/delivery/buckets-request.js +18 -0
  32. package/dist/core/delivery/index.d.ts +1 -0
  33. package/dist/core/delivery/index.js +1 -0
  34. package/dist/core/delivery/metrics.publisher.d.ts +23 -10
  35. package/dist/core/delivery/metrics.publisher.js +22 -22
  36. package/dist/core/delivery/websocket.events.d.ts +14 -3
  37. package/dist/core/delivery/websocket.events.js +13 -2
  38. package/dist/core/domain/correlation.types.d.ts +37 -0
  39. package/dist/core/domain/correlation.types.js +2 -0
  40. package/dist/core/domain/dashboard.types.d.ts +16 -69
  41. package/dist/core/domain/dashboard.types.js +29 -0
  42. package/dist/core/domain/endpoints.types.d.ts +3 -0
  43. package/dist/core/domain/event.types.d.ts +14 -0
  44. package/dist/core/domain/event.types.js +2 -0
  45. package/dist/core/domain/incident.types.d.ts +50 -0
  46. package/dist/core/domain/incident.types.js +2 -0
  47. package/dist/core/domain/index.d.ts +5 -0
  48. package/dist/core/domain/index.js +5 -0
  49. package/dist/core/domain/live-bucket.types.d.ts +86 -0
  50. package/dist/core/domain/live-bucket.types.js +2 -0
  51. package/dist/core/domain/metrics.types.d.ts +41 -11
  52. package/dist/core/domain/session.types.d.ts +93 -0
  53. package/dist/core/domain/session.types.js +2 -0
  54. package/dist/core/domain/telemetry.types.d.ts +1 -0
  55. package/dist/core/organizers/event.manager.d.ts +19 -0
  56. package/dist/core/organizers/event.manager.js +73 -0
  57. package/dist/core/organizers/index.d.ts +2 -0
  58. package/dist/core/organizers/index.js +18 -0
  59. package/dist/core/organizers/interval.manager.d.ts +18 -0
  60. package/dist/core/organizers/interval.manager.js +54 -0
  61. package/dist/core/storage/index.d.ts +1 -0
  62. package/dist/core/storage/index.js +1 -0
  63. package/dist/core/storage/local.repository.d.ts +12 -12
  64. package/dist/core/storage/local.repository.js +38 -38
  65. package/dist/core/storage/persistence/persistence-writer.d.ts +48 -0
  66. package/dist/core/storage/persistence/persistence-writer.js +191 -0
  67. package/dist/core/storage/persistence/segment-index.d.ts +27 -0
  68. package/dist/core/storage/persistence/segment-index.js +206 -0
  69. package/dist/core/storage/persistence/session-registry.d.ts +28 -0
  70. package/dist/core/storage/persistence/session-registry.js +127 -0
  71. package/dist/core/storage/persistence/session-summary.d.ts +3 -0
  72. package/dist/core/storage/persistence/session-summary.js +112 -0
  73. package/dist/core/storage/persistence.layer.d.ts +69 -0
  74. package/dist/core/storage/persistence.layer.js +404 -0
  75. package/dist/core/storage/runtime/anomaly-detector.d.ts +11 -0
  76. package/dist/core/storage/runtime/anomaly-detector.js +42 -0
  77. package/dist/core/storage/stores/analytics.store.d.ts +5 -5
  78. package/dist/core/storage/stores/analytics.store.js +39 -12
  79. package/dist/core/storage/stores/bucket-metric.d.ts +85 -0
  80. package/dist/core/storage/stores/bucket-metric.js +168 -0
  81. package/dist/core/storage/stores/bucket-view.d.ts +44 -0
  82. package/dist/core/storage/stores/bucket-view.js +63 -0
  83. package/dist/core/storage/stores/bucket.store.d.ts +39 -36
  84. package/dist/core/storage/stores/bucket.store.js +112 -60
  85. package/dist/core/storage/stores/dashboard.store.d.ts +11 -4
  86. package/dist/core/storage/stores/dashboard.store.js +179 -47
  87. package/dist/core/storage/stores/endpoint.store.d.ts +1 -0
  88. package/dist/core/storage/stores/endpoint.store.js +21 -5
  89. package/dist/core/storage/stores/index.d.ts +0 -7
  90. package/dist/core/storage/stores/index.js +0 -7
  91. package/dist/core/storage/stores/metrics.store.d.ts +1 -1
  92. package/dist/core/storage/stores/metrics.store.js +1 -0
  93. package/dist/core/storage/stores/runtime.store.d.ts +77 -0
  94. package/dist/core/storage/stores/runtime.store.js +222 -0
  95. package/dist/core/storage/utility/common.d.ts +1 -0
  96. package/dist/core/storage/utility/common.js +11 -0
  97. package/dist/core/storage/utility/file-buffer.d.ts +31 -0
  98. package/dist/core/storage/utility/file-buffer.js +238 -0
  99. package/dist/core/storage/utility/histogram.d.ts +1 -2
  100. package/dist/core/storage/utility/histogram.js +1 -2
  101. package/dist/core/storage/utility/index.d.ts +2 -0
  102. package/dist/core/storage/utility/index.js +2 -0
  103. package/dist/core/storage/utility/persistence-files.d.ts +3 -0
  104. package/dist/core/storage/utility/persistence-files.js +34 -0
  105. package/dist/core/storage/utility/persistence-writer.d.ts +39 -0
  106. package/dist/core/storage/utility/persistence-writer.js +183 -0
  107. package/dist/core/storage/utility/session-export.d.ts +3 -0
  108. package/dist/core/storage/utility/session-export.js +31 -0
  109. package/dist/core/storage/utility/session-registry.d.ts +21 -0
  110. package/dist/core/storage/utility/session-registry.js +91 -0
  111. package/dist/core/storage/utility/session-window.d.ts +8 -0
  112. package/dist/core/storage/utility/session-window.js +43 -0
  113. package/dist/core/telemetry/analytics.service.d.ts +18 -0
  114. package/dist/core/telemetry/analytics.service.js +100 -0
  115. package/dist/core/telemetry/collector.service.d.ts +17 -15
  116. package/dist/core/telemetry/collector.service.js +23 -80
  117. package/dist/core/telemetry/correlation.service.d.ts +57 -9
  118. package/dist/core/telemetry/correlation.service.js +246 -54
  119. package/dist/core/telemetry/dashboard-live.service.d.ts +10 -0
  120. package/dist/core/telemetry/dashboard-live.service.js +110 -0
  121. package/dist/core/telemetry/dashboard.service.d.ts +20 -0
  122. package/dist/core/telemetry/dashboard.service.js +140 -0
  123. package/dist/core/telemetry/incident.service.d.ts +71 -0
  124. package/dist/core/telemetry/incident.service.js +239 -0
  125. package/dist/core/telemetry/logger.d.ts +4 -0
  126. package/dist/core/telemetry/logger.js +5 -2
  127. package/dist/core/telemetry/runtime.service.d.ts +14 -0
  128. package/dist/core/telemetry/runtime.service.js +56 -0
  129. package/dist/core/telemetry/telemetry-query.service.d.ts +82 -0
  130. package/dist/core/telemetry/telemetry-query.service.js +231 -0
  131. package/dist/core/telemetry/telemetry.service.d.ts +10 -4
  132. package/dist/core/telemetry/telemetry.service.js +60 -10
  133. package/dist/core/telemetry/utility/correlation-analysis.d.ts +9 -0
  134. package/dist/core/telemetry/utility/correlation-analysis.js +143 -0
  135. package/dist/core/telemetry/utility/correlation-finding.d.ts +70 -0
  136. package/dist/core/telemetry/utility/correlation-finding.js +41 -0
  137. package/dist/core/telemetry/utility/correlation-input.d.ts +59 -0
  138. package/dist/core/telemetry/utility/correlation-input.js +139 -0
  139. package/dist/core/telemetry/utility/correlation-replay.d.ts +24 -0
  140. package/dist/core/telemetry/utility/correlation-replay.js +46 -0
  141. package/dist/core/utility/correlation.analysis.d.ts +9 -0
  142. package/dist/core/utility/correlation.analysis.js +144 -0
  143. package/dist/core/utility/statistics.d.ts +65 -0
  144. package/dist/core/utility/statistics.js +141 -0
  145. package/dist/express/index.d.ts +1 -1
  146. package/dist/express/index.js +7 -5
  147. package/dist/express/metrics-websocket.adapter.d.ts +8 -2
  148. package/dist/express/metrics-websocket.adapter.js +52 -17
  149. package/dist/express/metrics.adapter.d.ts +1 -0
  150. package/dist/express/metrics.adapter.js +8 -1
  151. package/dist/express/metrics.bootstrap.d.ts +6 -1
  152. package/dist/express/metrics.bootstrap.js +48 -22
  153. package/dist/express/metrics.dashboard.d.ts +2 -1
  154. package/dist/express/metrics.dashboard.js +4 -1
  155. package/dist/express/metrics.middleware.d.ts +2 -1
  156. package/dist/express/metrics.middleware.js +23 -20
  157. package/dist/nest/metrics-websocket.adapter.d.ts +19 -3
  158. package/dist/nest/metrics-websocket.adapter.js +101 -21
  159. package/dist/nest/metrics.adapter.d.ts +4 -1
  160. package/dist/nest/metrics.adapter.js +24 -1
  161. package/dist/nest/metrics.bootstrap.service.d.ts +7 -3
  162. package/dist/nest/metrics.bootstrap.service.js +56 -21
  163. package/dist/nest/metrics.interceptor.d.ts +3 -3
  164. package/dist/nest/metrics.interceptor.js +11 -8
  165. package/dist/nest/metrics.module.d.ts +3 -0
  166. package/dist/nest/metrics.module.js +27 -9
  167. package/dist/runtime/index.d.ts +32 -0
  168. package/dist/runtime/index.js +104 -0
  169. package/dist/simulation/simulation.d.ts +5 -16
  170. package/dist/simulation/simulation.js +5 -8
  171. package/package.json +2 -1
  172. package/dashboard-out/_next/static/chunks/20t9qbodfrw_z.js +0 -3
  173. package/dashboard-out/_next/static/chunks/23soo6qkun-yv.js +0 -1
  174. package/dashboard-out/_next/static/chunks/3xoopocxi2vi8.css +0 -3
  175. /package/dashboard-out/_next/static/{u8vU2V6fhASeGVCpWU3eW → 17egwqgk36_DSRF55LJZG}/_buildManifest.js +0 -0
  176. /package/dashboard-out/_next/static/{u8vU2V6fhASeGVCpWU3eW → 17egwqgk36_DSRF55LJZG}/_clientMiddlewareManifest.js +0 -0
  177. /package/dashboard-out/_next/static/{u8vU2V6fhASeGVCpWU3eW → 17egwqgk36_DSRF55LJZG}/_ssgManifest.js +0 -0
@@ -5,4 +5,5 @@ export interface TelemetryRequest {
5
5
  statusCode: number;
6
6
  responseTime: number;
7
7
  clientIp?: string;
8
+ origin?: 'real' | 'synthetic';
8
9
  }
@@ -0,0 +1,19 @@
1
+ import { PersistenceRepository } from '../storage/persistence.layer';
2
+ import { ApplicationEvent } from '../domain';
3
+ type AppPartialEvent = Omit<ApplicationEvent, 'timestamp' | 'applicationVersion'>;
4
+ type AsyncListener = (event: ApplicationEvent) => void | Promise<void>;
5
+ type ApplicationEventManagerInstance = Omit<ApplicationEventManager, 'start' | 'stop'>;
6
+ export declare class ApplicationEventManager {
7
+ private persistence;
8
+ private applicationVersion;
9
+ static instance: ApplicationEventManagerInstance | null;
10
+ private listeners;
11
+ private initilized;
12
+ constructor(persistence: PersistenceRepository, applicationVersion?: string);
13
+ start(): void;
14
+ stop(): void;
15
+ on(listener: AsyncListener): void;
16
+ off(listener: AsyncListener): void;
17
+ emit(event: AppPartialEvent): Promise<void>;
18
+ }
19
+ export {};
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ApplicationEventManager = void 0;
7
+ const logger_1 = __importDefault(require("../telemetry/logger"));
8
+ class ApplicationEventManager {
9
+ persistence;
10
+ applicationVersion;
11
+ static instance = null;
12
+ listeners = [];
13
+ initilized = false;
14
+ constructor(persistence, applicationVersion = '') {
15
+ this.persistence = persistence;
16
+ this.applicationVersion = applicationVersion;
17
+ if (ApplicationEventManager.instance) {
18
+ throw new Error('ApplicationEventManager is a singleton class. Use ApplicationEventManager.getInstance() to access the instance.');
19
+ }
20
+ ApplicationEventManager.instance = this;
21
+ }
22
+ start() {
23
+ if (this.initilized)
24
+ return;
25
+ this.initilized = true;
26
+ this.on((event) => this.persistence.onApplicationEvent(event));
27
+ }
28
+ stop() {
29
+ if (!this.initilized)
30
+ return;
31
+ for (const listener of this.listeners) {
32
+ this.off(listener);
33
+ }
34
+ this.listeners = [];
35
+ this.initilized = false;
36
+ }
37
+ on(listener) {
38
+ this.listeners.push(listener);
39
+ }
40
+ off(listener) {
41
+ this.listeners = this.listeners.filter(l => l !== listener);
42
+ }
43
+ async emit(event) {
44
+ if (!this.initilized)
45
+ return;
46
+ const fullEvent = {
47
+ timestamp: new Date().toISOString(),
48
+ type: event.type,
49
+ applicationVersion: this.applicationVersion,
50
+ reason: event.reason || '',
51
+ ...(event.details ? { details: event.details } : {}),
52
+ };
53
+ // Using Promise.allSettled to ensure all listeners are called, even if some fail
54
+ // But this is causing strange race conditions, so we will call them sequentially for now
55
+ // const results = await Promise.allSettled(
56
+ // this.listeners.map(listener => Promise.resolve(listener(fullEvent)))
57
+ // );
58
+ // for (const result of results) {
59
+ // if (result.status === 'rejected') {
60
+ // Logger.error('Error in event listener:', result.reason);
61
+ // }
62
+ // }
63
+ for (const listener of this.listeners) {
64
+ try {
65
+ await listener(fullEvent);
66
+ }
67
+ catch (error) {
68
+ logger_1.default.error('Error in event listener:', error);
69
+ }
70
+ }
71
+ }
72
+ }
73
+ exports.ApplicationEventManager = ApplicationEventManager;
@@ -0,0 +1,2 @@
1
+ export * from './interval.manager';
2
+ export * from './event.manager';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./interval.manager"), exports);
18
+ __exportStar(require("./event.manager"), exports);
@@ -0,0 +1,18 @@
1
+ import type { ReadonlyConfig } from "../../config";
2
+ interface TickCallback {
3
+ (): void;
4
+ }
5
+ interface TickCallbackAsync {
6
+ (): Promise<void>;
7
+ }
8
+ type IntervalType = 'tick' | 'publisher' | 'persistence';
9
+ export declare class IntervalManager {
10
+ private readonly config;
11
+ private intervals;
12
+ private initialized;
13
+ private started;
14
+ constructor(callbacks: Record<IntervalType, TickCallback | TickCallbackAsync | null>, config: ReadonlyConfig);
15
+ startIntervals(): void;
16
+ stopIntervals(): void;
17
+ }
18
+ export {};
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IntervalManager = void 0;
4
+ class IntervalManager {
5
+ config;
6
+ intervals = { tick: null, publisher: null, persistence: null, };
7
+ initialized = false;
8
+ started = false;
9
+ constructor(callbacks, config) {
10
+ this.config = config;
11
+ this.intervals.tick = callbacks.tick ? {
12
+ id: null,
13
+ callback: callbacks.tick,
14
+ intervalMs: config.tickIntervalMs,
15
+ } : null;
16
+ this.intervals.publisher = callbacks.publisher ? {
17
+ id: null,
18
+ callback: callbacks.publisher,
19
+ intervalMs: config.publisher.intervalMs,
20
+ } : null;
21
+ if (config.persistence !== false) {
22
+ this.intervals.persistence = callbacks.persistence ? {
23
+ id: null,
24
+ callback: callbacks.persistence,
25
+ intervalMs: config.persistence.archiveIntervalMs,
26
+ } : null;
27
+ }
28
+ this.initialized = true;
29
+ }
30
+ startIntervals() {
31
+ if (!this.initialized || this.started)
32
+ return;
33
+ for (const intervalType in this.intervals) {
34
+ const interval = this.intervals[intervalType];
35
+ if (interval && interval.callback) {
36
+ interval.id = setInterval(interval.callback, interval.intervalMs);
37
+ }
38
+ }
39
+ this.started = true;
40
+ }
41
+ stopIntervals() {
42
+ if (!this.initialized || !this.started)
43
+ return;
44
+ for (const intervalType in this.intervals) {
45
+ const interval = this.intervals[intervalType];
46
+ if (interval && interval.id) {
47
+ clearInterval(interval.id);
48
+ interval.id = null;
49
+ }
50
+ }
51
+ this.started = false;
52
+ }
53
+ }
54
+ exports.IntervalManager = IntervalManager;
@@ -1,3 +1,4 @@
1
1
  export * from './stores';
2
2
  export * from './utility';
3
3
  export * from './local.repository';
4
+ export * from './persistence.layer';
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./stores"), exports);
18
18
  __exportStar(require("./utility"), exports);
19
19
  __exportStar(require("./local.repository"), exports);
20
+ __exportStar(require("./persistence.layer"), exports);
@@ -1,17 +1,17 @@
1
1
  import type { TelemetryRequest } from '../domain';
2
- import { BucketStore, DashboardStore, AnalyticsStore, HealthStore, SystemStore, EndpointStore, AlertStore, MetricsStore } from './stores';
3
- declare class LocalRepository {
4
- readonly endpoint: EndpointStore;
5
- readonly alerts: AlertStore;
6
- readonly metrics: MetricsStore;
7
- readonly health: HealthStore;
2
+ import type { ReadonlyConfig } from '../../config';
3
+ import { BucketStore } from './stores';
4
+ import { PersistenceRepository } from './persistence.layer';
5
+ import { RuntimeStore } from './stores/runtime.store';
6
+ export declare class LocalRepository {
7
+ private readonly persistence;
8
+ private readonly conf;
8
9
  readonly bucket: BucketStore;
9
- readonly dashboard: DashboardStore;
10
- readonly analytics: AnalyticsStore;
11
- readonly system: SystemStore;
12
- constructor();
10
+ readonly runtime: RuntimeStore;
11
+ constructor(persistence: PersistenceRepository, conf: ReadonlyConfig);
13
12
  record(request: TelemetryRequest): void;
14
13
  tick(): void;
14
+ closeLastInterval(): Promise<void>;
15
+ dispose(): void;
16
+ private closeInterval;
15
17
  }
16
- export declare const storage: LocalRepository;
17
- export {};
@@ -1,52 +1,52 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.storage = void 0;
4
- const config_1 = require("../../config");
3
+ exports.LocalRepository = void 0;
4
+ const crypto_1 = require("crypto");
5
5
  const stores_1 = require("./stores");
6
+ const runtime_store_1 = require("./stores/runtime.store");
6
7
  class LocalRepository {
7
- // Standard Stores
8
- endpoint;
9
- alerts;
10
- metrics;
11
- // Historics Store
12
- health;
8
+ persistence;
9
+ conf;
13
10
  bucket;
14
- // Core Stores
15
- dashboard;
16
- analytics;
17
- system;
18
- constructor() {
19
- const config = (0, config_1.getConfig)();
20
- // Standard
21
- this.endpoint = new stores_1.EndpointStore();
22
- this.alerts = new stores_1.AlertStore(config.alertBufferSize);
23
- this.metrics = new stores_1.MetricsStore();
24
- // Historic
25
- this.health = new stores_1.HealthStore(config.ringBufferSize, config.publisher.eventLoopLagThresholdMs);
26
- this.bucket = new stores_1.BucketStore(this.endpoint, this.health, this.metrics, config.ringBufferSize);
27
- this.dashboard = new stores_1.DashboardStore(this.bucket, this.health, this.endpoint, this.alerts);
28
- this.analytics = new stores_1.AnalyticsStore(this.bucket, this.endpoint, config.publisher.slowLatencyThresholdMs);
29
- this.system = new stores_1.SystemStore();
11
+ runtime;
12
+ constructor(persistence, conf) {
13
+ this.persistence = persistence;
14
+ this.conf = conf;
15
+ this.runtime = new runtime_store_1.RuntimeStore(conf);
16
+ this.bucket = new stores_1.BucketStore(this.runtime, {
17
+ sessionId: persistence.sessionId,
18
+ instanceId: (0, crypto_1.randomUUID)(),
19
+ intervalMs: conf.collection.bucketIntervalMs,
20
+ historySize: conf.cache.liveBuckets,
21
+ maxEndpoints: conf.collection.maxEndpointsPerBucket,
22
+ });
30
23
  }
31
24
  record(request) {
32
- const data = {
25
+ this.bucket.record({
33
26
  method: request.method,
34
27
  route: request.endpoint,
35
28
  duration: request.responseTime,
36
- statusCode: request.statusCode
37
- };
38
- this.endpoint.record(data);
39
- const status = this.metrics.record({
40
- duration: request.responseTime,
41
- statusCode: request.statusCode
29
+ statusCode: request.statusCode,
30
+ origin: request.origin
31
+ });
32
+ this.persistence.onHttpRequest({
33
+ method: request.method,
34
+ path: request.endpoint,
35
+ statusCode: request.statusCode,
36
+ durationMs: request.responseTime,
37
+ timestamp: new Date(request.timestamp).toISOString()
42
38
  });
43
- if (status === 'ANOMALY') {
44
- this.alerts.warning(`Anomaly detected: ${request.method} ${request.endpoint} took ${request.responseTime}ms`);
45
- }
46
- this.bucket.recordRequest(request.method, request.endpoint);
47
39
  }
48
- tick() {
49
- this.bucket.flush();
40
+ tick() { void this.closeInterval(false); }
41
+ closeLastInterval() { return this.closeInterval(true); }
42
+ dispose() { this.runtime.dispose(); }
43
+ async closeInterval(final) {
44
+ const bucket = this.bucket.flush({ force: final, seal: final });
45
+ if (!bucket)
46
+ return;
47
+ const ok = await this.persistence.onBucketClosed(bucket);
48
+ if (ok)
49
+ this.bucket.markCommitted(bucket.sequence);
50
50
  }
51
51
  }
52
- exports.storage = new LocalRepository();
52
+ exports.LocalRepository = LocalRepository;
@@ -0,0 +1,48 @@
1
+ import { type StoredRecord } from '../utility/index';
2
+ export type PersistenceCategory = 'http_requests' | 'events' | 'correlation' | 'metric_buckets';
3
+ export type StorageStatus = 'OK' | 'DEGRADED';
4
+ export interface WriterOptions {
5
+ maxQueue: number;
6
+ maxRetries: number;
7
+ retryDelayMs: number;
8
+ onStatusChange?: (status: StorageStatus) => void;
9
+ }
10
+ export interface RecordInput<T> {
11
+ recordId: string;
12
+ payload: T;
13
+ }
14
+ export declare class PersistenceWriter {
15
+ private readonly queue;
16
+ private runner;
17
+ private accepting;
18
+ private readonly maxQueue;
19
+ private readonly maxRetries;
20
+ private readonly retryDelayMs;
21
+ private readonly onStatusChange?;
22
+ private _status;
23
+ private _lostRecords;
24
+ private _completedJobs;
25
+ private _lastError;
26
+ constructor(opts: WriterOptions);
27
+ get status(): StorageStatus;
28
+ get lostRecords(): number;
29
+ get completedJobs(): number;
30
+ get lastError(): {
31
+ description: string;
32
+ message: string;
33
+ at: string;
34
+ } | null;
35
+ get isAccepting(): boolean;
36
+ get pending(): number;
37
+ enqueueAppend<T>(baseDir: string, category: PersistenceCategory, items: RecordInput<T>[], opts?: {
38
+ date?: Date;
39
+ }): Promise<number>;
40
+ enqueueTask(description: string, task: () => Promise<void>): Promise<number>;
41
+ whenIdle(): Promise<void>;
42
+ close(): Promise<void>;
43
+ private push;
44
+ private kick;
45
+ private drain;
46
+ private markLost;
47
+ }
48
+ export declare function dedupeRecords<T>(source: AsyncIterable<StoredRecord<T>>, onConflict?: (id: string) => void): AsyncGenerator<StoredRecord<T>>;
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.PersistenceWriter = void 0;
40
+ exports.dedupeRecords = dedupeRecords;
41
+ const fsp = __importStar(require("fs/promises"));
42
+ const logger_1 = __importDefault(require("../../telemetry/logger"));
43
+ const index_1 = require("../utility/index");
44
+ const sleep = (ms) => new Promise(r => setTimeout(r, ms));
45
+ class PersistenceWriter {
46
+ queue = [];
47
+ runner = null;
48
+ accepting = true;
49
+ maxQueue;
50
+ maxRetries;
51
+ retryDelayMs;
52
+ onStatusChange;
53
+ _status = 'OK';
54
+ _lostRecords = 0;
55
+ _completedJobs = 0;
56
+ _lastError = null;
57
+ constructor(opts) {
58
+ this.maxQueue = opts.maxQueue;
59
+ this.maxRetries = opts.maxRetries;
60
+ this.retryDelayMs = opts.retryDelayMs;
61
+ this.onStatusChange = opts.onStatusChange;
62
+ }
63
+ get status() { return this._status; }
64
+ get lostRecords() { return this._lostRecords; }
65
+ get completedJobs() { return this._completedJobs; }
66
+ get lastError() { return this._lastError; }
67
+ get isAccepting() { return this.accepting; }
68
+ get pending() { return this.queue.length; }
69
+ enqueueAppend(baseDir, category, items, opts = {}) {
70
+ if (items.length === 0)
71
+ return Promise.resolve(this._completedJobs);
72
+ const createdAt = new Date().toISOString();
73
+ const records = items.map(i => ({
74
+ id: i.recordId,
75
+ createdAt,
76
+ schemaVersion: 1,
77
+ category,
78
+ payload: i.payload
79
+ }));
80
+ let chunk;
81
+ try {
82
+ chunk = records.map(r => JSON.stringify(r)).join('\n') + '\n';
83
+ }
84
+ catch (err) {
85
+ this.markLost(items.length);
86
+ return Promise.reject(new Error(`Serialization failed for ${category}: ${err}`));
87
+ }
88
+ const filePath = (0, index_1.constructFilePath)({ baseDir, category, date: opts.date });
89
+ return this.push({
90
+ description: `Append ${records.length} records to ${filePath}`,
91
+ recordCount: items.length,
92
+ run: async (attempt) => {
93
+ await fsp.appendFile(filePath, attempt > 0 ? '\n' + chunk : chunk, 'utf-8');
94
+ },
95
+ });
96
+ }
97
+ enqueueTask(description, task) {
98
+ return this.push({ description, recordCount: 0, run: () => task() });
99
+ }
100
+ async whenIdle() {
101
+ while (this.runner)
102
+ await this.runner;
103
+ }
104
+ async close() {
105
+ this.accepting = false;
106
+ await this.whenIdle();
107
+ }
108
+ push(partial) {
109
+ if (!this.accepting) {
110
+ this.markLost(partial.recordCount);
111
+ return Promise.reject(new Error(`Writer closed; rejected: ${partial.description}`));
112
+ }
113
+ if (this.queue.length >= this.maxQueue) {
114
+ this.markLost(partial.recordCount);
115
+ logger_1.default.error(`PersistenceWriter: queue full (${this.maxQueue}); rejected: ${partial.description}`);
116
+ return Promise.reject(new Error('Persistence queue full'));
117
+ }
118
+ return new Promise((resolve, reject) => {
119
+ this.queue.push({ ...partial, resolve, reject });
120
+ this.kick();
121
+ });
122
+ }
123
+ kick() {
124
+ if (this.runner)
125
+ return;
126
+ this.runner = this.drain()
127
+ .catch(err => logger_1.default.error('PersistenceWriter: unexpected drain error', err))
128
+ .finally(() => {
129
+ this.runner = null;
130
+ if (this.queue.length > 0)
131
+ this.kick();
132
+ });
133
+ }
134
+ async drain() {
135
+ while (this.queue.length > 0) {
136
+ const job = this.queue[0];
137
+ let lastErr = null;
138
+ let ok = false;
139
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
140
+ try {
141
+ await job.run(attempt);
142
+ ok = true;
143
+ break;
144
+ }
145
+ catch (err) {
146
+ lastErr = err;
147
+ if (attempt < this.maxRetries)
148
+ await sleep(this.retryDelayMs * 2 ** attempt);
149
+ }
150
+ }
151
+ this.queue.shift();
152
+ if (ok) {
153
+ this._completedJobs++;
154
+ job.resolve(this._completedJobs);
155
+ }
156
+ else {
157
+ this._lastError = {
158
+ description: job.description,
159
+ message: lastErr instanceof Error ? lastErr.message : String(lastErr),
160
+ at: new Date().toISOString()
161
+ };
162
+ this.markLost(job.recordCount);
163
+ logger_1.default.error(`PersistenceWriter: giving up on "${job.description}":`, lastErr);
164
+ job.reject(lastErr instanceof Error ? lastErr : new Error(String(lastErr)));
165
+ }
166
+ }
167
+ }
168
+ markLost(count) {
169
+ this._lostRecords += count;
170
+ if (this._status !== 'DEGRADED') {
171
+ this._status = 'DEGRADED';
172
+ this.onStatusChange?.(this._status);
173
+ }
174
+ }
175
+ }
176
+ exports.PersistenceWriter = PersistenceWriter;
177
+ async function* dedupeRecords(source, onConflict) {
178
+ const seen = new Map();
179
+ for await (const record of source) {
180
+ const fingerprint = JSON.stringify(record.payload);
181
+ const prev = seen.get(record.id);
182
+ if (prev === undefined) {
183
+ seen.set(record.id, fingerprint);
184
+ yield record;
185
+ }
186
+ else if (prev !== fingerprint) {
187
+ logger_1.default.error(`Record ID conflict: ${record.id}`);
188
+ onConflict?.(record.id);
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,27 @@
1
+ import { type MetricBucket } from '../stores/bucket-metric';
2
+ export interface SegmentEntry {
3
+ date: string;
4
+ sessionId: string;
5
+ minSequence: number;
6
+ maxSequence: number;
7
+ minStartMs: number;
8
+ maxEndMs: number;
9
+ count: number;
10
+ }
11
+ export declare class SegmentIndex {
12
+ private readonly baseDir;
13
+ private readonly dir;
14
+ private readonly byDate;
15
+ private readonly dirty;
16
+ constructor(baseDir: string);
17
+ rebuild(): Promise<void>;
18
+ noteWritten(bucket: MetricBucket): void;
19
+ find(sessionId: string, fromMs: number, toMs: number): string[];
20
+ persistClosed(): Promise<void>;
21
+ private indexPath;
22
+ private resolveExistingFile;
23
+ private loadOrScan;
24
+ private tryLoad;
25
+ private scan;
26
+ private persist;
27
+ }