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
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tryOrDefault = tryOrDefault;
4
+ async function tryOrDefault(fn, fallback) {
5
+ try {
6
+ return await fn();
7
+ }
8
+ catch {
9
+ return fallback;
10
+ }
11
+ }
@@ -0,0 +1,31 @@
1
+ export interface StoredRecord<T = unknown> {
2
+ id: string;
3
+ createdAt: string;
4
+ schemaVersion: number;
5
+ category: string;
6
+ payload: T;
7
+ }
8
+ export interface WriteOptions {
9
+ schemaVersion?: number;
10
+ id?: string;
11
+ }
12
+ export interface PathAttrs {
13
+ baseDir: string;
14
+ category: string;
15
+ date?: Date;
16
+ }
17
+ export declare function constructFilePath(attrs: PathAttrs): string;
18
+ export declare function dataToBuffer(data: unknown): Buffer;
19
+ export declare function writeToFileBuffer(filePath: string, data: Buffer): void;
20
+ export declare function wrapRecord<T>(category: string, payload: T, options?: WriteOptions): StoredRecord<T>;
21
+ export declare function appendRecords<T>(filePath: string, category: string, payloads: T[], options?: WriteOptions): StoredRecord<T>[];
22
+ export declare function appendRecordsAsync<T>(filePath: string, category: string, payloads: T[], options?: WriteOptions): Promise<StoredRecord<T>[]>;
23
+ export declare function readRecords<T = unknown>(filePath: string): AsyncGenerator<StoredRecord<T>>;
24
+ export declare function readAllRecords<T = unknown>(filePath: string): Promise<StoredRecord<T>[]>;
25
+ export declare function writeNDJSONAtomic<T>(filePath: string, records: StoredRecord<T>[]): Promise<void>;
26
+ export declare function writeJSONAtomic<T>(filePath: string, data: unknown): Promise<void>;
27
+ export declare function readJSON<T>(filePath: string): Promise<T>;
28
+ export declare function writeChecksum(filePath: string): Promise<string>;
29
+ export declare function verifyChecksum(filePath: string): Promise<boolean>;
30
+ export declare function archiveFile(filePath: string, deleteOriginal?: boolean): Promise<string>;
31
+ export declare function cleanupOrphanedTempFiles(baseDir: string): Promise<number>;
@@ -0,0 +1,238 @@
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.constructFilePath = constructFilePath;
40
+ exports.dataToBuffer = dataToBuffer;
41
+ exports.writeToFileBuffer = writeToFileBuffer;
42
+ exports.wrapRecord = wrapRecord;
43
+ exports.appendRecords = appendRecords;
44
+ exports.appendRecordsAsync = appendRecordsAsync;
45
+ exports.readRecords = readRecords;
46
+ exports.readAllRecords = readAllRecords;
47
+ exports.writeNDJSONAtomic = writeNDJSONAtomic;
48
+ exports.writeJSONAtomic = writeJSONAtomic;
49
+ exports.readJSON = readJSON;
50
+ exports.writeChecksum = writeChecksum;
51
+ exports.verifyChecksum = verifyChecksum;
52
+ exports.archiveFile = archiveFile;
53
+ exports.cleanupOrphanedTempFiles = cleanupOrphanedTempFiles;
54
+ const fs = __importStar(require("fs"));
55
+ const fsp = __importStar(require("fs/promises"));
56
+ const path = __importStar(require("path"));
57
+ const zlib = __importStar(require("zlib"));
58
+ const crypto = __importStar(require("crypto"));
59
+ const readline = __importStar(require("readline"));
60
+ const crypto_1 = require("crypto");
61
+ const logger_1 = __importDefault(require("../../telemetry/logger"));
62
+ function constructFilePath(attrs) {
63
+ const { baseDir, category } = attrs;
64
+ const date = attrs.date ?? new Date();
65
+ const safeCategory = sanitizeSegment(category);
66
+ const dateStr = date.toISOString().slice(0, 10);
67
+ const dir = path.join(baseDir, safeCategory);
68
+ fs.mkdirSync(dir, { recursive: true });
69
+ return path.join(dir, `${dateStr}.ndjson`);
70
+ }
71
+ function sanitizeSegment(segment) {
72
+ const cleaned = segment.trim().replace(/[^a-zA-Z0-9_\-]/g, '_');
73
+ if (!cleaned)
74
+ throw new Error(`Invalid path segment: "${segment}"`);
75
+ return cleaned;
76
+ }
77
+ function dataToBuffer(data) {
78
+ if (Buffer.isBuffer(data)) {
79
+ return data;
80
+ }
81
+ else if (typeof data === 'string') {
82
+ return Buffer.from(data, 'utf8');
83
+ }
84
+ else if (data !== null && typeof data === 'object') {
85
+ return Buffer.from(JSON.stringify(data), 'utf8');
86
+ }
87
+ else {
88
+ throw new Error(`Invalid data type: ${typeof data}`);
89
+ }
90
+ }
91
+ function writeToFileBuffer(filePath, data) {
92
+ fs.writeFileSync(filePath, data);
93
+ }
94
+ function wrapRecord(category, payload, options = {}) {
95
+ return {
96
+ id: options.id ?? (0, crypto_1.randomUUID)(),
97
+ createdAt: new Date().toISOString(),
98
+ schemaVersion: options.schemaVersion ?? 1,
99
+ category,
100
+ payload,
101
+ };
102
+ }
103
+ function appendRecords(filePath, category, payloads, options = {}) {
104
+ const records = payloads.map(p => wrapRecord(category, p, options));
105
+ const lines = records.map(r => {
106
+ try {
107
+ return JSON.stringify(r);
108
+ }
109
+ catch (err) {
110
+ throw new Error(`Failed to serialize record: ${err}`);
111
+ }
112
+ });
113
+ const chunk = lines.join('\n') + '\n';
114
+ fs.appendFileSync(filePath, chunk, 'utf8');
115
+ return records;
116
+ }
117
+ async function appendRecordsAsync(filePath, category, payloads, options = {}) {
118
+ const records = payloads.map(p => wrapRecord(category, p, options));
119
+ const chunk = records.map(r => JSON.stringify(r)).join('\n') + '\n';
120
+ await fsp.appendFile(filePath, chunk, 'utf8');
121
+ return records;
122
+ }
123
+ async function* readRecords(filePath) {
124
+ const isGz = filePath.endsWith('.gz');
125
+ const rawStream = fs.createReadStream(filePath);
126
+ const stream = isGz ? rawStream.pipe(zlib.createGunzip()) : rawStream;
127
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
128
+ let lineNumber = 0;
129
+ for await (const line of rl) {
130
+ lineNumber++;
131
+ if (!line.trim())
132
+ continue;
133
+ try {
134
+ yield JSON.parse(line);
135
+ }
136
+ catch (err) {
137
+ logger_1.default.error('Failed to parse line', lineNumber, 'in file', filePath, ':', err);
138
+ }
139
+ }
140
+ }
141
+ async function readAllRecords(filePath) {
142
+ const out = [];
143
+ for await (const record of readRecords(filePath)) {
144
+ out.push(record);
145
+ }
146
+ return out;
147
+ }
148
+ async function writeNDJSONAtomic(filePath, records) {
149
+ const dir = path.dirname(filePath);
150
+ const tmpPath = path.join(dir, `.${path.basename(filePath)}.${process.pid}.${(0, crypto_1.randomUUID)()}.tmp`);
151
+ const content = records.map((r) => JSON.stringify(r)).join('\n') + (records.length ? '\n' : '');
152
+ const handle = await fsp.open(tmpPath, 'w');
153
+ try {
154
+ await handle.writeFile(content, 'utf8');
155
+ await handle.sync();
156
+ }
157
+ finally {
158
+ await handle.close();
159
+ }
160
+ await fsp.rename(tmpPath, filePath);
161
+ }
162
+ async function writeJSONAtomic(filePath, data) {
163
+ const dir = path.dirname(filePath);
164
+ const tmpPath = path.join(dir, `.${path.basename(filePath)}.${process.pid}.${(0, crypto_1.randomUUID)()}.tmp`);
165
+ await fsp.mkdir(dir, { recursive: true });
166
+ const handle = await fsp.open(tmpPath, "w");
167
+ try {
168
+ await handle.writeFile(JSON.stringify(data, null, 2), "utf8");
169
+ await handle.sync();
170
+ }
171
+ finally {
172
+ await handle.close();
173
+ }
174
+ await fsp.rename(tmpPath, filePath);
175
+ }
176
+ async function readJSON(filePath) {
177
+ const content = await fsp.readFile(filePath, 'utf8');
178
+ return JSON.parse(content);
179
+ }
180
+ async function writeChecksum(filePath) {
181
+ const hash = crypto.createHash('sha256');
182
+ const stream = fs.createReadStream(filePath);
183
+ for await (const chunk of stream)
184
+ hash.update(chunk);
185
+ const digest = hash.digest('hex');
186
+ await fsp.writeFile(`${filePath}.sha256`, `${digest} ${path.basename(filePath)}\n`, 'utf8');
187
+ return digest;
188
+ }
189
+ async function verifyChecksum(filePath) {
190
+ const sidecarPath = `${filePath}.sha256`;
191
+ const expected = (await fsp.readFile(sidecarPath, 'utf8')).split(/\s+/)[0];
192
+ const hash = crypto.createHash('sha256');
193
+ const stream = fs.createReadStream(filePath);
194
+ for await (const chunk of stream)
195
+ hash.update(chunk);
196
+ const actual = hash.digest('hex');
197
+ return actual === expected;
198
+ }
199
+ async function archiveFile(filePath, deleteOriginal = false) {
200
+ const gzPath = `${filePath}.gz`;
201
+ await new Promise((resolve, reject) => {
202
+ const source = fs.createReadStream(filePath);
203
+ const dest = fs.createWriteStream(gzPath);
204
+ const gzip = zlib.createGzip({ level: zlib.constants.Z_BEST_COMPRESSION });
205
+ source.pipe(gzip).pipe(dest);
206
+ dest.on('finish', () => resolve());
207
+ dest.on('error', (err) => reject(err));
208
+ source.on('error', (err) => reject(err));
209
+ });
210
+ await writeChecksum(gzPath);
211
+ if (deleteOriginal) {
212
+ await fsp.unlink(filePath);
213
+ await fsp.rm(`${filePath}.sha256`, { force: true });
214
+ }
215
+ return gzPath;
216
+ }
217
+ async function cleanupOrphanedTempFiles(baseDir) {
218
+ if (!fs.existsSync(baseDir)) {
219
+ logger_1.default.debug(`Base directory does not exist: ${baseDir}`);
220
+ return 0;
221
+ }
222
+ const files = await fsp.readdir(baseDir);
223
+ let removedCount = 0;
224
+ for (const file of files) {
225
+ if (file.startsWith('.') && file.includes('.tmp')) {
226
+ const fullPath = path.join(baseDir, file);
227
+ try {
228
+ await fsp.unlink(fullPath);
229
+ removedCount++;
230
+ logger_1.default.debug(`Removed orphaned temp file: ${fullPath}`);
231
+ }
232
+ catch (err) {
233
+ logger_1.default.error(`Failed to remove orphaned temp file: ${fullPath}`, err);
234
+ }
235
+ }
236
+ }
237
+ return removedCount;
238
+ }
@@ -19,8 +19,7 @@ export declare class Histogram {
19
19
  private totalSum;
20
20
  private totalSumOfSquares;
21
21
  private anomalyCount;
22
- constructor(anomalySampleSize?: number, //! For this use calculation
23
- anomalyLowerLimit?: number);
22
+ constructor(anomalySampleSize?: number, anomalyLowerLimit?: number);
24
23
  record(durationMs: number): RecordStatus;
25
24
  snapshot(): HistogramSnapshot;
26
25
  private percentile;
@@ -17,8 +17,7 @@ class Histogram {
17
17
  totalSum = 0;
18
18
  totalSumOfSquares = 0;
19
19
  anomalyCount = 0;
20
- constructor(anomalySampleSize = 60, //! For this use calculation
21
- anomalyLowerLimit = 180) {
20
+ constructor(anomalySampleSize = 60, anomalyLowerLimit = 180) {
22
21
  this.anomalySampleSize = anomalySampleSize;
23
22
  this.anomalyLowerLimit = anomalyLowerLimit;
24
23
  this.counters = new Uint32Array(this.limits.length);
@@ -1,2 +1,4 @@
1
1
  export * from './histogram';
2
2
  export * from './ring-buffer';
3
+ export * from './common';
4
+ export * from './file-buffer';
@@ -16,3 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./histogram"), exports);
18
18
  __exportStar(require("./ring-buffer"), exports);
19
+ __exportStar(require("./common"), exports);
20
+ __exportStar(require("./file-buffer"), exports);
@@ -0,0 +1,3 @@
1
+ export type PersistenceCategory = 'http_requests' | 'system_health' | 'events' | 'correlation';
2
+ export declare function resolveExistingFile(baseDir: string, category: PersistenceCategory, date: string): Promise<string | null>;
3
+ export declare function archiveOldPersistenceFiles(baseDir: string, category: PersistenceCategory): Promise<void>;
@@ -0,0 +1,34 @@
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.resolveExistingFile = resolveExistingFile;
7
+ exports.archiveOldPersistenceFiles = archiveOldPersistenceFiles;
8
+ const promises_1 = __importDefault(require("fs/promises"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const file_buffer_1 = require("./file-buffer");
11
+ async function resolveExistingFile(baseDir, category, date) {
12
+ const dir = path_1.default.join(baseDir, category);
13
+ const plain = path_1.default.join(dir, `${date}.ndjson`);
14
+ const gz = `${plain}.gz`;
15
+ if (await promises_1.default.access(plain).then(() => true).catch(() => false))
16
+ return plain;
17
+ if (await promises_1.default.access(gz).then(() => true).catch(() => false))
18
+ return gz;
19
+ return null;
20
+ }
21
+ async function archiveOldPersistenceFiles(baseDir, category) {
22
+ const dir = path_1.default.join(baseDir, category);
23
+ const today = new Date().toISOString().slice(0, 10);
24
+ const files = await promises_1.default.readdir(dir).catch(() => []);
25
+ for (const file of files) {
26
+ if (!file.endsWith('.ndjson'))
27
+ continue;
28
+ if (file.startsWith(today))
29
+ continue;
30
+ const fullPath = path_1.default.join(dir, file);
31
+ await (0, file_buffer_1.writeChecksum)(fullPath);
32
+ await (0, file_buffer_1.archiveFile)(fullPath);
33
+ }
34
+ }
@@ -0,0 +1,39 @@
1
+ import { type StoredRecord } from './index';
2
+ import type { PersistenceCategory } from './persistence-files';
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 _commitedSequence;
25
+ constructor(options?: WriterOptions);
26
+ get status(): StorageStatus;
27
+ get lostRecords(): number;
28
+ get commitedSequence(): number;
29
+ get pending(): number;
30
+ enqueueAppend<T>(baseDir: string, category: PersistenceCategory, items: RecordInput<T>[]): Promise<number>;
31
+ enqueueTask(description: string, task: () => Promise<void>): Promise<number>;
32
+ whenIdle(): Promise<void>;
33
+ close(): Promise<void>;
34
+ private push;
35
+ private kick;
36
+ private drain;
37
+ private markLost;
38
+ }
39
+ export declare function dedupeRecords<T>(source: AsyncIterable<StoredRecord<T>>, onConflict?: (id: string) => void): AsyncGenerator<StoredRecord<T>>;
@@ -0,0 +1,183 @@
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("./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
+ _commitedSequence = 0;
56
+ constructor(options = {}) {
57
+ this.maxQueue = options.maxQueue ?? 1000;
58
+ this.maxRetries = options.maxRetries ?? 3;
59
+ this.retryDelayMs = options.retryDelayMs ?? 100;
60
+ this.onStatusChange = options.onStatusChange;
61
+ }
62
+ get status() { return this._status; }
63
+ get lostRecords() { return this._lostRecords; }
64
+ get commitedSequence() { return this._commitedSequence; }
65
+ get pending() { return this.queue.length; }
66
+ enqueueAppend(baseDir, category, items) {
67
+ if (items.length === 0)
68
+ return Promise.resolve(this._commitedSequence);
69
+ const createdAt = new Date().toISOString();
70
+ const records = items.map(i => ({
71
+ id: i.recordId,
72
+ createdAt,
73
+ schemaVersion: 1,
74
+ category,
75
+ payload: i.payload
76
+ }));
77
+ let chunk;
78
+ try {
79
+ chunk = records.map(r => JSON.stringify(r)).join('\n') + '\n';
80
+ }
81
+ catch (err) {
82
+ this.markLost(items.length);
83
+ return Promise.reject(new Error(`Serialization failed for ${category}: ${err}`));
84
+ }
85
+ const filePath = (0, index_1.constructFilePath)({ baseDir, category });
86
+ return this.push({
87
+ description: `Append ${records.length} records to ${filePath}`,
88
+ recordCount: items.length,
89
+ run: async (attempt) => {
90
+ await fsp.appendFile(filePath, attempt > 0 ? '\n' + chunk : chunk, 'utf-8');
91
+ },
92
+ });
93
+ }
94
+ enqueueTask(description, task) {
95
+ return this.push({ description, recordCount: 0, run: () => task() });
96
+ }
97
+ async whenIdle() {
98
+ while (this.runner)
99
+ await this.runner;
100
+ }
101
+ async close() {
102
+ this.accepting = false;
103
+ await this.whenIdle();
104
+ }
105
+ push(partial) {
106
+ if (!this.accepting) {
107
+ this.markLost(partial.recordCount);
108
+ return Promise.reject(new Error(`Writer closed; rejected: ${partial.description}`));
109
+ }
110
+ if (this.queue.length >= this.maxQueue) {
111
+ this.markLost(partial.recordCount);
112
+ logger_1.default.error(`PersistenceWriter: queue full (${this.maxQueue}); rejected: ${partial.description}`);
113
+ return Promise.reject(new Error('Persistence queue full'));
114
+ }
115
+ return new Promise((resolve, reject) => {
116
+ this.queue.push({ ...partial, resolve, reject });
117
+ this.kick();
118
+ });
119
+ }
120
+ kick() {
121
+ if (this.runner)
122
+ return;
123
+ this.runner = this.drain()
124
+ .catch(err => logger_1.default.error('PersistenceWriter: unexpected drain error', err))
125
+ .finally(() => {
126
+ this.runner = null;
127
+ if (this.queue.length > 0)
128
+ this.kick();
129
+ });
130
+ }
131
+ async drain() {
132
+ while (this.queue.length > 0) {
133
+ const job = this.queue[0];
134
+ let lastErr = null;
135
+ let ok = false;
136
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
137
+ try {
138
+ await job.run(attempt);
139
+ ok = true;
140
+ break;
141
+ }
142
+ catch (err) {
143
+ lastErr = err;
144
+ if (attempt < this.maxRetries)
145
+ await sleep(this.retryDelayMs * 2 ** attempt);
146
+ }
147
+ }
148
+ this.queue.shift();
149
+ if (ok) {
150
+ this._commitedSequence++;
151
+ job.resolve(this._commitedSequence);
152
+ }
153
+ else {
154
+ this.markLost(job.recordCount);
155
+ logger_1.default.error(`PersistenceWriter: giving up on "${job.description}":`, lastErr);
156
+ job.reject(lastErr instanceof Error ? lastErr : new Error(String(lastErr)));
157
+ }
158
+ }
159
+ }
160
+ markLost(count) {
161
+ this._lostRecords += count;
162
+ if (this._status !== 'DEGRADED') {
163
+ this._status = 'DEGRADED';
164
+ this.onStatusChange?.(this._status);
165
+ }
166
+ }
167
+ }
168
+ exports.PersistenceWriter = PersistenceWriter;
169
+ async function* dedupeRecords(source, onConflict) {
170
+ const seen = new Map();
171
+ for await (const record of source) {
172
+ const fingerprint = JSON.stringify(record.payload);
173
+ const prev = seen.get(record.id);
174
+ if (prev === undefined) {
175
+ seen.set(record.id, fingerprint);
176
+ yield record;
177
+ }
178
+ else if (prev !== fingerprint) {
179
+ logger_1.default.error(`Record ID conflict: ${record.id}`);
180
+ onConflict?.(record.id);
181
+ }
182
+ }
183
+ }
@@ -0,0 +1,3 @@
1
+ import { type PersistenceCategory } from "./persistence-files";
2
+ import type { SessionWindow } from "./session-window";
3
+ export declare function streamWriteCategory(out: NodeJS.WritableStream, baseDir: string, category: PersistenceCategory, dates: string[], window: SessionWindow, isLast?: boolean): Promise<void>;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.streamWriteCategory = streamWriteCategory;
4
+ const file_buffer_1 = require("./file-buffer");
5
+ const persistence_files_1 = require("./persistence-files");
6
+ async function streamWriteCategoryRecords(out, baseDir, category, dates, window) {
7
+ let isFirst = true;
8
+ const { start: windowStart, end: windowEnd } = window;
9
+ for (const date of dates) {
10
+ const filePath = await (0, persistence_files_1.resolveExistingFile)(baseDir, category, date);
11
+ if (!filePath)
12
+ continue;
13
+ for await (const record of (0, file_buffer_1.readRecords)(filePath)) {
14
+ const ts = new Date(record.createdAt);
15
+ if (ts < windowStart || ts > windowEnd)
16
+ continue;
17
+ if (!isFirst)
18
+ out.write(',');
19
+ isFirst = false;
20
+ const canContinue = out.write(JSON.stringify(record));
21
+ if (!canContinue) {
22
+ await new Promise((resolve) => out.once('drain', resolve));
23
+ }
24
+ }
25
+ }
26
+ }
27
+ async function streamWriteCategory(out, baseDir, category, dates, window, isLast = false) {
28
+ out.write(`"${category}":[`);
29
+ await streamWriteCategoryRecords(out, baseDir, category, dates, window);
30
+ out.write(isLast ? ']' : '],');
31
+ }
@@ -0,0 +1,21 @@
1
+ import type { SessionRecord } from '../../domain';
2
+ import type { PersistenceWriter } from './persistence-writer';
3
+ export declare class SessionRegistry {
4
+ private readonly manifestPath;
5
+ private readonly writer;
6
+ private manifest;
7
+ private dirty;
8
+ private _current;
9
+ private _recoveredFromCrash;
10
+ constructor(manifestPath: string, writer: PersistenceWriter);
11
+ get current(): SessionRecord | null;
12
+ get all(): readonly SessionRecord[];
13
+ get recoveredFromCrash(): boolean;
14
+ start(): Promise<SessionRecord>;
15
+ notePersisted(): void;
16
+ heartbeat(): Promise<void>;
17
+ complete(): Promise<void>;
18
+ static windowEnd(s: SessionRecord): string | null;
19
+ private save;
20
+ private migrate;
21
+ }