@runtime-digital-twin/sdk 1.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 (56) hide show
  1. package/README.md +214 -0
  2. package/dist/constants.d.ts +11 -0
  3. package/dist/constants.d.ts.map +1 -0
  4. package/dist/constants.js +13 -0
  5. package/dist/db-wrapper.d.ts +258 -0
  6. package/dist/db-wrapper.d.ts.map +1 -0
  7. package/dist/db-wrapper.js +636 -0
  8. package/dist/event-envelope.d.ts +35 -0
  9. package/dist/event-envelope.d.ts.map +1 -0
  10. package/dist/event-envelope.js +101 -0
  11. package/dist/fastify-plugin.d.ts +29 -0
  12. package/dist/fastify-plugin.d.ts.map +1 -0
  13. package/dist/fastify-plugin.js +243 -0
  14. package/dist/http-sentinels.d.ts +39 -0
  15. package/dist/http-sentinels.d.ts.map +1 -0
  16. package/dist/http-sentinels.js +169 -0
  17. package/dist/http-wrapper.d.ts +25 -0
  18. package/dist/http-wrapper.d.ts.map +1 -0
  19. package/dist/http-wrapper.js +477 -0
  20. package/dist/index.d.ts +19 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +93 -0
  23. package/dist/invariants.d.ts +58 -0
  24. package/dist/invariants.d.ts.map +1 -0
  25. package/dist/invariants.js +192 -0
  26. package/dist/multi-service-edge-builder.d.ts +80 -0
  27. package/dist/multi-service-edge-builder.d.ts.map +1 -0
  28. package/dist/multi-service-edge-builder.js +107 -0
  29. package/dist/outbound-matcher.d.ts +192 -0
  30. package/dist/outbound-matcher.d.ts.map +1 -0
  31. package/dist/outbound-matcher.js +457 -0
  32. package/dist/peer-service-resolver.d.ts +22 -0
  33. package/dist/peer-service-resolver.d.ts.map +1 -0
  34. package/dist/peer-service-resolver.js +85 -0
  35. package/dist/redaction.d.ts +111 -0
  36. package/dist/redaction.d.ts.map +1 -0
  37. package/dist/redaction.js +487 -0
  38. package/dist/replay-logger.d.ts +438 -0
  39. package/dist/replay-logger.d.ts.map +1 -0
  40. package/dist/replay-logger.js +434 -0
  41. package/dist/root-cause-analyzer.d.ts +45 -0
  42. package/dist/root-cause-analyzer.d.ts.map +1 -0
  43. package/dist/root-cause-analyzer.js +606 -0
  44. package/dist/shape-digest-utils.d.ts +45 -0
  45. package/dist/shape-digest-utils.d.ts.map +1 -0
  46. package/dist/shape-digest-utils.js +154 -0
  47. package/dist/trace-bundle-writer.d.ts +52 -0
  48. package/dist/trace-bundle-writer.d.ts.map +1 -0
  49. package/dist/trace-bundle-writer.js +267 -0
  50. package/dist/trace-loader.d.ts +69 -0
  51. package/dist/trace-loader.d.ts.map +1 -0
  52. package/dist/trace-loader.js +146 -0
  53. package/dist/trace-uploader.d.ts +25 -0
  54. package/dist/trace-uploader.d.ts.map +1 -0
  55. package/dist/trace-uploader.js +132 -0
  56. package/package.json +63 -0
