@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.
- package/README.md +214 -0
- package/dist/constants.d.ts +11 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +13 -0
- package/dist/db-wrapper.d.ts +258 -0
- package/dist/db-wrapper.d.ts.map +1 -0
- package/dist/db-wrapper.js +636 -0
- package/dist/event-envelope.d.ts +35 -0
- package/dist/event-envelope.d.ts.map +1 -0
- package/dist/event-envelope.js +101 -0
- package/dist/fastify-plugin.d.ts +29 -0
- package/dist/fastify-plugin.d.ts.map +1 -0
- package/dist/fastify-plugin.js +243 -0
- package/dist/http-sentinels.d.ts +39 -0
- package/dist/http-sentinels.d.ts.map +1 -0
- package/dist/http-sentinels.js +169 -0
- package/dist/http-wrapper.d.ts +25 -0
- package/dist/http-wrapper.d.ts.map +1 -0
- package/dist/http-wrapper.js +477 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/invariants.d.ts +58 -0
- package/dist/invariants.d.ts.map +1 -0
- package/dist/invariants.js +192 -0
- package/dist/multi-service-edge-builder.d.ts +80 -0
- package/dist/multi-service-edge-builder.d.ts.map +1 -0
- package/dist/multi-service-edge-builder.js +107 -0
- package/dist/outbound-matcher.d.ts +192 -0
- package/dist/outbound-matcher.d.ts.map +1 -0
- package/dist/outbound-matcher.js +457 -0
- package/dist/peer-service-resolver.d.ts +22 -0
- package/dist/peer-service-resolver.d.ts.map +1 -0
- package/dist/peer-service-resolver.js +85 -0
- package/dist/redaction.d.ts +111 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +487 -0
- package/dist/replay-logger.d.ts +438 -0
- package/dist/replay-logger.d.ts.map +1 -0
- package/dist/replay-logger.js +434 -0
- package/dist/root-cause-analyzer.d.ts +45 -0
- package/dist/root-cause-analyzer.d.ts.map +1 -0
- package/dist/root-cause-analyzer.js +606 -0
- package/dist/shape-digest-utils.d.ts +45 -0
- package/dist/shape-digest-utils.d.ts.map +1 -0
- package/dist/shape-digest-utils.js +154 -0
- package/dist/trace-bundle-writer.d.ts +52 -0
- package/dist/trace-bundle-writer.d.ts.map +1 -0
- package/dist/trace-bundle-writer.js +267 -0
- package/dist/trace-loader.d.ts +69 -0
- package/dist/trace-loader.d.ts.map +1 -0
- package/dist/trace-loader.js +146 -0
- package/dist/trace-uploader.d.ts +25 -0
- package/dist/trace-uploader.d.ts.map +1 -0
- package/dist/trace-uploader.js +132 -0
- package/package.json +63 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadTraceMeta = loadTraceMeta;
|
|
4
|
+
exports.validateTraceStructure = validateTraceStructure;
|
|
5
|
+
exports.formatTraceLoadFailure = formatTraceLoadFailure;
|
|
6
|
+
const promises_1 = require("fs/promises");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
const constants_1 = require("./constants");
|
|
10
|
+
/**
|
|
11
|
+
* Check if a file exists
|
|
12
|
+
*/
|
|
13
|
+
async function fileExists(path) {
|
|
14
|
+
try {
|
|
15
|
+
await (0, promises_1.access)(path, fs_1.constants.F_OK);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load trace metadata with version guard.
|
|
24
|
+
*
|
|
25
|
+
* - If trace_format_version is missing: assumes version 1 and emits a warning
|
|
26
|
+
* - If trace_format_version > CURRENT_TRACE_FORMAT_VERSION: throws TraceLoadFailure
|
|
27
|
+
*
|
|
28
|
+
* @param traceDir - Path to the trace directory
|
|
29
|
+
* @returns TraceLoadResult with metadata and any warnings
|
|
30
|
+
* @throws TraceLoadFailure if trace cannot be loaded or version is unsupported
|
|
31
|
+
*/
|
|
32
|
+
async function loadTraceMeta(traceDir) {
|
|
33
|
+
const metaPath = (0, path_1.join)(traceDir, 'meta.json');
|
|
34
|
+
// Check file exists
|
|
35
|
+
if (!(await fileExists(metaPath))) {
|
|
36
|
+
const failure = {
|
|
37
|
+
type: 'missing_file',
|
|
38
|
+
location: metaPath,
|
|
39
|
+
expected: 'meta.json file',
|
|
40
|
+
actual: 'file not found',
|
|
41
|
+
hint: 'Ensure the trace directory contains a valid meta.json file',
|
|
42
|
+
};
|
|
43
|
+
throw failure;
|
|
44
|
+
}
|
|
45
|
+
// Read and parse meta.json
|
|
46
|
+
let rawMeta;
|
|
47
|
+
try {
|
|
48
|
+
const content = await (0, promises_1.readFile)(metaPath, 'utf8');
|
|
49
|
+
rawMeta = JSON.parse(content);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const failure = {
|
|
53
|
+
type: 'parse_error',
|
|
54
|
+
location: metaPath,
|
|
55
|
+
expected: 'valid JSON',
|
|
56
|
+
actual: `parse error: ${error instanceof Error ? error.message : String(error)}`,
|
|
57
|
+
hint: 'Check that meta.json contains valid JSON',
|
|
58
|
+
};
|
|
59
|
+
throw failure;
|
|
60
|
+
}
|
|
61
|
+
const warnings = [];
|
|
62
|
+
let version;
|
|
63
|
+
// Check trace_format_version
|
|
64
|
+
if (rawMeta.trace_format_version === undefined) {
|
|
65
|
+
// Missing version: assume version 1 and emit warning
|
|
66
|
+
version = 1;
|
|
67
|
+
const warning = {
|
|
68
|
+
type: 'warning',
|
|
69
|
+
code: 'missing_trace_format_version',
|
|
70
|
+
message: `trace_format_version missing in ${metaPath}, assuming version 1`,
|
|
71
|
+
traceDir,
|
|
72
|
+
assumedVersion: 1,
|
|
73
|
+
};
|
|
74
|
+
warnings.push(warning);
|
|
75
|
+
// Output warning to stderr for CLI visibility
|
|
76
|
+
console.warn(`[Trace Loader] Warning: ${warning.message}`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
version = Number(rawMeta.trace_format_version);
|
|
80
|
+
if (isNaN(version) || version < 1) {
|
|
81
|
+
const failure = {
|
|
82
|
+
type: 'validation_error',
|
|
83
|
+
location: metaPath,
|
|
84
|
+
expected: 'positive integer trace_format_version',
|
|
85
|
+
actual: String(rawMeta.trace_format_version),
|
|
86
|
+
hint: 'trace_format_version must be a positive integer',
|
|
87
|
+
};
|
|
88
|
+
throw failure;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Check if version is higher than supported
|
|
92
|
+
if (version > constants_1.CURRENT_TRACE_FORMAT_VERSION) {
|
|
93
|
+
const failure = {
|
|
94
|
+
type: 'schema_drift',
|
|
95
|
+
location: metaPath,
|
|
96
|
+
expected: `trace_format_version <= ${constants_1.CURRENT_TRACE_FORMAT_VERSION}`,
|
|
97
|
+
actual: `trace_format_version = ${version}`,
|
|
98
|
+
hint: `This trace was created with a newer version of the SDK. Upgrade your SDK to version that supports trace format version ${version}.`,
|
|
99
|
+
};
|
|
100
|
+
throw failure;
|
|
101
|
+
}
|
|
102
|
+
// Build typed metadata
|
|
103
|
+
const meta = {
|
|
104
|
+
trace_format_version: version,
|
|
105
|
+
traceId: String(rawMeta.traceId || ''),
|
|
106
|
+
startedAt: String(rawMeta.startedAt || ''),
|
|
107
|
+
completedAt: rawMeta.completedAt ? String(rawMeta.completedAt) : null,
|
|
108
|
+
serviceName: String(rawMeta.serviceName || ''),
|
|
109
|
+
serviceVersion: String(rawMeta.serviceVersion || ''),
|
|
110
|
+
environment: String(rawMeta.environment || ''),
|
|
111
|
+
mode: rawMeta.mode === 'replay' ? 'replay' : 'record',
|
|
112
|
+
inboundRequest: rawMeta.inboundRequest,
|
|
113
|
+
};
|
|
114
|
+
return { meta, warnings };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Validate that a trace directory contains required files.
|
|
118
|
+
*
|
|
119
|
+
* @param traceDir - Path to the trace directory
|
|
120
|
+
* @throws TraceLoadFailure if required files are missing
|
|
121
|
+
*/
|
|
122
|
+
async function validateTraceStructure(traceDir) {
|
|
123
|
+
const eventsPath = (0, path_1.join)(traceDir, 'events.jsonl');
|
|
124
|
+
if (!(await fileExists(eventsPath))) {
|
|
125
|
+
const failure = {
|
|
126
|
+
type: 'missing_file',
|
|
127
|
+
location: eventsPath,
|
|
128
|
+
expected: 'events.jsonl file',
|
|
129
|
+
actual: 'file not found',
|
|
130
|
+
hint: 'Ensure the trace directory contains a valid events.jsonl file',
|
|
131
|
+
};
|
|
132
|
+
throw failure;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Helper to format TraceLoadFailure for CLI output
|
|
137
|
+
*/
|
|
138
|
+
function formatTraceLoadFailure(failure) {
|
|
139
|
+
return [
|
|
140
|
+
`[Trace Load Error] ${failure.type}`,
|
|
141
|
+
` Location: ${failure.location}`,
|
|
142
|
+
` Expected: ${failure.expected}`,
|
|
143
|
+
` Actual: ${failure.actual}`,
|
|
144
|
+
` Hint: ${failure.hint}`,
|
|
145
|
+
].join('\n');
|
|
146
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace Uploader
|
|
3
|
+
*
|
|
4
|
+
* Uploads trace bundles to the ingestion endpoint after capture
|
|
5
|
+
*/
|
|
6
|
+
export interface TraceUploadOptions {
|
|
7
|
+
uploadUrl: string;
|
|
8
|
+
apiKey: string;
|
|
9
|
+
traceDir: string;
|
|
10
|
+
traceId: string;
|
|
11
|
+
serviceName: string;
|
|
12
|
+
serviceVersion?: string;
|
|
13
|
+
environment?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface TraceUploadResult {
|
|
16
|
+
success: boolean;
|
|
17
|
+
traceId: string;
|
|
18
|
+
blobRoot?: string;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Upload trace to ingestion endpoint
|
|
23
|
+
*/
|
|
24
|
+
export declare function uploadTrace(options: TraceUploadOptions): Promise<TraceUploadResult>;
|
|
25
|
+
//# sourceMappingURL=trace-uploader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-uploader.d.ts","sourceRoot":"","sources":["../src/trace-uploader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+CD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAiF5B"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Trace Uploader
|
|
4
|
+
*
|
|
5
|
+
* Uploads trace bundles to the ingestion endpoint after capture
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.uploadTrace = uploadTrace;
|
|
9
|
+
const promises_1 = require("fs/promises");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const fs_1 = require("fs");
|
|
12
|
+
const readline_1 = require("readline");
|
|
13
|
+
/**
|
|
14
|
+
* Read trace events from events.jsonl
|
|
15
|
+
*/
|
|
16
|
+
async function readTraceEvents(eventsPath) {
|
|
17
|
+
const events = [];
|
|
18
|
+
try {
|
|
19
|
+
const fileStream = (0, fs_1.createReadStream)(eventsPath);
|
|
20
|
+
const rl = (0, readline_1.createInterface)({
|
|
21
|
+
input: fileStream,
|
|
22
|
+
crlfDelay: Infinity,
|
|
23
|
+
});
|
|
24
|
+
for await (const line of rl) {
|
|
25
|
+
const trimmed = line.trim();
|
|
26
|
+
if (trimmed) {
|
|
27
|
+
try {
|
|
28
|
+
events.push(JSON.parse(trimmed));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.warn(`[SDK] Failed to parse event line: ${error}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
// If file doesn't exist or can't be read, return empty array
|
|
38
|
+
if (error.code !== 'ENOENT') {
|
|
39
|
+
console.warn(`[SDK] Failed to read events.jsonl: ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return events;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Read trace metadata from meta.json
|
|
46
|
+
*/
|
|
47
|
+
async function readTraceMeta(metaPath) {
|
|
48
|
+
try {
|
|
49
|
+
const content = await (0, promises_1.readFile)(metaPath, 'utf-8');
|
|
50
|
+
return JSON.parse(content);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
throw new Error(`Failed to read meta.json: ${error.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Upload trace to ingestion endpoint
|
|
58
|
+
*/
|
|
59
|
+
async function uploadTrace(options) {
|
|
60
|
+
const { uploadUrl, apiKey, traceDir, traceId, serviceName, serviceVersion, environment } = options;
|
|
61
|
+
try {
|
|
62
|
+
// Trace files are stored in traceDir/traceId/
|
|
63
|
+
const tracePath = (0, path_1.join)(traceDir, traceId);
|
|
64
|
+
const metaPath = (0, path_1.join)(tracePath, 'meta.json');
|
|
65
|
+
const eventsPath = (0, path_1.join)(tracePath, 'events.jsonl');
|
|
66
|
+
// Read metadata
|
|
67
|
+
const meta = await readTraceMeta(metaPath);
|
|
68
|
+
// Read events
|
|
69
|
+
const events = await readTraceEvents(eventsPath);
|
|
70
|
+
if (events.length === 0) {
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
traceId,
|
|
74
|
+
error: 'No events found in trace',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Extract startedAt from metadata (convert ISO string to timestamp)
|
|
78
|
+
const startedAt = meta.startedAt
|
|
79
|
+
? new Date(meta.startedAt).getTime()
|
|
80
|
+
: Date.now();
|
|
81
|
+
// Build batch payload
|
|
82
|
+
const batch = {
|
|
83
|
+
metadata: {
|
|
84
|
+
traceId,
|
|
85
|
+
serviceName,
|
|
86
|
+
serviceVersion: serviceVersion || meta.serviceVersion || '1.0.0',
|
|
87
|
+
environment: environment || meta.environment || 'production',
|
|
88
|
+
startedAt,
|
|
89
|
+
correlationIds: meta.correlationIds || [],
|
|
90
|
+
},
|
|
91
|
+
events: events.map(event => {
|
|
92
|
+
// Ensure all events have required base envelope fields
|
|
93
|
+
return {
|
|
94
|
+
...event,
|
|
95
|
+
traceId: event.traceId || traceId,
|
|
96
|
+
serviceName: event.serviceName || serviceName,
|
|
97
|
+
parentSpanId: event.parentSpanId ?? null,
|
|
98
|
+
};
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
// Upload to ingestion endpoint
|
|
102
|
+
const response = await fetch(uploadUrl, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: {
|
|
105
|
+
'Content-Type': 'application/json',
|
|
106
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
107
|
+
},
|
|
108
|
+
body: JSON.stringify(batch),
|
|
109
|
+
});
|
|
110
|
+
if (!response.ok) {
|
|
111
|
+
const errorText = await response.text();
|
|
112
|
+
return {
|
|
113
|
+
success: false,
|
|
114
|
+
traceId,
|
|
115
|
+
error: `Upload failed: ${response.status} ${response.statusText} - ${errorText}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const result = await response.json();
|
|
119
|
+
return {
|
|
120
|
+
success: true,
|
|
121
|
+
traceId: result.traceId || traceId,
|
|
122
|
+
blobRoot: result.blobRoot,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
return {
|
|
127
|
+
success: false,
|
|
128
|
+
traceId,
|
|
129
|
+
error: error.message || String(error),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@runtime-digital-twin/sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "SDK for capturing runtime behavior - automatic incident response and debugging",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./src/*": "./src/*",
|
|
13
|
+
"./dist/*": "./dist/*"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"test": "jest",
|
|
19
|
+
"test:watch": "jest --watch",
|
|
20
|
+
"prepublishOnly": "pnpm build"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@runtime-digital-twin/core": "^1.0.0",
|
|
24
|
+
"fastify": "^4.24.0",
|
|
25
|
+
"fastify-plugin": "^4.5.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@jest/globals": "^29.7.0",
|
|
29
|
+
"@types/jest": "^29.5.0",
|
|
30
|
+
"@types/node": "^20.0.0",
|
|
31
|
+
"jest": "^29.7.0",
|
|
32
|
+
"ts-jest": "^29.1.0",
|
|
33
|
+
"typescript": "^5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/your-org/WraithOnCallEngineer.git",
|
|
42
|
+
"directory": "packages/sdk"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"runtime",
|
|
46
|
+
"digital-twin",
|
|
47
|
+
"tracing",
|
|
48
|
+
"replay",
|
|
49
|
+
"debugging",
|
|
50
|
+
"fastify",
|
|
51
|
+
"instrumentation",
|
|
52
|
+
"observability"
|
|
53
|
+
],
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18.0.0"
|
|
56
|
+
},
|
|
57
|
+
"files": [
|
|
58
|
+
"dist",
|
|
59
|
+
"README.md",
|
|
60
|
+
"LICENSE"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
|