@runtime-digital-twin/sdk 1.0.2 → 1.0.3
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 +11 -0
- package/dist/fastify-plugin.d.ts +4 -0
- package/dist/fastify-plugin.d.ts.map +1 -1
- package/dist/fastify-plugin.js +85 -16
- package/dist/trace-uploader.d.ts +2 -1
- package/dist/trace-uploader.d.ts.map +1 -1
- package/dist/trace-uploader.js +85 -22
- package/package.json +17 -19
package/README.md
CHANGED
|
@@ -91,6 +91,8 @@ await fastify.register(fastifyTracing, {
|
|
|
91
91
|
headerAllowlist: [], // Optional: Headers to capture
|
|
92
92
|
maxBodySize: 10 * 1024 * 1024, // Optional: Max body size to capture (default: 10MB)
|
|
93
93
|
enabled: true, // Optional: Enable/disable tracing (default: true)
|
|
94
|
+
traceSampleRate: 1, // Optional: 0–1; 1 = always, 0.1 = 10% of requests (default: 1)
|
|
95
|
+
alwaysTraceErrors: true, // Optional: always capture trace for requests that error (default: true)
|
|
94
96
|
// Upload options (optional)
|
|
95
97
|
uploadUrl: process.env.WRAITH_API_URL + '/api/traces/ingest',
|
|
96
98
|
apiKey: process.env.WRAITH_API_KEY,
|
|
@@ -136,6 +138,15 @@ WRAITH_API_KEY=your-api-key-here
|
|
|
136
138
|
|
|
137
139
|
Traces are uploaded asynchronously after each request completes, so they don't slow down your application.
|
|
138
140
|
|
|
141
|
+
### Sampling and errors
|
|
142
|
+
|
|
143
|
+
- **traceSampleRate** (0–1): When set below 1, only that fraction of requests are traced (e.g. 0.1 = 10%). Reduces volume and cost while keeping a representative sample.
|
|
144
|
+
- **alwaysTraceErrors**: When true (default), any request that ends in an error (Fastify `onError`) is traced and uploaded even if it was not sampled. Ensures failures are never missed.
|
|
145
|
+
|
|
146
|
+
### Rate limits and 429
|
|
147
|
+
|
|
148
|
+
If the ingest endpoint returns **429 Too Many Requests**, the uploader retries with exponential backoff (same as for 5xx). Use the **Retry-After** response header when present; the SDK respects backoff between attempts. To avoid hitting limits, reduce volume with **traceSampleRate** or increase **TRACE_INGEST_RATE_LIMIT_REQUESTS_PER_MIN** on the server.
|
|
149
|
+
|
|
139
150
|
## Modes
|
|
140
151
|
|
|
141
152
|
### Record Mode
|
package/dist/fastify-plugin.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export interface FastifyTracingOptions {
|
|
|
8
8
|
headerAllowlist?: string[];
|
|
9
9
|
maxBodySize?: number;
|
|
10
10
|
enabled?: boolean;
|
|
11
|
+
/** Sample rate 0–1. 1 = always trace, 0 = never, 0.1 = 10% of requests. Default 1. */
|
|
12
|
+
traceSampleRate?: number;
|
|
13
|
+
/** When true, always capture a trace for requests that end in error (onError), even if not sampled. Default true. */
|
|
14
|
+
alwaysTraceErrors?: boolean;
|
|
11
15
|
uploadUrl?: string;
|
|
12
16
|
apiKey?: string;
|
|
13
17
|
uploadOnComplete?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fastify-plugin.d.ts","sourceRoot":"","sources":["../src/fastify-plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fastify-plugin.d.ts","sourceRoot":"","sources":["../src/fastify-plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAgC,MAAM,SAAS,CAAC;AAE3E,OAAO,EAKL,WAAW,EACZ,MAAM,uBAAuB,CAAC;AAY/B,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sFAAsF;IACtF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qHAAqH;IACrH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,WAAW,CAAC,EAAE,WAAW,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACtC;CACF;AAID;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,kBAAkB,CAAC,qBAAqB,CAyVpE,CAAC;;AAGF,wBAGG"}
|
package/dist/fastify-plugin.js
CHANGED
|
@@ -4,6 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.fastifyTracing = void 0;
|
|
7
|
+
const promises_1 = require("fs/promises");
|
|
8
|
+
const path_1 = require("path");
|
|
7
9
|
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
8
10
|
const trace_bundle_writer_1 = require("./trace-bundle-writer");
|
|
9
11
|
const http_wrapper_1 = require("./http-wrapper");
|
|
@@ -15,7 +17,7 @@ const DEFAULT_MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB
|
|
|
15
17
|
* Fastify plugin for capturing HTTP request/response traces
|
|
16
18
|
*/
|
|
17
19
|
const fastifyTracing = async (fastify, options) => {
|
|
18
|
-
const { serviceName, serviceVersion, environment, traceDir, headerAllowlist, maxBodySize = DEFAULT_MAX_BODY_SIZE, enabled = true, uploadUrl, apiKey, uploadOnComplete = true, } = options;
|
|
20
|
+
const { serviceName, serviceVersion, environment, traceDir, headerAllowlist, maxBodySize = DEFAULT_MAX_BODY_SIZE, enabled = true, traceSampleRate = 1, alwaysTraceErrors = true, uploadUrl, apiKey, uploadOnComplete = true, } = options;
|
|
19
21
|
if (!enabled) {
|
|
20
22
|
fastify.log.info("[SDK] Tracing is disabled");
|
|
21
23
|
return;
|
|
@@ -27,30 +29,36 @@ const fastifyTracing = async (fastify, options) => {
|
|
|
27
29
|
// Hook: onRequest - initialize trace but don't write request event yet (body not available)
|
|
28
30
|
fastify.addHook("onRequest", async (request, reply) => {
|
|
29
31
|
try {
|
|
32
|
+
const sampledIn = traceSampleRate >= 1 || (traceSampleRate > 0 && Math.random() < traceSampleRate);
|
|
33
|
+
request.traceSampledIn = sampledIn;
|
|
34
|
+
if (!sampledIn) {
|
|
35
|
+
request.traceBundle = null;
|
|
36
|
+
request.traceSpanId = null;
|
|
37
|
+
request.traceStartTime = null;
|
|
38
|
+
request.traceRequestBodyHash = null;
|
|
39
|
+
request.traceIncomingParentSpanId = null;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
30
42
|
// Check for trace context propagation headers
|
|
31
43
|
const incomingTraceId = request.headers['x-wraith-trace-id'];
|
|
32
44
|
const incomingParentSpanId = request.headers['x-wraith-parent-span-id'];
|
|
33
45
|
// Create trace bundle for this request
|
|
34
|
-
// Use incoming traceId if present (cross-service propagation), otherwise generate new one
|
|
35
46
|
const traceBundle = await (0, trace_bundle_writer_1.createTrace)({
|
|
36
47
|
serviceName,
|
|
37
48
|
serviceVersion,
|
|
38
49
|
environment,
|
|
39
50
|
traceDir,
|
|
40
|
-
traceId: incomingTraceId,
|
|
51
|
+
traceId: incomingTraceId,
|
|
41
52
|
});
|
|
42
53
|
const spanId = (0, trace_bundle_writer_1.generateSpanId)();
|
|
43
54
|
const startTime = Date.now();
|
|
44
|
-
// Store on request for later use
|
|
45
55
|
request.traceBundle = traceBundle;
|
|
46
56
|
request.traceSpanId = spanId;
|
|
47
57
|
request.traceStartTime = startTime;
|
|
48
|
-
request.traceRequestBodyHash = null;
|
|
49
|
-
// Store incoming parent span ID for use in preHandler
|
|
58
|
+
request.traceRequestBodyHash = null;
|
|
50
59
|
request.traceIncomingParentSpanId = incomingParentSpanId || null;
|
|
51
60
|
}
|
|
52
61
|
catch (error) {
|
|
53
|
-
// Fail-open: log error but don't crash
|
|
54
62
|
fastify.log.error(`[SDK] Failed to initialize trace: ${error}`);
|
|
55
63
|
}
|
|
56
64
|
});
|
|
@@ -179,8 +187,8 @@ const fastifyTracing = async (fastify, options) => {
|
|
|
179
187
|
});
|
|
180
188
|
// Auto-upload trace if configured
|
|
181
189
|
if (uploadUrl && apiKey && uploadOnComplete !== false) {
|
|
182
|
-
// Upload asynchronously (don't block response)
|
|
183
190
|
const actualTraceDir = traceDir || './traces';
|
|
191
|
+
const tracePath = (0, path_1.join)(actualTraceDir, traceBundle.traceId);
|
|
184
192
|
(0, trace_uploader_1.uploadTrace)({
|
|
185
193
|
uploadUrl,
|
|
186
194
|
apiKey,
|
|
@@ -189,15 +197,22 @@ const fastifyTracing = async (fastify, options) => {
|
|
|
189
197
|
serviceName,
|
|
190
198
|
serviceVersion,
|
|
191
199
|
environment,
|
|
192
|
-
}).then(result => {
|
|
200
|
+
}).then(async (result) => {
|
|
193
201
|
if (result.success) {
|
|
194
|
-
fastify.log.info(
|
|
202
|
+
fastify.log.info({ traceId: result.traceId }, '[SDK] Trace uploaded');
|
|
203
|
+
// Delete local trace dir after successful upload to avoid re-upload and unbounded disk growth
|
|
204
|
+
try {
|
|
205
|
+
await (0, promises_1.rm)(tracePath, { recursive: true, force: true });
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
fastify.log.debug({ traceId: traceBundle.traceId, err: err?.message }, '[SDK] Could not delete trace dir after upload');
|
|
209
|
+
}
|
|
195
210
|
}
|
|
196
211
|
else {
|
|
197
|
-
fastify.log.warn(
|
|
212
|
+
fastify.log.warn({ traceId: traceBundle.traceId, error: result.error }, '[SDK] Trace upload failed');
|
|
198
213
|
}
|
|
199
214
|
}).catch(error => {
|
|
200
|
-
fastify.log.error(
|
|
215
|
+
fastify.log.error({ traceId: traceBundle.traceId, error: error?.message }, '[SDK] Trace upload error');
|
|
201
216
|
});
|
|
202
217
|
}
|
|
203
218
|
// Clear trace context
|
|
@@ -210,13 +225,67 @@ const fastifyTracing = async (fastify, options) => {
|
|
|
210
225
|
(0, http_wrapper_1.setTraceContext)(null, null);
|
|
211
226
|
}
|
|
212
227
|
});
|
|
213
|
-
// Hook: onError - capture errors
|
|
228
|
+
// Hook: onError - capture errors (always capture if alwaysTraceErrors and no bundle yet)
|
|
214
229
|
fastify.addHook("onError", async (request, reply, error) => {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
230
|
+
let traceBundle = request.traceBundle;
|
|
231
|
+
let spanId = request.traceSpanId;
|
|
232
|
+
const wasSampledOut = !request.traceSampledIn;
|
|
233
|
+
if (wasSampledOut && alwaysTraceErrors) {
|
|
234
|
+
try {
|
|
235
|
+
const bundle = await (0, trace_bundle_writer_1.createTrace)({
|
|
236
|
+
serviceName,
|
|
237
|
+
serviceVersion,
|
|
238
|
+
environment,
|
|
239
|
+
traceDir,
|
|
240
|
+
});
|
|
241
|
+
const sid = (0, trace_bundle_writer_1.generateSpanId)();
|
|
242
|
+
traceBundle = bundle;
|
|
243
|
+
spanId = sid;
|
|
244
|
+
await bundle.writeEvent({
|
|
245
|
+
type: "error",
|
|
246
|
+
timestamp: Date.now(),
|
|
247
|
+
spanId: sid,
|
|
248
|
+
parentSpanId: null,
|
|
249
|
+
error: {
|
|
250
|
+
name: error.name || "Error",
|
|
251
|
+
message: error.message || String(error),
|
|
252
|
+
stack: error.stack || undefined,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
await bundle.complete({
|
|
256
|
+
method: 'ERROR',
|
|
257
|
+
path: '/error',
|
|
258
|
+
headers: {},
|
|
259
|
+
});
|
|
260
|
+
if (uploadUrl && apiKey) {
|
|
261
|
+
const actualTraceDir = traceDir || './traces';
|
|
262
|
+
const tracePath = (0, path_1.join)(actualTraceDir, bundle.traceId);
|
|
263
|
+
(0, trace_uploader_1.uploadTrace)({
|
|
264
|
+
uploadUrl,
|
|
265
|
+
apiKey,
|
|
266
|
+
traceDir: actualTraceDir,
|
|
267
|
+
traceId: bundle.traceId,
|
|
268
|
+
serviceName,
|
|
269
|
+
serviceVersion,
|
|
270
|
+
environment,
|
|
271
|
+
}).then(async (result) => {
|
|
272
|
+
if (result.success) {
|
|
273
|
+
try {
|
|
274
|
+
// Delete local trace dir after successful upload to prevent re-upload and unbounded disk growth
|
|
275
|
+
await (0, promises_1.rm)(tracePath, { recursive: true, force: true });
|
|
276
|
+
}
|
|
277
|
+
catch (_) { }
|
|
278
|
+
}
|
|
279
|
+
}).catch(() => { });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
fastify.log.error(`[SDK] Failed to capture error trace: ${err}`);
|
|
284
|
+
}
|
|
218
285
|
return;
|
|
219
286
|
}
|
|
287
|
+
if (!traceBundle || !spanId)
|
|
288
|
+
return;
|
|
220
289
|
try {
|
|
221
290
|
await traceBundle.writeEvent({
|
|
222
291
|
type: "error",
|
package/dist/trace-uploader.d.ts
CHANGED
|
@@ -19,7 +19,8 @@ export interface TraceUploadResult {
|
|
|
19
19
|
error?: string;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Upload trace to ingestion endpoint
|
|
22
|
+
* Upload trace to ingestion endpoint with retries (5xx, 429, network errors).
|
|
23
|
+
* Exponential backoff: 1s, 2s, 4s (capped at 8s).
|
|
23
24
|
*/
|
|
24
25
|
export declare function uploadTrace(options: TraceUploadOptions): Promise<TraceUploadResult>;
|
|
25
26
|
//# sourceMappingURL=trace-uploader.d.ts.map
|
|
@@ -1 +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;
|
|
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;AAmED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAwI5B"}
|
package/dist/trace-uploader.js
CHANGED
|
@@ -53,8 +53,26 @@ async function readTraceMeta(metaPath) {
|
|
|
53
53
|
throw new Error(`Failed to read meta.json: ${error.message}`);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
const MAX_UPLOAD_ATTEMPTS = 3;
|
|
57
|
+
const INITIAL_BACKOFF_MS = 1000;
|
|
58
|
+
const MAX_BACKOFF_MS = 8000;
|
|
59
|
+
function isRetryableStatus(status) {
|
|
60
|
+
return status >= 500 || status === 429;
|
|
61
|
+
}
|
|
62
|
+
function sleep(ms) {
|
|
63
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
64
|
+
}
|
|
65
|
+
function logUpload(event) {
|
|
66
|
+
try {
|
|
67
|
+
console.warn('[SDK]', JSON.stringify({ event: 'trace_upload', ...event }));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
console.warn('[SDK] trace_upload', event);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
56
73
|
/**
|
|
57
|
-
* Upload trace to ingestion endpoint
|
|
74
|
+
* Upload trace to ingestion endpoint with retries (5xx, 429, network errors).
|
|
75
|
+
* Exponential backoff: 1s, 2s, 4s (capped at 8s).
|
|
58
76
|
*/
|
|
59
77
|
async function uploadTrace(options) {
|
|
60
78
|
const { uploadUrl, apiKey, traceDir, traceId, serviceName, serviceVersion, environment } = options;
|
|
@@ -89,7 +107,6 @@ async function uploadTrace(options) {
|
|
|
89
107
|
correlationIds: meta.correlationIds || [],
|
|
90
108
|
},
|
|
91
109
|
events: events.map(event => {
|
|
92
|
-
// Ensure all events have required base envelope fields
|
|
93
110
|
return {
|
|
94
111
|
...event,
|
|
95
112
|
traceId: event.traceId || traceId,
|
|
@@ -98,31 +115,77 @@ async function uploadTrace(options) {
|
|
|
98
115
|
};
|
|
99
116
|
}),
|
|
100
117
|
};
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
let lastError;
|
|
119
|
+
let lastStatus;
|
|
120
|
+
for (let attempt = 1; attempt <= MAX_UPLOAD_ATTEMPTS; attempt++) {
|
|
121
|
+
logUpload({ traceId, attempt });
|
|
122
|
+
try {
|
|
123
|
+
const response = await fetch(uploadUrl, {
|
|
124
|
+
method: 'POST',
|
|
125
|
+
headers: {
|
|
126
|
+
'Content-Type': 'application/json',
|
|
127
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
128
|
+
},
|
|
129
|
+
body: JSON.stringify(batch),
|
|
130
|
+
});
|
|
131
|
+
lastStatus = response.status;
|
|
132
|
+
logUpload({ traceId, attempt, statusCode: response.status });
|
|
133
|
+
if (response.ok) {
|
|
134
|
+
const result = await response.json();
|
|
135
|
+
logUpload({ traceId, attempt, statusCode: response.status, success: true });
|
|
136
|
+
return {
|
|
137
|
+
success: true,
|
|
138
|
+
traceId: result.traceId || traceId,
|
|
139
|
+
blobRoot: result.blobRoot,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const errorText = await response.text();
|
|
143
|
+
lastError = `${response.status} ${response.statusText} - ${errorText.substring(0, 200)}`;
|
|
144
|
+
if (!isRetryableStatus(response.status) || attempt === MAX_UPLOAD_ATTEMPTS) {
|
|
145
|
+
logUpload({ traceId, attempt, statusCode: response.status, success: false, error: lastError?.substring(0, 200) });
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
traceId,
|
|
149
|
+
error: lastError,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
let backoffMs = Math.min(INITIAL_BACKOFF_MS * Math.pow(2, attempt - 1), MAX_BACKOFF_MS);
|
|
153
|
+
if (response.status === 429) {
|
|
154
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
155
|
+
if (retryAfter != null) {
|
|
156
|
+
const retryAfterMs = /^\d+$/.test(retryAfter.trim())
|
|
157
|
+
? parseInt(retryAfter.trim(), 10) * 1000
|
|
158
|
+
: 0;
|
|
159
|
+
if (retryAfterMs > 0) {
|
|
160
|
+
backoffMs = Math.min(backoffMs, retryAfterMs);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
await sleep(backoffMs);
|
|
165
|
+
}
|
|
166
|
+
catch (networkError) {
|
|
167
|
+
lastError = networkError.message || String(networkError);
|
|
168
|
+
logUpload({ traceId, attempt, success: false, error: lastError?.substring(0, 200) });
|
|
169
|
+
if (attempt === MAX_UPLOAD_ATTEMPTS) {
|
|
170
|
+
return {
|
|
171
|
+
success: false,
|
|
172
|
+
traceId,
|
|
173
|
+
error: lastError,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const backoffMs = Math.min(INITIAL_BACKOFF_MS * Math.pow(2, attempt - 1), MAX_BACKOFF_MS);
|
|
177
|
+
await sleep(backoffMs);
|
|
178
|
+
}
|
|
117
179
|
}
|
|
118
|
-
|
|
180
|
+
logUpload({ traceId, attempt: MAX_UPLOAD_ATTEMPTS, statusCode: lastStatus, success: false, error: lastError?.substring(0, 200) });
|
|
119
181
|
return {
|
|
120
|
-
success:
|
|
121
|
-
traceId
|
|
122
|
-
|
|
182
|
+
success: false,
|
|
183
|
+
traceId,
|
|
184
|
+
error: lastError || 'Upload failed after retries',
|
|
123
185
|
};
|
|
124
186
|
}
|
|
125
187
|
catch (error) {
|
|
188
|
+
logUpload({ traceId, success: false, error: error.message?.substring(0, 200) });
|
|
126
189
|
return {
|
|
127
190
|
success: false,
|
|
128
191
|
traceId,
|
package/package.json
CHANGED
|
@@ -1,35 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtime-digital-twin/sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "SDK for capturing runtime behavior - automatic incident response and debugging with enhanced autofix support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
|
-
"default": "./
|
|
10
|
+
"default": "./src/index.ts"
|
|
11
11
|
},
|
|
12
12
|
"./src/*": "./src/*",
|
|
13
13
|
"./dist/*": "./dist/*"
|
|
14
14
|
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "tsc",
|
|
17
|
-
"dev": "tsc --watch",
|
|
18
|
-
"test": "jest",
|
|
19
|
-
"test:watch": "jest --watch",
|
|
20
|
-
"prepublishOnly": "pnpm build"
|
|
21
|
-
},
|
|
22
15
|
"dependencies": {
|
|
23
|
-
"@runtime-digital-twin/core": "^1.0.0",
|
|
24
16
|
"fastify": "^4.24.0",
|
|
25
|
-
"fastify-plugin": "^4.5.0"
|
|
17
|
+
"fastify-plugin": "^4.5.0",
|
|
18
|
+
"@runtime-digital-twin/core": "1.0.1"
|
|
26
19
|
},
|
|
27
20
|
"devDependencies": {
|
|
28
|
-
"@jest/globals": "^
|
|
29
|
-
"@types/jest": "^
|
|
21
|
+
"@jest/globals": "^30.2.0",
|
|
22
|
+
"@types/jest": "^30.0.0",
|
|
30
23
|
"@types/node": "^20.0.0",
|
|
31
|
-
"jest": "^
|
|
32
|
-
"ts-jest": "^29.
|
|
24
|
+
"jest": "^30.2.0",
|
|
25
|
+
"ts-jest": "^29.4.6",
|
|
33
26
|
"typescript": "^5.0.0"
|
|
34
27
|
},
|
|
35
28
|
"license": "MIT",
|
|
@@ -38,7 +31,7 @@
|
|
|
38
31
|
},
|
|
39
32
|
"repository": {
|
|
40
33
|
"type": "git",
|
|
41
|
-
"url": "git+https://github.com/
|
|
34
|
+
"url": "git+https://github.com/usewraith/wraith.git",
|
|
42
35
|
"directory": "packages/sdk"
|
|
43
36
|
},
|
|
44
37
|
"keywords": [
|
|
@@ -58,6 +51,11 @@
|
|
|
58
51
|
"dist",
|
|
59
52
|
"README.md",
|
|
60
53
|
"LICENSE"
|
|
61
|
-
]
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
],
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsc",
|
|
57
|
+
"dev": "tsc --watch",
|
|
58
|
+
"test": "jest",
|
|
59
|
+
"test:watch": "jest --watch"
|
|
60
|
+
}
|
|
61
|
+
}
|