@@ -0,0 +1,434 @@
1
+ "use strict";
2
+ /**
3
+ * Structured Replay Logger
4
+ *
5
+ * Provides structured logging for replay execution with stable keys.
6
+ * Supports JSON and human-readable output modes.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ReplayLogger = void 0;
10
+ exports.getReplayLogger = getReplayLogger;
11
+ exports.resetReplayLogger = resetReplayLogger;
12
+ exports.createReplayLogger = createReplayLogger;
13
+ // ============================================================================
14
+ // Logger Implementation
15
+ // ============================================================================
16
+ const LOG_LEVELS = {
17
+ debug: 0,
18
+ info: 1,
19
+ warn: 2,
20
+ error: 3,
21
+ };
22
+ const LEVEL_COLORS = {
23
+ debug: "\x1b[90m", // gray
24
+ info: "\x1b[36m", // cyan
25
+ warn: "\x1b[33m", // yellow
26
+ error: "\x1b[31m", // red
27
+ };
28
+ const CATEGORY_ICONS = {
29
+ http: "🌐",
30
+ db: "💾",
31
+ trace: "📦",
32
+ lifecycle: "⚙️",
33
+ system: "🔧",
34
+ };
35
+ const RESET = "\x1b[0m";
36
+ /**
37
+ * Replay Logger instance
38
+ */
39
+ class ReplayLogger {
40
+ config;
41
+ events = [];
42
+ startTime = Date.now();
43
+ constructor(config = {}) {
44
+ this.config = {
45
+ format: config.format ?? "human",
46
+ minLevel: config.minLevel ?? "info",
47
+ output: config.output ?? {
48
+ write: (msg) => process.stderr.write(msg + "\n"),
49
+ },
50
+ timestamps: config.timestamps ?? true,
51
+ includeTrace: config.includeTrace ?? false,
52
+ colors: config.colors ?? process.stdout.isTTY,
53
+ };
54
+ }
55
+ /**
56
+ * Get all logged events
57
+ */
58
+ getEvents() {
59
+ return [...this.events];
60
+ }
61
+ /**
62
+ * Clear all logged events
63
+ */
64
+ clear() {
65
+ this.events = [];
66
+ this.startTime = Date.now();
67
+ }
68
+ /**
69
+ * Check if a log level should be emitted
70
+ */
71
+ shouldLog(level) {
72
+ return LOG_LEVELS[level] >= LOG_LEVELS[this.config.minLevel];
73
+ }
74
+ /**
75
+ * Log an event
76
+ */
77
+ log(event) {
78
+ const fullEvent = {
79
+ ...event,
80
+ timestamp: Date.now(),
81
+ };
82
+ this.events.push(fullEvent);
83
+ if (!this.shouldLog(event.level)) {
84
+ return;
85
+ }
86
+ const output = this.config.format === "json"
87
+ ? this.formatJson(fullEvent)
88
+ : this.formatHuman(fullEvent);
89
+ this.config.output.write(output);
90
+ }
91
+ /**
92
+ * Format event as JSON
93
+ */
94
+ formatJson(event) {
95
+ return JSON.stringify(event);
96
+ }
97
+ /**
98
+ * Format event as human-readable
99
+ */
100
+ formatHuman(event) {
101
+ const parts = [];
102
+ // Timestamp
103
+ if (this.config.timestamps) {
104
+ const elapsed = event.timestamp - this.startTime;
105
+ parts.push(`[+${elapsed.toString().padStart(6)}ms]`);
106
+ }
107
+ // Category icon
108
+ parts.push(CATEGORY_ICONS[event.category] || "•");
109
+ // Level with color
110
+ const levelStr = event.level.toUpperCase().padEnd(5);
111
+ if (this.config.colors) {
112
+ parts.push(`${LEVEL_COLORS[event.level]}${levelStr}${RESET}`);
113
+ }
114
+ else {
115
+ parts.push(levelStr);
116
+ }
117
+ // Event type
118
+ parts.push(`[${event.event}]`);
119
+ // Event-specific message
120
+ parts.push(this.formatEventMessage(event));
121
+ // Trace context
122
+ if (this.config.includeTrace && event.trace) {
123
+ parts.push(`(trace:${event.trace.traceId.substring(0, 16)}...)`);
124
+ }
125
+ return parts.join(" ");
126
+ }
127
+ /**
128
+ * Format event-specific message
129
+ */
130
+ formatEventMessage(event) {
131
+ switch (event.event) {
132
+ // HTTP events
133
+ case "http.outbound.match":
134
+ return `#${event.data.callIndex} ${event.data.method} ${event.data.normalizedUrl} → matched span:${event.data.matchedSpanId}`;
135
+ case "http.outbound.return":
136
+ return `#${event.data.callIndex} ${event.data.method} ${event.data.url} → ${event.data.statusCode}`;
137
+ case "http.outbound.mismatch":
138
+ return `#${event.data.callIndex} expected ${event.data.expected.method} ${event.data.expected.url} got ${event.data.actual.method} ${event.data.actual.url}`;
139
+ case "http.outbound.unexpected":
140
+ return `#${event.data.callIndex} unexpected call ${event.data.method} ${event.data.url} (only ${event.data.expectedCallCount} recorded)`;
141
+ case "http.outbound.missing":
142
+ return `#${event.data.callIndex} missing call ${event.data.method} ${event.data.url} (span:${event.data.spanId})`;
143
+ case "http.outbound.blocked":
144
+ return `BLOCKED ${event.data.method} ${event.data.url}: ${event.data.reason}`;
145
+ // DB events
146
+ case "db.query.match":
147
+ return `#${event.data.queryIndex} ${event.data.operation} → matched span:${event.data.matchedSpanId} (${event.data.rowCount} rows)`;
148
+ case "db.query.return":
149
+ return `#${event.data.queryIndex} ${event.data.operation} → ${event.data.rowCount} rows`;
150
+ case "db.query.mismatch":
151
+ return `#${event.data.queryIndex} expected ${event.data.expected.operation}: "${event.data.expected.sql.substring(0, 40)}..." got "${event.data.actual.sql.substring(0, 40)}..."`;
152
+ case "db.query.unexpected":
153
+ return `#${event.data.queryIndex} unexpected query ${event.data.operation}: "${event.data.sql.substring(0, 50)}..." (only ${event.data.expectedQueryCount} recorded)`;
154
+ case "db.query.missing":
155
+ return `#${event.data.queryIndex} missing query ${event.data.operation}: "${event.data.sql.substring(0, 50)}..." (span:${event.data.spanId})`;
156
+ case "db.query.blocked":
157
+ return `BLOCKED ${event.data.operation}: ${event.data.reason}`;
158
+ // Trace events
159
+ case "trace.load":
160
+ return `loaded ${event.data.traceId} from ${event.data.traceDir} (v${event.data.formatVersion}, ${event.data.httpCallCount || 0} HTTP, ${event.data.dbQueryCount || 0} DB)`;
161
+ case "trace.load.warning":
162
+ return `${event.data.warning}`;
163
+ case "trace.load.error":
164
+ return `${event.data.errorType}: ${event.data.message}`;
165
+ // Lifecycle events
166
+ case "replay.start":
167
+ return `starting replay of ${event.data.traceId} in ${event.data.mode} mode`;
168
+ case "replay.complete":
169
+ const status = event.data.success ? "✓ SUCCESS" : "✗ FAILED";
170
+ return `${status} in ${event.data.durationMs}ms (HTTP: ${event.data.httpCallsMatched}/${event.data.httpCallsTotal}, DB: ${event.data.dbQueriesMatched}/${event.data.dbQueriesTotal}, ${event.data.divergenceCount} divergences, ${event.data.errorCount} errors)`;
171
+ case "replay.error":
172
+ return `${event.data.errorType}: ${event.data.message}${event.data.location ? ` at ${event.data.location}` : ""}`;
173
+ case "replay.divergence":
174
+ return `[${event.data.source}] ${event.data.message} (${event.data.recoverable ? "recoverable" : "fatal"})`;
175
+ default:
176
+ return JSON.stringify(event.data || {});
177
+ }
178
+ }
179
+ // ============================================================================
180
+ // Convenience Methods
181
+ // ============================================================================
182
+ /**
183
+ * Log HTTP outbound match
184
+ */
185
+ httpMatch(data, trace) {
186
+ this.log({
187
+ event: "http.outbound.match",
188
+ category: "http",
189
+ level: "info",
190
+ data,
191
+ trace,
192
+ });
193
+ }
194
+ /**
195
+ * Log HTTP outbound return
196
+ */
197
+ httpReturn(data, trace) {
198
+ this.log({
199
+ event: "http.outbound.return",
200
+ category: "http",
201
+ level: "debug",
202
+ data,
203
+ trace,
204
+ });
205
+ }
206
+ /**
207
+ * Log HTTP outbound mismatch
208
+ */
209
+ httpMismatch(data, trace) {
210
+ this.log({
211
+ event: "http.outbound.mismatch",
212
+ category: "http",
213
+ level: "error",
214
+ data,
215
+ trace,
216
+ });
217
+ }
218
+ /**
219
+ * Log HTTP outbound unexpected
220
+ */
221
+ httpUnexpected(data, trace) {
222
+ this.log({
223
+ event: "http.outbound.unexpected",
224
+ category: "http",
225
+ level: "error",
226
+ data,
227
+ trace,
228
+ });
229
+ }
230
+ /**
231
+ * Log HTTP outbound missing
232
+ */
233
+ httpMissing(data, trace) {
234
+ this.log({
235
+ event: "http.outbound.missing",
236
+ category: "http",
237
+ level: "error",
238
+ data,
239
+ trace,
240
+ });
241
+ }
242
+ /**
243
+ * Log HTTP outbound blocked
244
+ */
245
+ httpBlocked(data, trace) {
246
+ this.log({
247
+ event: "http.outbound.blocked",
248
+ category: "http",
249
+ level: "error",
250
+ data,
251
+ trace,
252
+ });
253
+ }
254
+ /**
255
+ * Log DB query match
256
+ */
257
+ dbMatch(data, trace) {
258
+ this.log({
259
+ event: "db.query.match",
260
+ category: "db",
261
+ level: "info",
262
+ data,
263
+ trace,
264
+ });
265
+ }
266
+ /**
267
+ * Log DB query return
268
+ */
269
+ dbReturn(data, trace) {
270
+ this.log({
271
+ event: "db.query.return",
272
+ category: "db",
273
+ level: "debug",
274
+ data,
275
+ trace,
276
+ });
277
+ }
278
+ /**
279
+ * Log DB query mismatch
280
+ */
281
+ dbMismatch(data, trace) {
282
+ this.log({
283
+ event: "db.query.mismatch",
284
+ category: "db",
285
+ level: "error",
286
+ data,
287
+ trace,
288
+ });
289
+ }
290
+ /**
291
+ * Log DB query unexpected
292
+ */
293
+ dbUnexpected(data, trace) {
294
+ this.log({
295
+ event: "db.query.unexpected",
296
+ category: "db",
297
+ level: "error",
298
+ data,
299
+ trace,
300
+ });
301
+ }
302
+ /**
303
+ * Log DB query missing
304
+ */
305
+ dbMissing(data, trace) {
306
+ this.log({
307
+ event: "db.query.missing",
308
+ category: "db",
309
+ level: "error",
310
+ data,
311
+ trace,
312
+ });
313
+ }
314
+ /**
315
+ * Log DB query blocked
316
+ */
317
+ dbBlocked(data, trace) {
318
+ this.log({
319
+ event: "db.query.blocked",
320
+ category: "db",
321
+ level: "error",
322
+ data,
323
+ trace,
324
+ });
325
+ }
326
+ /**
327
+ * Log trace load
328
+ */
329
+ traceLoad(data) {
330
+ this.log({
331
+ event: "trace.load",
332
+ category: "trace",
333
+ level: "info",
334
+ data,
335
+ trace: { traceId: data.traceId },
336
+ });
337
+ }
338
+ /**
339
+ * Log trace load warning
340
+ */
341
+ traceLoadWarning(data) {
342
+ this.log({
343
+ event: "trace.load.warning",
344
+ category: "trace",
345
+ level: "warn",
346
+ data,
347
+ });
348
+ }
349
+ /**
350
+ * Log trace load error
351
+ */
352
+ traceLoadError(data) {
353
+ this.log({
354
+ event: "trace.load.error",
355
+ category: "trace",
356
+ level: "error",
357
+ data,
358
+ });
359
+ }
360
+ /**
361
+ * Log replay start
362
+ */
363
+ replayStart(data) {
364
+ this.log({
365
+ event: "replay.start",
366
+ category: "lifecycle",
367
+ level: "info",
368
+ data,
369
+ trace: { traceId: data.traceId },
370
+ });
371
+ }
372
+ /**
373
+ * Log replay complete
374
+ */
375
+ replayComplete(data) {
376
+ this.log({
377
+ event: "replay.complete",
378
+ category: "lifecycle",
379
+ level: "info",
380
+ data,
381
+ trace: { traceId: data.traceId },
382
+ });
383
+ }
384
+ /**
385
+ * Log replay error
386
+ */
387
+ replayError(data) {
388
+ this.log({
389
+ event: "replay.error",
390
+ category: "lifecycle",
391
+ level: "error",
392
+ data,
393
+ trace: { traceId: data.traceId },
394
+ });
395
+ }
396
+ /**
397
+ * Log replay divergence
398
+ */
399
+ replayDivergence(data, trace) {
400
+ this.log({
401
+ event: "replay.divergence",
402
+ category: "lifecycle",
403
+ level: "warn",
404
+ data,
405
+ trace,
406
+ });
407
+ }
408
+ }
409
+ exports.ReplayLogger = ReplayLogger;
410
+ // ============================================================================
411
+ // Global Logger Instance
412
+ // ============================================================================
413
+ let globalLogger = null;
414
+ /**
415
+ * Get or create the global replay logger
416
+ */
417
+ function getReplayLogger(config) {
418
+ if (!globalLogger || config) {
419
+ globalLogger = new ReplayLogger(config);
420
+ }
421
+ return globalLogger;
422
+ }
423
+ /**
424
+ * Reset the global replay logger
425
+ */
426
+ function resetReplayLogger() {
427
+ globalLogger = null;
428
+ }
429
+ /**
430
+ * Create a new replay logger instance
431
+ */
432
+ function createReplayLogger(config) {
433
+ return new ReplayLogger(config);
434
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Multi-service root cause analyzer
3
+ *
4
+ * Analyzes distributed traces to determine which upstream service
5
+ * most likely caused a failure.
6
+ */
7
+ import { type TraceEvent } from './multi-service-edge-builder';
8
+ export interface RootCauseAnalysis {
9
+ culprit: {
10
+ serviceName: string;
11
+ spanId: string;
12
+ operationName: string;
13
+ evidence: {
14
+ kind: 'http' | 'error';
15
+ statusCode?: number;
16
+ peerService?: string;
17
+ message?: string;
18
+ invariants?: Array<{
19
+ name: string;
20
+ paths?: string[];
21
+ }>;
22
+ responseShapeDigest?: {
23
+ hash?: string;
24
+ nullPaths?: string[];
25
+ };
26
+ };
27
+ confidence: number;
28
+ };
29
+ propagationPath: string[];
30
+ rankedFindings: Array<{
31
+ score: number;
32
+ reason: string;
33
+ serviceName: string;
34
+ spanId: string;
35
+ }>;
36
+ }
37
+ /**
38
+ * Analyze multi-service root cause
39
+ *
40
+ * @param traceId - The trace ID
41
+ * @param events - All events for this traceId
42
+ * @returns Root cause analysis result
43
+ */
44
+ export declare function analyzeMultiServiceRootCause(traceId: string, events: TraceEvent[]): RootCauseAnalysis;
45
+ //# sourceMappingURL=root-cause-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-cause-analyzer.d.ts","sourceRoot":"","sources":["../src/root-cause-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAc,KAAK,UAAU,EAAoB,MAAM,8BAA8B,CAAC;AAE7F,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE;YACR,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;YACvB,UAAU,CAAC,EAAE,MAAM,CAAC;YACpB,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,OAAO,CAAC,EAAE,MAAM,CAAC;YAEjB,UAAU,CAAC,EAAE,KAAK,CAAC;gBACjB,IAAI,EAAE,MAAM,CAAC;gBACb,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;aAClB,CAAC,CAAC;YACH,mBAAmB,CAAC,EAAE;gBACpB,IAAI,CAAC,EAAE,MAAM,CAAC;gBACd,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;aACtB,CAAC;SACH,CAAC;QACF,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,EAAE,KAAK,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ;AAwcD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,UAAU,EAAE,GACnB,iBAAiB,CAoSnB"}