@rasputin-ai/core 0.7.2-beta.1 → 0.8.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 +3 -3
- package/dist/create-client/create-client-types.d.ts +8 -0
- package/dist/create-client/create-client-types.d.ts.map +1 -1
- package/dist/create-client/create-client.d.ts.map +1 -1
- package/dist/create-ingest-event/create-ingest-event-types.d.ts +2 -0
- package/dist/create-ingest-event/create-ingest-event-types.d.ts.map +1 -1
- package/dist/derivation/redact-env-secret-values.d.ts.map +1 -1
- package/dist/derivation/sensitive-property-name.d.ts.map +1 -1
- package/dist/index.js +360 -280
- package/dist/sdk-meta.d.ts +1 -1
- package/dist/sdk-meta.d.ts.map +1 -1
- package/dist/suppression/compute-burst-key.d.ts +1 -1
- package/dist/suppression/compute-burst-key.d.ts.map +1 -1
- package/dist/suppression/create-suppression.d.ts +3 -4
- package/dist/suppression/create-suppression.d.ts.map +1 -1
- package/dist/suppression/grouping-ref-cache.d.ts +6 -0
- package/dist/suppression/grouping-ref-cache.d.ts.map +1 -0
- package/dist/suppression/parameterize-message.d.ts +2 -4
- package/dist/suppression/parameterize-message.d.ts.map +1 -1
- package/dist/suppression/suppression-types.d.ts +6 -0
- package/dist/suppression/suppression-types.d.ts.map +1 -1
- package/dist/transport/create-transport.d.ts.map +1 -1
- package/dist/transport/default-ingest-request-timeout.d.ts +2 -0
- package/dist/transport/default-ingest-request-timeout.d.ts.map +1 -0
- package/dist/transport/node-http-fetch.d.ts +6 -3
- package/dist/transport/node-http-fetch.d.ts.map +1 -1
- package/dist/transport/send-batch.d.ts +6 -4
- package/dist/transport/send-batch.d.ts.map +1 -1
- package/dist/transport/transport-types.d.ts +13 -3
- package/dist/transport/transport-types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/suppression/identity-needs-message-template.d.ts +0 -8
- package/dist/suppression/identity-needs-message-template.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// src/transport/node-http-fetch.ts
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
2
|
+
import * as net from "node:net";
|
|
3
|
+
import * as tls from "node:tls";
|
|
4
|
+
var CONNECT_TIMEOUT_MS = 2e3;
|
|
5
|
+
var abortError = () => Object.assign(new Error("aborted"), { name: "AbortError" });
|
|
6
|
+
var connectTimeoutError = () => Object.assign(new Error("connect timeout"), { name: "ConnectTimeoutError" });
|
|
4
7
|
var headerRecord = (headers) => {
|
|
5
8
|
const view = new Headers(headers);
|
|
6
9
|
const record = {};
|
|
@@ -9,26 +12,84 @@ var headerRecord = (headers) => {
|
|
|
9
12
|
});
|
|
10
13
|
return record;
|
|
11
14
|
};
|
|
12
|
-
var
|
|
13
|
-
if (body == null)
|
|
14
|
-
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
if (typeof body === "string") {
|
|
18
|
-
req.end(body);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
15
|
+
var toBuffer = (body) => {
|
|
16
|
+
if (body == null) return Buffer.alloc(0);
|
|
17
|
+
if (typeof body === "string") return Buffer.from(body);
|
|
21
18
|
if (ArrayBuffer.isView(body)) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
return Buffer.from(body.buffer, body.byteOffset, body.byteLength);
|
|
20
|
+
}
|
|
21
|
+
if (body instanceof ArrayBuffer) return Buffer.from(body);
|
|
22
|
+
return Buffer.alloc(0);
|
|
23
|
+
};
|
|
24
|
+
var encodeHttpRequest = (url, init) => {
|
|
25
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
26
|
+
const path = `${url.pathname}${url.search}` || "/";
|
|
27
|
+
const headers = headerRecord(init?.headers);
|
|
28
|
+
const body = toBuffer(init?.body ?? null);
|
|
29
|
+
if (body.length > 0 && headers["content-length"] === void 0) {
|
|
30
|
+
headers["content-length"] = String(body.length);
|
|
31
|
+
}
|
|
32
|
+
if (headers.host === void 0) headers.host = url.host;
|
|
33
|
+
if (headers.connection === void 0) headers.connection = "close";
|
|
34
|
+
const head = [`${method} ${path} HTTP/1.1`];
|
|
35
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
36
|
+
head.push(`${key}: ${value}`);
|
|
37
|
+
}
|
|
38
|
+
return Buffer.concat([Buffer.from(`${head.join("\r\n")}\r
|
|
39
|
+
\r
|
|
40
|
+
`), body]);
|
|
41
|
+
};
|
|
42
|
+
var tryDecodeChunked = (raw) => {
|
|
43
|
+
const parts = [];
|
|
44
|
+
let offset = 0;
|
|
45
|
+
for (; ; ) {
|
|
46
|
+
const lineEnd = raw.indexOf("\r\n", offset);
|
|
47
|
+
if (lineEnd < 0) return null;
|
|
48
|
+
const size = Number.parseInt(raw.subarray(offset, lineEnd).toString("ascii"), 16);
|
|
49
|
+
if (!Number.isFinite(size) || size < 0) throw new Error("invalid chunk size");
|
|
50
|
+
const dataStart = lineEnd + 2;
|
|
51
|
+
if (size === 0) return raw.length < dataStart + 2 ? null : Buffer.concat(parts);
|
|
52
|
+
const dataEnd = dataStart + size;
|
|
53
|
+
if (raw.length < dataEnd + 2) return null;
|
|
54
|
+
parts.push(raw.subarray(dataStart, dataEnd));
|
|
55
|
+
offset = dataEnd + 2;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var toResponseBody = (buf) => {
|
|
59
|
+
const copy = new Uint8Array(buf.byteLength);
|
|
60
|
+
copy.set(buf);
|
|
61
|
+
return copy;
|
|
62
|
+
};
|
|
63
|
+
var tryParseHttpResponse = (raw, connectionClosed) => {
|
|
64
|
+
const split = raw.indexOf("\r\n\r\n");
|
|
65
|
+
if (split < 0) return null;
|
|
66
|
+
const lines = raw.subarray(0, split).toString("latin1").split("\r\n");
|
|
67
|
+
const status = /^HTTP\/\d\.\d (\d{3})/.exec(lines[0] ?? "");
|
|
68
|
+
if (!status) throw new Error("invalid HTTP response");
|
|
69
|
+
const headers = new Headers();
|
|
70
|
+
for (const line of lines.slice(1)) {
|
|
71
|
+
const colon = line.indexOf(":");
|
|
72
|
+
if (colon < 0) continue;
|
|
73
|
+
headers.append(line.slice(0, colon).trim(), line.slice(colon + 1).trim());
|
|
74
|
+
}
|
|
75
|
+
const body = raw.subarray(split + 4);
|
|
76
|
+
if (headers.get("transfer-encoding")?.toLowerCase().includes("chunked")) {
|
|
77
|
+
const decoded = tryDecodeChunked(body);
|
|
78
|
+
return decoded === null ? null : new Response(toResponseBody(decoded), { status: Number(status[1]), headers });
|
|
79
|
+
}
|
|
80
|
+
const lengthHeader = headers.get("content-length");
|
|
81
|
+
if (lengthHeader !== null) {
|
|
82
|
+
const length = Number(lengthHeader);
|
|
83
|
+
if (!Number.isInteger(length) || length < 0) throw new Error("invalid content-length");
|
|
84
|
+
if (body.length < length) return null;
|
|
85
|
+
return new Response(toResponseBody(body.subarray(0, length)), {
|
|
86
|
+
status: Number(status[1]),
|
|
87
|
+
headers
|
|
88
|
+
});
|
|
28
89
|
}
|
|
29
|
-
|
|
90
|
+
return connectionClosed ? new Response(toResponseBody(body), { status: Number(status[1]), headers }) : null;
|
|
30
91
|
};
|
|
31
|
-
var
|
|
92
|
+
var defaultIngestFetch = () => (input, init) => new Promise((resolve4, reject) => {
|
|
32
93
|
let parsed;
|
|
33
94
|
try {
|
|
34
95
|
parsed = new URL(String(input));
|
|
@@ -36,62 +97,77 @@ var createNodeHttpFetch = (unrefSockets = false) => (input, init) => new Promise
|
|
|
36
97
|
reject(error);
|
|
37
98
|
return;
|
|
38
99
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (Array.isArray(value)) {
|
|
60
|
-
for (const item of value) headers.append(key, item);
|
|
61
|
-
} else {
|
|
62
|
-
headers.set(key, value);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
resolve4(
|
|
66
|
-
new Response(Buffer.concat(chunks), {
|
|
67
|
-
status: response.statusCode ?? 0,
|
|
68
|
-
headers
|
|
69
|
-
})
|
|
70
|
-
);
|
|
71
|
-
});
|
|
100
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
101
|
+
reject(new Error(`unsupported protocol: ${parsed.protocol}`));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (init?.signal?.aborted) {
|
|
105
|
+
reject(abortError());
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const port = Number(parsed.port) || (parsed.protocol === "https:" ? 443 : 80);
|
|
109
|
+
const socket = parsed.protocol === "https:" ? tls.connect({ host: parsed.hostname, port, servername: parsed.hostname }) : net.connect({ host: parsed.hostname, port });
|
|
110
|
+
socket.unref();
|
|
111
|
+
let settled = false;
|
|
112
|
+
let sent = false;
|
|
113
|
+
let connectTimer;
|
|
114
|
+
const finish = (error, response) => {
|
|
115
|
+
if (settled) return;
|
|
116
|
+
settled = true;
|
|
117
|
+
if (connectTimer) {
|
|
118
|
+
clearTimeout(connectTimer);
|
|
119
|
+
connectTimer = void 0;
|
|
72
120
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
reject(Object.assign(new Error("aborted"), { name: "AbortError" }));
|
|
121
|
+
socket.destroy();
|
|
122
|
+
if (error) reject(error);
|
|
123
|
+
else if (response) resolve4(response);
|
|
77
124
|
};
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
125
|
+
connectTimer = setTimeout(() => {
|
|
126
|
+
if (sent || settled) return;
|
|
127
|
+
finish(connectTimeoutError());
|
|
128
|
+
}, CONNECT_TIMEOUT_MS);
|
|
129
|
+
const onConnected = () => {
|
|
130
|
+
if (settled || sent) return;
|
|
131
|
+
sent = true;
|
|
132
|
+
if (connectTimer) {
|
|
133
|
+
clearTimeout(connectTimer);
|
|
134
|
+
connectTimer = void 0;
|
|
82
135
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
136
|
+
try {
|
|
137
|
+
socket.write(encodeHttpRequest(parsed, init));
|
|
138
|
+
} catch (error) {
|
|
139
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
if (parsed.protocol === "https:") {
|
|
143
|
+
socket.once("secureConnect", onConnected);
|
|
144
|
+
} else {
|
|
145
|
+
socket.once("connect", onConnected);
|
|
146
|
+
if (socket.remoteAddress) onConnected();
|
|
147
|
+
}
|
|
148
|
+
init?.signal?.addEventListener("abort", () => finish(abortError()), { once: true });
|
|
149
|
+
if (init?.signal?.aborted) finish(abortError());
|
|
150
|
+
let buf = Buffer.alloc(0);
|
|
151
|
+
socket.on("data", (chunk) => {
|
|
152
|
+
buf = Buffer.concat([buf, chunk]);
|
|
153
|
+
try {
|
|
154
|
+
const response = tryParseHttpResponse(buf, false);
|
|
155
|
+
if (response) finish(null, response);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
socket.on("end", () => {
|
|
161
|
+
try {
|
|
162
|
+
const response = tryParseHttpResponse(buf, true);
|
|
163
|
+
if (response) finish(null, response);
|
|
164
|
+
else finish(new Error("incomplete HTTP response"));
|
|
165
|
+
} catch (error) {
|
|
166
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
socket.on("error", (error) => finish(error));
|
|
93
170
|
});
|
|
94
|
-
var defaultIngestFetch = (options) => createNodeHttpFetch(options?.unrefSockets === true);
|
|
95
171
|
|
|
96
172
|
// src/announce-deploy/announce-deploy.ts
|
|
97
173
|
var trimmedString = (value) => typeof value === "string" ? value.trim() : "";
|
|
@@ -370,13 +446,6 @@ var DEFAULT_SENSITIVE_PROPERTY_NAMES = [
|
|
|
370
446
|
"passwd",
|
|
371
447
|
"pwd",
|
|
372
448
|
"passphrase",
|
|
373
|
-
"token",
|
|
374
|
-
"accesstoken",
|
|
375
|
-
"refreshtoken",
|
|
376
|
-
"sessiontoken",
|
|
377
|
-
"authorization",
|
|
378
|
-
"auth",
|
|
379
|
-
"bearer",
|
|
380
449
|
"cookie",
|
|
381
450
|
"cookies",
|
|
382
451
|
"signedcookies",
|
|
@@ -384,77 +453,18 @@ var DEFAULT_SENSITIVE_PROPERTY_NAMES = [
|
|
|
384
453
|
"secret",
|
|
385
454
|
"secrets",
|
|
386
455
|
"clientsecret",
|
|
387
|
-
"
|
|
388
|
-
"
|
|
389
|
-
"
|
|
390
|
-
"credentials",
|
|
391
|
-
"privatekey",
|
|
392
|
-
"privkey",
|
|
393
|
-
"pem",
|
|
394
|
-
"jwt",
|
|
395
|
-
"otp",
|
|
396
|
-
"totp",
|
|
397
|
-
"pin",
|
|
398
|
-
"mfacode",
|
|
399
|
-
"sessionid",
|
|
400
|
-
"sid",
|
|
401
|
-
"databaseurl",
|
|
402
|
-
"connectionstring",
|
|
403
|
-
"dsn",
|
|
404
|
-
"mongouri",
|
|
405
|
-
"redisurl",
|
|
406
|
-
"signingkey",
|
|
407
|
-
"encryptionkey",
|
|
408
|
-
"cvv",
|
|
409
|
-
"cvc",
|
|
410
|
-
"cardnumber",
|
|
411
|
-
"ssn",
|
|
412
|
-
"creditcard",
|
|
413
|
-
"securitycode",
|
|
414
|
-
"twofactorcode",
|
|
415
|
-
"masterkey",
|
|
416
|
-
"servicerolekey",
|
|
417
|
-
"stripekey",
|
|
418
|
-
"rawheaders"
|
|
456
|
+
"authorization",
|
|
457
|
+
"proxyauthorization",
|
|
458
|
+
"bearer"
|
|
419
459
|
];
|
|
420
460
|
var DEFAULT_SENSITIVE_PROPERTY_NAME_SET = new Set(DEFAULT_SENSITIVE_PROPERTY_NAMES);
|
|
421
|
-
var
|
|
461
|
+
var SENSITIVE_SUFFIXES = [
|
|
422
462
|
"password",
|
|
423
463
|
"passwd",
|
|
424
|
-
"pwd",
|
|
425
464
|
"passphrase",
|
|
426
|
-
"
|
|
427
|
-
"secret",
|
|
465
|
+
"pwd",
|
|
428
466
|
"cookie",
|
|
429
|
-
"cookies"
|
|
430
|
-
"authorization",
|
|
431
|
-
"auth",
|
|
432
|
-
"bearer",
|
|
433
|
-
"credential",
|
|
434
|
-
"credentials",
|
|
435
|
-
"jwt",
|
|
436
|
-
"otp",
|
|
437
|
-
"totp",
|
|
438
|
-
"pin",
|
|
439
|
-
"sid",
|
|
440
|
-
"dsn",
|
|
441
|
-
"pem",
|
|
442
|
-
"privkey",
|
|
443
|
-
"cvv",
|
|
444
|
-
"cvc",
|
|
445
|
-
"ssn",
|
|
446
|
-
"secrets"
|
|
447
|
-
]);
|
|
448
|
-
var SENSITIVE_SUFFIXES = [
|
|
449
|
-
"password",
|
|
450
|
-
"passwd",
|
|
451
|
-
"token",
|
|
452
|
-
"secret",
|
|
453
|
-
"apikey",
|
|
454
|
-
"privatekey",
|
|
455
|
-
"credential",
|
|
456
|
-
"credentials",
|
|
457
|
-
"cookie"
|
|
467
|
+
"cookies"
|
|
458
468
|
];
|
|
459
469
|
var compactKey = (key) => {
|
|
460
470
|
let compacted = "";
|
|
@@ -474,34 +484,6 @@ var stripTrailingDigits = (value) => {
|
|
|
474
484
|
}
|
|
475
485
|
return end === 0 ? value : value.slice(0, end);
|
|
476
486
|
};
|
|
477
|
-
var segmentsOf = (key) => {
|
|
478
|
-
const segments = [];
|
|
479
|
-
let current = "";
|
|
480
|
-
for (let index = 0; index < key.length; index++) {
|
|
481
|
-
const char = key[index] ?? "";
|
|
482
|
-
const code = key.charCodeAt(index);
|
|
483
|
-
if (code === 45 || code === 46 || code === 95) {
|
|
484
|
-
if (current) segments.push(current.toLowerCase());
|
|
485
|
-
current = "";
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
if (current && code >= 65 && code <= 90) {
|
|
489
|
-
const prev = current.charCodeAt(current.length - 1);
|
|
490
|
-
const prevIsLower = prev >= 97 && prev <= 122;
|
|
491
|
-
const next = key.charCodeAt(index + 1);
|
|
492
|
-
const nextIsLower = next >= 97 && next <= 122;
|
|
493
|
-
if (prevIsLower || nextIsLower) {
|
|
494
|
-
segments.push(current.toLowerCase());
|
|
495
|
-
current = char;
|
|
496
|
-
continue;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
current += char;
|
|
500
|
-
}
|
|
501
|
-
if (current) segments.push(current.toLowerCase());
|
|
502
|
-
return segments;
|
|
503
|
-
};
|
|
504
|
-
var adjacentPairsSensitive = (left, right) => left === "api" && right === "key" || left === "private" && right === "key" || left === "client" && right === "secret" || left === "set" && right === "cookie" || left === "database" && right === "url" || left === "connection" && right === "string" || left === "mongo" && (right === "uri" || right === "url") || left === "mongodb" && (right === "uri" || right === "url") || left === "redis" && (right === "uri" || right === "url") || left === "postgres" && (right === "uri" || right === "url") || left === "database" && right === "uri" || left === "signing" && right === "key" || left === "master" && right === "key" || left === "stripe" && right === "key" || left === "service" && right === "role" || left === "security" && right === "code" || left === "two" && right === "factor" || left === "encryption" && right === "key" || left === "session" && (right === "id" || right === "key" || right === "token") || left === "card" && right === "number" || left === "mfa" && right === "code";
|
|
505
487
|
var defaultSensitivePropertyNames = () => DEFAULT_SENSITIVE_PROPERTY_NAME_SET;
|
|
506
488
|
var propertyNameIsSensitive = (key) => {
|
|
507
489
|
const compacted = compactKey(key);
|
|
@@ -513,16 +495,6 @@ var propertyNameIsSensitive = (key) => {
|
|
|
513
495
|
for (const suffix of SENSITIVE_SUFFIXES) {
|
|
514
496
|
if (form.endsWith(suffix)) return true;
|
|
515
497
|
}
|
|
516
|
-
if (form.includes("apikey") || form.includes("xapikey")) return true;
|
|
517
|
-
}
|
|
518
|
-
const segments = segmentsOf(key).map((segment) => stripTrailingDigits(segment) || segment);
|
|
519
|
-
for (const segment of segments) {
|
|
520
|
-
if (SENSITIVE_SEGMENTS.has(segment)) return true;
|
|
521
|
-
}
|
|
522
|
-
for (let index = 0; index < segments.length - 1; index++) {
|
|
523
|
-
const left = segments[index];
|
|
524
|
-
const right = segments[index + 1];
|
|
525
|
-
if (left && right && adjacentPairsSensitive(left, right)) return true;
|
|
526
498
|
}
|
|
527
499
|
return false;
|
|
528
500
|
};
|
|
@@ -544,7 +516,11 @@ var identityMatcher = {
|
|
|
544
516
|
redact: (text) => text,
|
|
545
517
|
needleCount: 0
|
|
546
518
|
};
|
|
547
|
-
var envKeyLooksSensitive = (key) =>
|
|
519
|
+
var envKeyLooksSensitive = (key) => {
|
|
520
|
+
if (propertyNameIsSensitive(key)) return true;
|
|
521
|
+
const compact = key.replace(/[-._]/g, "").toLowerCase();
|
|
522
|
+
return compact.includes("accesskey") || compact.includes("apikey") || compact.endsWith("secret") || compact.endsWith("token") || compact.includes("privatekey");
|
|
523
|
+
};
|
|
548
524
|
var isCollectableSecretValue = (value) => value.length >= MIN_ENV_SECRET_LENGTH && !COMMON_CONFIG_VALUES.has(value.toLowerCase());
|
|
549
525
|
var looksLikeUrl = (value) => {
|
|
550
526
|
const schemeEnd = value.indexOf("://");
|
|
@@ -657,6 +633,7 @@ var collectEnvSecretValues = (env = process.env) => {
|
|
|
657
633
|
if (userinfo.userinfo.length >= MIN_ENV_SECRET_LENGTH) {
|
|
658
634
|
addWithRepresentations(needles, userinfo.userinfo);
|
|
659
635
|
}
|
|
636
|
+
addWithRepresentations(needles, value);
|
|
660
637
|
}
|
|
661
638
|
const queryValues = querySecretValues(value);
|
|
662
639
|
for (let queryIndex = 0; queryIndex < queryValues.length; queryIndex++) {
|
|
@@ -1158,13 +1135,7 @@ var resolveSourceMaps = async (frames) => {
|
|
|
1158
1135
|
|
|
1159
1136
|
// src/sdk-meta.ts
|
|
1160
1137
|
var SDK_NAME = "@rasputin-ai/core";
|
|
1161
|
-
var SDK_VERSION = "0.
|
|
1162
|
-
|
|
1163
|
-
// src/suppression/identity-needs-message-template.ts
|
|
1164
|
-
var identityNeedsMessageTemplate = (type, code) => {
|
|
1165
|
-
if (code != null && code !== "") return false;
|
|
1166
|
-
return type === "Error";
|
|
1167
|
-
};
|
|
1138
|
+
var SDK_VERSION = "0.8.0";
|
|
1168
1139
|
|
|
1169
1140
|
// src/suppression/parameterize-message.ts
|
|
1170
1141
|
var MAX_INPUT_LENGTH = 8192;
|
|
@@ -1222,10 +1193,6 @@ var RULES = [
|
|
|
1222
1193
|
{
|
|
1223
1194
|
token: "duration",
|
|
1224
1195
|
pattern: /\b\d+(?:\.\d+)?\s?(?:ns|µs|us|ms|secs?|seconds?|mins?|minutes?|hrs?|hours?|days?)\b/giu
|
|
1225
|
-
},
|
|
1226
|
-
{
|
|
1227
|
-
token: "id",
|
|
1228
|
-
pattern: /\b(?=[a-z0-9]*[a-z])(?=[a-z0-9]*\d)[a-z0-9]{8,}\b/giu
|
|
1229
1196
|
}
|
|
1230
1197
|
];
|
|
1231
1198
|
var normalizeForIdentity = (normalized) => {
|
|
@@ -1286,19 +1253,22 @@ var frameBurstParts = (frames) => {
|
|
|
1286
1253
|
const inApp = collapsed.filter((frame) => frame.in_app);
|
|
1287
1254
|
const selected = (inApp.length > 0 ? inApp : collapsed).slice(0, FRAME_COUNT);
|
|
1288
1255
|
return selected.map((frame) => {
|
|
1289
|
-
const
|
|
1256
|
+
const loc = frame.original ?? frame.generated;
|
|
1290
1257
|
const fn = normalizeFunction(frame.function ?? frame.original?.name);
|
|
1291
|
-
return `${file}:${fn}`;
|
|
1258
|
+
return `${loc.file}:${fn}:${loc.line ?? ""}:${loc.column ?? ""}`;
|
|
1292
1259
|
});
|
|
1293
1260
|
};
|
|
1294
|
-
var computeBurstKey = (errorType, message, code, frames) => {
|
|
1261
|
+
var computeBurstKey = (errorType, message, code, frames, route, method) => {
|
|
1295
1262
|
const parts = frameBurstParts(frames);
|
|
1296
1263
|
const codePart = code ?? "";
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1264
|
+
return [
|
|
1265
|
+
errorType,
|
|
1266
|
+
codePart,
|
|
1267
|
+
method ?? "",
|
|
1268
|
+
route ?? "",
|
|
1269
|
+
parameterizeMessage(message),
|
|
1270
|
+
parts.join("\0")
|
|
1271
|
+
].join("\0");
|
|
1302
1272
|
};
|
|
1303
1273
|
|
|
1304
1274
|
// src/suppression/create-suppression.ts
|
|
@@ -1349,9 +1319,12 @@ var createSuppression = (options = {}) => {
|
|
|
1349
1319
|
windowStart: bucket.windowStart,
|
|
1350
1320
|
windowEndMs,
|
|
1351
1321
|
lastRelease: bucket.lastRelease,
|
|
1322
|
+
lastRoute: bucket.lastRoute,
|
|
1323
|
+
lastMethod: bucket.lastMethod,
|
|
1352
1324
|
lastEnvironment: bucket.lastEnvironment,
|
|
1353
1325
|
identity: bucket.identity,
|
|
1354
|
-
captureRawFrames: bucket.captureRawFrames
|
|
1326
|
+
captureRawFrames: bucket.captureRawFrames,
|
|
1327
|
+
burstKey: bucket.burstKey
|
|
1355
1328
|
});
|
|
1356
1329
|
const toSummary = (draft, identity) => ({
|
|
1357
1330
|
count: draft.count,
|
|
@@ -1359,7 +1332,8 @@ var createSuppression = (options = {}) => {
|
|
|
1359
1332
|
window_end: new Date(draft.windowEndMs).toISOString(),
|
|
1360
1333
|
release: draft.lastRelease,
|
|
1361
1334
|
environment: draft.lastEnvironment,
|
|
1362
|
-
...identity
|
|
1335
|
+
...identity,
|
|
1336
|
+
...draft.lastRoute || draft.lastMethod ? { request: { route: draft.lastRoute, method: draft.lastMethod } } : {}
|
|
1363
1337
|
});
|
|
1364
1338
|
const flushSuppressed = (bucket, windowEndMs) => {
|
|
1365
1339
|
if (bucket.suppressed <= 0) return;
|
|
@@ -1370,9 +1344,17 @@ var createSuppression = (options = {}) => {
|
|
|
1370
1344
|
const now = input.now ?? Date.now();
|
|
1371
1345
|
const identity = captureIdentity(input);
|
|
1372
1346
|
const captureRawFrames = (input.rawFrames ?? []).slice(0, MAX_STACK_FRAMES);
|
|
1373
|
-
const burstKey = computeBurstKey(
|
|
1347
|
+
const burstKey = computeBurstKey(
|
|
1348
|
+
input.errorType,
|
|
1349
|
+
input.message,
|
|
1350
|
+
input.code,
|
|
1351
|
+
input.frames,
|
|
1352
|
+
input.route,
|
|
1353
|
+
input.method
|
|
1354
|
+
);
|
|
1374
1355
|
const release = input.release;
|
|
1375
1356
|
const route = input.route ?? null;
|
|
1357
|
+
const method = input.method ?? null;
|
|
1376
1358
|
let bucket = burstBuckets.get(burstKey);
|
|
1377
1359
|
if (!bucket) {
|
|
1378
1360
|
bucket = {
|
|
@@ -1381,9 +1363,11 @@ var createSuppression = (options = {}) => {
|
|
|
1381
1363
|
windowStart: now,
|
|
1382
1364
|
lastRelease: release,
|
|
1383
1365
|
lastRoute: route,
|
|
1366
|
+
lastMethod: method,
|
|
1384
1367
|
lastEnvironment: input.environment,
|
|
1385
1368
|
identity,
|
|
1386
|
-
captureRawFrames
|
|
1369
|
+
captureRawFrames,
|
|
1370
|
+
burstKey
|
|
1387
1371
|
};
|
|
1388
1372
|
burstBuckets.set(burstKey, bucket);
|
|
1389
1373
|
evictIfNeeded();
|
|
@@ -1398,6 +1382,7 @@ var createSuppression = (options = {}) => {
|
|
|
1398
1382
|
bucket.windowStart = now;
|
|
1399
1383
|
bucket.lastRelease = release;
|
|
1400
1384
|
bucket.lastRoute = route;
|
|
1385
|
+
bucket.lastMethod = method;
|
|
1401
1386
|
bucket.lastEnvironment = input.environment;
|
|
1402
1387
|
bucket.identity = identity;
|
|
1403
1388
|
bucket.captureRawFrames = captureRawFrames;
|
|
@@ -1407,6 +1392,7 @@ var createSuppression = (options = {}) => {
|
|
|
1407
1392
|
bucket.sentInWindow += 1;
|
|
1408
1393
|
bucket.lastRelease = release;
|
|
1409
1394
|
bucket.lastRoute = route;
|
|
1395
|
+
bucket.lastMethod = method;
|
|
1410
1396
|
bucket.lastEnvironment = input.environment;
|
|
1411
1397
|
return { action: "send", priority: "repeat" };
|
|
1412
1398
|
}
|
|
@@ -1423,7 +1409,8 @@ var createSuppression = (options = {}) => {
|
|
|
1423
1409
|
}
|
|
1424
1410
|
return drafts.map((draft) => ({
|
|
1425
1411
|
summary: toSummary(draft, draft.identity),
|
|
1426
|
-
rawFrames: draft.captureRawFrames
|
|
1412
|
+
rawFrames: draft.captureRawFrames,
|
|
1413
|
+
burstKey: draft.burstKey
|
|
1427
1414
|
}));
|
|
1428
1415
|
};
|
|
1429
1416
|
const takeSummaries = async (now = Date.now(), prepareSummary) => {
|
|
@@ -1453,6 +1440,42 @@ var createSuppression = (options = {}) => {
|
|
|
1453
1440
|
};
|
|
1454
1441
|
};
|
|
1455
1442
|
|
|
1443
|
+
// src/suppression/grouping-ref-cache.ts
|
|
1444
|
+
var GROUPING_REF_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
1445
|
+
var createGroupingRefCache = (maxSize = 2e3) => {
|
|
1446
|
+
const byBurstKey = /* @__PURE__ */ new Map();
|
|
1447
|
+
const get = (burstKey, now = Date.now()) => {
|
|
1448
|
+
const entry = byBurstKey.get(burstKey);
|
|
1449
|
+
if (!entry || entry.expiresAt <= now) {
|
|
1450
|
+
if (entry) byBurstKey.delete(burstKey);
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
byBurstKey.delete(burstKey);
|
|
1454
|
+
byBurstKey.set(burstKey, entry);
|
|
1455
|
+
return entry.groupingRef;
|
|
1456
|
+
};
|
|
1457
|
+
const set = (burstKey, groupingRef, now = Date.now()) => {
|
|
1458
|
+
byBurstKey.delete(burstKey);
|
|
1459
|
+
byBurstKey.set(burstKey, { groupingRef, expiresAt: now + GROUPING_REF_TTL_MS });
|
|
1460
|
+
while (byBurstKey.size > maxSize) {
|
|
1461
|
+
const oldest = byBurstKey.keys().next().value;
|
|
1462
|
+
if (oldest === void 0) break;
|
|
1463
|
+
byBurstKey.delete(oldest);
|
|
1464
|
+
}
|
|
1465
|
+
};
|
|
1466
|
+
const getOrCreate = (burstKey, now = Date.now()) => {
|
|
1467
|
+
const existing = get(burstKey, now);
|
|
1468
|
+
if (existing) return existing;
|
|
1469
|
+
const groupingRef = crypto.randomUUID();
|
|
1470
|
+
set(burstKey, groupingRef, now);
|
|
1471
|
+
return groupingRef;
|
|
1472
|
+
};
|
|
1473
|
+
return { get, set, getOrCreate };
|
|
1474
|
+
};
|
|
1475
|
+
|
|
1476
|
+
// src/transport/default-ingest-request-timeout.ts
|
|
1477
|
+
var DEFAULT_INGEST_REQUEST_TIMEOUT_MS = 2e4;
|
|
1478
|
+
|
|
1456
1479
|
// src/transport/process-shutdown-hooks.ts
|
|
1457
1480
|
var SIGNAL_EXIT_CODES = { SIGTERM: 143, SIGINT: 130 };
|
|
1458
1481
|
var RASPUTIN_PROCESS_LISTENER = Symbol.for("rasputin.process.listener.v1");
|
|
@@ -1529,33 +1552,32 @@ var encodeJsonRequestBody = (payload, gzipThresholdBytes = GZIP_JSON_THRESHOLD_B
|
|
|
1529
1552
|
|
|
1530
1553
|
// src/transport/send-batch.ts
|
|
1531
1554
|
var sleep = (ms, signal) => new Promise((resolve4) => {
|
|
1532
|
-
if (ms <= 0
|
|
1555
|
+
if (ms <= 0) {
|
|
1533
1556
|
resolve4();
|
|
1534
1557
|
return;
|
|
1535
1558
|
}
|
|
1536
1559
|
const timer = setTimeout(resolve4, ms);
|
|
1537
1560
|
timer.unref?.();
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
()
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
{ once: true }
|
|
1545
|
-
);
|
|
1561
|
+
const onAbort = () => {
|
|
1562
|
+
clearTimeout(timer);
|
|
1563
|
+
resolve4();
|
|
1564
|
+
};
|
|
1565
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1566
|
+
if (signal.aborted) onAbort();
|
|
1546
1567
|
});
|
|
1547
1568
|
var backoffMs = (attempt) => {
|
|
1548
1569
|
const base = Math.min(3e4, 500 * 2 ** attempt);
|
|
1549
1570
|
return Math.floor(base * (0.5 + Math.random()));
|
|
1550
1571
|
};
|
|
1551
|
-
var
|
|
1572
|
+
var abortError2 = () => Object.assign(new Error("aborted"), { name: "AbortError" });
|
|
1552
1573
|
var fetchUntilAborted = (fetchFn, url, init) => new Promise((resolve4, reject) => {
|
|
1574
|
+
const onAbort = () => reject(abortError2());
|
|
1575
|
+
init.signal.addEventListener("abort", onAbort, { once: true });
|
|
1553
1576
|
if (init.signal.aborted) {
|
|
1554
|
-
|
|
1577
|
+
init.signal.removeEventListener("abort", onAbort);
|
|
1578
|
+
reject(abortError2());
|
|
1555
1579
|
return;
|
|
1556
1580
|
}
|
|
1557
|
-
const onAbort = () => reject(abortError());
|
|
1558
|
-
init.signal.addEventListener("abort", onAbort, { once: true });
|
|
1559
1581
|
void fetchFn(url, init).then(
|
|
1560
1582
|
(value) => {
|
|
1561
1583
|
init.signal.removeEventListener("abort", onAbort);
|
|
@@ -1575,8 +1597,29 @@ var retryAfterMs = (response) => {
|
|
|
1575
1597
|
const date = Date.parse(header);
|
|
1576
1598
|
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : null;
|
|
1577
1599
|
};
|
|
1600
|
+
var asRefList = (value) => {
|
|
1601
|
+
if (!Array.isArray(value)) return [];
|
|
1602
|
+
return value.map((entry) => typeof entry === "string" ? entry : null);
|
|
1603
|
+
};
|
|
1604
|
+
var readGroupingRefs = async (response) => {
|
|
1605
|
+
try {
|
|
1606
|
+
const body = await response.json();
|
|
1607
|
+
const payload = typeof body === "object" && body !== null && "payload" in body && typeof body.payload === "object" && body.payload !== null ? body.payload : body;
|
|
1608
|
+
if (typeof payload !== "object" || payload === null) {
|
|
1609
|
+
return { eventGroupingRefs: [], summaryGroupingRefs: [] };
|
|
1610
|
+
}
|
|
1611
|
+
const record = payload;
|
|
1612
|
+
return {
|
|
1613
|
+
eventGroupingRefs: asRefList(record.event_grouping_refs),
|
|
1614
|
+
summaryGroupingRefs: asRefList(record.summary_grouping_refs)
|
|
1615
|
+
};
|
|
1616
|
+
} catch {
|
|
1617
|
+
return { eventGroupingRefs: [], summaryGroupingRefs: [] };
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1578
1620
|
var sendBatch = async (options) => {
|
|
1579
1621
|
const summaries = options.summaries ?? [];
|
|
1622
|
+
const { signal } = options;
|
|
1580
1623
|
let payload;
|
|
1581
1624
|
if (summaries.length > 0) {
|
|
1582
1625
|
payload = { events: options.events, summaries };
|
|
@@ -1585,12 +1628,13 @@ var sendBatch = async (options) => {
|
|
|
1585
1628
|
}
|
|
1586
1629
|
if (options.sdkVersion) payload.sdk_version = options.sdkVersion;
|
|
1587
1630
|
if (options.sdkName) payload.sdk_name = options.sdkName;
|
|
1631
|
+
payload.scrub_pii = options.scrubPii !== false;
|
|
1588
1632
|
if (options.idempotencyKey) payload.idempotency_key = options.idempotencyKey;
|
|
1589
1633
|
let attempts = 0;
|
|
1590
1634
|
let networkErrors = 0;
|
|
1591
1635
|
let rateLimitedResponses = 0;
|
|
1592
1636
|
let serverErrorResponses = 0;
|
|
1593
|
-
const result = (outcome, failureKind = null, statusCode = null) => ({
|
|
1637
|
+
const result = (outcome, failureKind = null, statusCode = null, eventGroupingRefs = [], summaryGroupingRefs = []) => ({
|
|
1594
1638
|
outcome,
|
|
1595
1639
|
attempts,
|
|
1596
1640
|
retries: Math.max(0, attempts - 1),
|
|
@@ -1599,15 +1643,10 @@ var sendBatch = async (options) => {
|
|
|
1599
1643
|
serverErrorResponses,
|
|
1600
1644
|
aborted: failureKind === "aborted",
|
|
1601
1645
|
failureKind,
|
|
1602
|
-
statusCode
|
|
1646
|
+
statusCode,
|
|
1647
|
+
eventGroupingRefs,
|
|
1648
|
+
summaryGroupingRefs
|
|
1603
1649
|
});
|
|
1604
|
-
const abortAfter = (ms) => {
|
|
1605
|
-
const controller = new AbortController();
|
|
1606
|
-
const timer = setTimeout(() => controller.abort(), ms);
|
|
1607
|
-
timer.unref?.();
|
|
1608
|
-
return { signal: controller.signal, cancel: () => clearTimeout(timer) };
|
|
1609
|
-
};
|
|
1610
|
-
const requestTimeoutMs = options.requestTimeoutMs ?? 5e3;
|
|
1611
1650
|
const { body, gzip } = encodeJsonRequestBody(payload, options.gzipThresholdBytes);
|
|
1612
1651
|
const headers = {
|
|
1613
1652
|
Authorization: `Bearer ${options.projectApiKey}`,
|
|
@@ -1616,10 +1655,8 @@ var sendBatch = async (options) => {
|
|
|
1616
1655
|
};
|
|
1617
1656
|
if (gzip) headers["Content-Encoding"] = "gzip";
|
|
1618
1657
|
for (let attempt = 0; attempt <= options.maxRetries; attempt++) {
|
|
1619
|
-
if (
|
|
1658
|
+
if (signal.aborted) return result("failed", "aborted");
|
|
1620
1659
|
attempts++;
|
|
1621
|
-
const timeout = abortAfter(requestTimeoutMs);
|
|
1622
|
-
const signal = options.signal ? AbortSignal.any([options.signal, timeout.signal]) : timeout.signal;
|
|
1623
1660
|
try {
|
|
1624
1661
|
const response = await fetchUntilAborted(options.fetch, options.ingestUrl, {
|
|
1625
1662
|
method: "POST",
|
|
@@ -1627,13 +1664,22 @@ var sendBatch = async (options) => {
|
|
|
1627
1664
|
body,
|
|
1628
1665
|
signal
|
|
1629
1666
|
});
|
|
1630
|
-
if (response.ok)
|
|
1667
|
+
if (response.ok) {
|
|
1668
|
+
const refs = await readGroupingRefs(response);
|
|
1669
|
+
return result(
|
|
1670
|
+
"ok",
|
|
1671
|
+
null,
|
|
1672
|
+
response.status,
|
|
1673
|
+
refs.eventGroupingRefs,
|
|
1674
|
+
refs.summaryGroupingRefs
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1631
1677
|
if (response.status === 429) {
|
|
1632
1678
|
rateLimitedResponses++;
|
|
1633
1679
|
if (attempt === options.maxRetries)
|
|
1634
1680
|
return result("failed", "rate_limited", response.status);
|
|
1635
1681
|
const wait = retryAfterMs(response) ?? backoffMs(attempt);
|
|
1636
|
-
await sleep(wait,
|
|
1682
|
+
await sleep(wait, signal);
|
|
1637
1683
|
continue;
|
|
1638
1684
|
}
|
|
1639
1685
|
if (response.status >= 400 && response.status < 500) {
|
|
@@ -1641,19 +1687,17 @@ var sendBatch = async (options) => {
|
|
|
1641
1687
|
}
|
|
1642
1688
|
serverErrorResponses++;
|
|
1643
1689
|
if (attempt === options.maxRetries) return result("failed", "server_error", response.status);
|
|
1644
|
-
await sleep(backoffMs(attempt),
|
|
1690
|
+
await sleep(backoffMs(attempt), signal);
|
|
1645
1691
|
} catch (error) {
|
|
1646
1692
|
if (signal.aborted || error instanceof Error && error.name === "AbortError") {
|
|
1647
1693
|
return result("failed", "aborted");
|
|
1648
1694
|
}
|
|
1649
1695
|
networkErrors++;
|
|
1650
1696
|
if (attempt === options.maxRetries) return result("failed", "network_error");
|
|
1651
|
-
await sleep(backoffMs(attempt),
|
|
1652
|
-
} finally {
|
|
1653
|
-
timeout.cancel();
|
|
1697
|
+
await sleep(backoffMs(attempt), signal);
|
|
1654
1698
|
}
|
|
1655
1699
|
}
|
|
1656
|
-
return result("failed",
|
|
1700
|
+
return result("failed", signal.aborted ? "aborted" : "network_error");
|
|
1657
1701
|
};
|
|
1658
1702
|
|
|
1659
1703
|
// src/transport/transport-queue.ts
|
|
@@ -1780,9 +1824,8 @@ var createTransport = (options) => {
|
|
|
1780
1824
|
const flushIntervalMs = options.flushIntervalMs ?? DEFAULTS2.flushIntervalMs;
|
|
1781
1825
|
const gzipThresholdBytes = options.gzipThresholdBytes ?? DEFAULTS2.gzipThresholdBytes;
|
|
1782
1826
|
const maxRetries = options.maxRetries ?? DEFAULTS2.maxRetries;
|
|
1783
|
-
const requestTimeoutMs = options.requestTimeoutMs ??
|
|
1827
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_INGEST_REQUEST_TIMEOUT_MS;
|
|
1784
1828
|
const fetchFn = options.fetch ?? defaultIngestFetch();
|
|
1785
|
-
const backgroundFetch = options.fetch ?? defaultIngestFetch({ unrefSockets: true });
|
|
1786
1829
|
const queue = createQueue(
|
|
1787
1830
|
options.maxQueueSize ?? DEFAULTS2.maxQueueSize,
|
|
1788
1831
|
options.maxQueueBytes ?? DEFAULTS2.maxQueueBytes
|
|
@@ -1810,6 +1853,7 @@ var createTransport = (options) => {
|
|
|
1810
1853
|
let flushAbort;
|
|
1811
1854
|
let flushAbortTimer;
|
|
1812
1855
|
let flushDeadlineMs = 0;
|
|
1856
|
+
let flushHoldsProcess = false;
|
|
1813
1857
|
let inFlightBytes = 0;
|
|
1814
1858
|
let retryBytes = 0;
|
|
1815
1859
|
const estimateItemsBytes = (items) => {
|
|
@@ -1824,23 +1868,30 @@ var createTransport = (options) => {
|
|
|
1824
1868
|
};
|
|
1825
1869
|
const setFlushDeadline = (timeoutMs, holdProcess = false) => {
|
|
1826
1870
|
const nextDeadline = timeoutMs > 0 ? Date.now() + timeoutMs : Number.POSITIVE_INFINITY;
|
|
1827
|
-
if (
|
|
1828
|
-
|
|
1829
|
-
if (!
|
|
1871
|
+
if (holdProcess) flushHoldsProcess = true;
|
|
1872
|
+
const shouldReplace = !flushAbort || nextDeadline < flushDeadlineMs;
|
|
1873
|
+
if (!shouldReplace && !holdProcess) return;
|
|
1874
|
+
if (shouldReplace) {
|
|
1875
|
+
flushDeadlineMs = nextDeadline;
|
|
1876
|
+
if (!flushAbort) flushAbort = new AbortController();
|
|
1877
|
+
}
|
|
1830
1878
|
if (flushAbortTimer) clearTimeout(flushAbortTimer);
|
|
1831
|
-
if (Number.isFinite(
|
|
1832
|
-
flushAbortTimer =
|
|
1833
|
-
|
|
1834
|
-
Math.max(0, nextDeadline - Date.now())
|
|
1835
|
-
);
|
|
1836
|
-
if (!holdProcess) flushAbortTimer.unref?.();
|
|
1879
|
+
if (!Number.isFinite(flushDeadlineMs)) {
|
|
1880
|
+
flushAbortTimer = void 0;
|
|
1881
|
+
return;
|
|
1837
1882
|
}
|
|
1883
|
+
flushAbortTimer = setTimeout(
|
|
1884
|
+
() => flushAbort?.abort(),
|
|
1885
|
+
Math.max(0, flushDeadlineMs - Date.now())
|
|
1886
|
+
);
|
|
1887
|
+
if (!flushHoldsProcess) flushAbortTimer.unref?.();
|
|
1838
1888
|
};
|
|
1839
1889
|
const clearFlushDeadline = () => {
|
|
1840
1890
|
if (flushAbortTimer) clearTimeout(flushAbortTimer);
|
|
1841
1891
|
flushAbortTimer = void 0;
|
|
1842
1892
|
flushAbort = void 0;
|
|
1843
1893
|
flushDeadlineMs = 0;
|
|
1894
|
+
flushHoldsProcess = false;
|
|
1844
1895
|
};
|
|
1845
1896
|
const schedule = () => {
|
|
1846
1897
|
if (timer || startTimer || !enabled || closed || shuttingDown2) return;
|
|
@@ -1879,11 +1930,8 @@ var createTransport = (options) => {
|
|
|
1879
1930
|
};
|
|
1880
1931
|
const whenAborted = (signal) => new Promise((_, reject) => {
|
|
1881
1932
|
const abort = () => reject(signal.reason ?? new Error("aborted"));
|
|
1882
|
-
if (signal.aborted) {
|
|
1883
|
-
abort();
|
|
1884
|
-
return;
|
|
1885
|
-
}
|
|
1886
1933
|
signal.addEventListener("abort", abort, { once: true });
|
|
1934
|
+
if (signal.aborted) abort();
|
|
1887
1935
|
});
|
|
1888
1936
|
const performFlush = async () => {
|
|
1889
1937
|
options.beforeFlush?.();
|
|
@@ -1937,15 +1985,15 @@ var createTransport = (options) => {
|
|
|
1937
1985
|
}
|
|
1938
1986
|
}
|
|
1939
1987
|
const retainOrDrop = (nextReplays, createdAt) => {
|
|
1988
|
+
inFlightBytes = 0;
|
|
1940
1989
|
if (nextReplays >= DEFAULTS2.maxBatchReplays || Date.now() - createdAt >= DEFAULTS2.maxRetryBatchAgeMs) {
|
|
1941
1990
|
batchesDropped++;
|
|
1942
1991
|
retryBatch = null;
|
|
1943
1992
|
retryBytes = 0;
|
|
1944
1993
|
} else {
|
|
1945
1994
|
retryBatch = { items: batch, idempotencyKey, createdAt, replays: nextReplays };
|
|
1946
|
-
retryBytes =
|
|
1995
|
+
retryBytes = estimateItemsBytes(batch);
|
|
1947
1996
|
}
|
|
1948
|
-
inFlightBytes = 0;
|
|
1949
1997
|
syncReservedBytes();
|
|
1950
1998
|
};
|
|
1951
1999
|
if (controller.signal.aborted) {
|
|
@@ -1960,10 +2008,10 @@ var createTransport = (options) => {
|
|
|
1960
2008
|
idempotencyKey,
|
|
1961
2009
|
sdkVersion: options.sdkVersion,
|
|
1962
2010
|
sdkName: options.sdkName,
|
|
2011
|
+
scrubPii: options.scrubPii,
|
|
1963
2012
|
gzipThresholdBytes,
|
|
1964
2013
|
maxRetries: shuttingDown2 ? 0 : maxRetries,
|
|
1965
|
-
|
|
1966
|
-
fetch: shuttingDown2 ? fetchFn : backgroundFetch,
|
|
2014
|
+
fetch: fetchFn,
|
|
1967
2015
|
signal: controller.signal
|
|
1968
2016
|
});
|
|
1969
2017
|
sendAttempts += result.attempts;
|
|
@@ -1972,8 +2020,21 @@ var createTransport = (options) => {
|
|
|
1972
2020
|
rateLimitedResponses += result.rateLimitedResponses;
|
|
1973
2021
|
serverErrorResponses += result.serverErrorResponses;
|
|
1974
2022
|
if (result.aborted) flushTimeouts++;
|
|
1975
|
-
if (result.outcome === "ok")
|
|
1976
|
-
|
|
2023
|
+
if (result.outcome === "ok") {
|
|
2024
|
+
batchesSucceeded++;
|
|
2025
|
+
const eventBurstKeys = [];
|
|
2026
|
+
const summaryBurstKeys = [];
|
|
2027
|
+
for (const item of batch) {
|
|
2028
|
+
if (item.kind === "event") eventBurstKeys.push(item.burstKey);
|
|
2029
|
+
else summaryBurstKeys.push(item.burstKey);
|
|
2030
|
+
}
|
|
2031
|
+
options.onGroupingRefs?.({
|
|
2032
|
+
eventBurstKeys,
|
|
2033
|
+
summaryBurstKeys,
|
|
2034
|
+
eventGroupingRefs: result.eventGroupingRefs,
|
|
2035
|
+
summaryGroupingRefs: result.summaryGroupingRefs
|
|
2036
|
+
});
|
|
2037
|
+
} else {
|
|
1977
2038
|
lastFailureKind = result.failureKind;
|
|
1978
2039
|
lastFailureStatusCode = result.statusCode;
|
|
1979
2040
|
if (result.outcome === "dropped") batchesDropped++;
|
|
@@ -2029,15 +2090,15 @@ var createTransport = (options) => {
|
|
|
2029
2090
|
}
|
|
2030
2091
|
await startFlushWorker();
|
|
2031
2092
|
};
|
|
2032
|
-
const enqueue = (event, priority = "first", rawFrames = [], encodedBytes) => {
|
|
2093
|
+
const enqueue = (event, priority = "first", rawFrames = [], encodedBytes, burstKey) => {
|
|
2033
2094
|
if (!enabled || closed) return;
|
|
2034
|
-
queue.enqueue({ kind: "event", event, priority, rawFrames, encodedBytes });
|
|
2095
|
+
queue.enqueue({ kind: "event", event, priority, rawFrames, encodedBytes, burstKey });
|
|
2035
2096
|
schedule();
|
|
2036
2097
|
if (queue.size() >= batchSize) kickBackgroundFlush();
|
|
2037
2098
|
};
|
|
2038
|
-
const enqueueSummary = (summary, rawFrames = []) => {
|
|
2099
|
+
const enqueueSummary = (summary, rawFrames = [], burstKey) => {
|
|
2039
2100
|
if (!enabled || closed) return;
|
|
2040
|
-
queue.enqueue({ kind: "summary", summary, priority: "summary", rawFrames });
|
|
2101
|
+
queue.enqueue({ kind: "summary", summary, priority: "summary", rawFrames, burstKey });
|
|
2041
2102
|
schedule();
|
|
2042
2103
|
if (queue.size() >= batchSize) kickBackgroundFlush();
|
|
2043
2104
|
};
|
|
@@ -2080,21 +2141,10 @@ var createTransport = (options) => {
|
|
|
2080
2141
|
stopTimer();
|
|
2081
2142
|
unregisterSignals();
|
|
2082
2143
|
shuttingDown2 = true;
|
|
2083
|
-
await
|
|
2084
|
-
const timer2 = setTimeout(resolve4, DEFAULTS2.shutdownFlushMs + 250);
|
|
2085
|
-
void flush(DEFAULTS2.shutdownFlushMs).then(
|
|
2086
|
-
() => {
|
|
2087
|
-
clearTimeout(timer2);
|
|
2088
|
-
resolve4();
|
|
2089
|
-
},
|
|
2090
|
-
(error) => {
|
|
2091
|
-
clearTimeout(timer2);
|
|
2092
|
-
reject(error);
|
|
2093
|
-
}
|
|
2094
|
-
);
|
|
2144
|
+
await flush(DEFAULTS2.shutdownFlushMs).catch(() => {
|
|
2095
2145
|
});
|
|
2096
|
-
dropRemainingOnExit();
|
|
2097
2146
|
closed = true;
|
|
2147
|
+
dropRemainingOnExit();
|
|
2098
2148
|
queue.clear();
|
|
2099
2149
|
retryBatch = null;
|
|
2100
2150
|
};
|
|
@@ -2316,6 +2366,7 @@ var createClient = (options, hooks = {}) => {
|
|
|
2316
2366
|
return pending;
|
|
2317
2367
|
};
|
|
2318
2368
|
const suppression = createSuppression();
|
|
2369
|
+
const groupingRefs = createGroupingRefCache();
|
|
2319
2370
|
let enqueuePendingSummaries = () => {
|
|
2320
2371
|
};
|
|
2321
2372
|
const transport = createTransport({
|
|
@@ -2326,7 +2377,23 @@ var createClient = (options, hooks = {}) => {
|
|
|
2326
2377
|
installSignalHandlers: hooks.installTransportSignalHandlers,
|
|
2327
2378
|
sdkVersion: hooks.sdkVersion ?? SDK_VERSION,
|
|
2328
2379
|
sdkName: hooks.sdkName ?? SDK_NAME,
|
|
2380
|
+
scrubPii: options.scrubPII !== false,
|
|
2329
2381
|
beforeFlush: () => enqueuePendingSummaries(),
|
|
2382
|
+
onGroupingRefs: ({
|
|
2383
|
+
eventBurstKeys,
|
|
2384
|
+
summaryBurstKeys,
|
|
2385
|
+
eventGroupingRefs,
|
|
2386
|
+
summaryGroupingRefs
|
|
2387
|
+
}) => {
|
|
2388
|
+
for (const [index, burstKey] of eventBurstKeys.entries()) {
|
|
2389
|
+
const groupingRef = eventGroupingRefs[index];
|
|
2390
|
+
if (burstKey && groupingRef) groupingRefs.set(burstKey, groupingRef);
|
|
2391
|
+
}
|
|
2392
|
+
for (const [index, burstKey] of summaryBurstKeys.entries()) {
|
|
2393
|
+
const groupingRef = summaryGroupingRefs[index];
|
|
2394
|
+
if (burstKey && groupingRef) groupingRefs.set(burstKey, groupingRef);
|
|
2395
|
+
}
|
|
2396
|
+
},
|
|
2330
2397
|
prepareEvent: async ({ event, rawFrames }) => {
|
|
2331
2398
|
const prepared = await resolveStack(rawFrames);
|
|
2332
2399
|
return withResolvedFrames(event, prepared.frames, prepared.resolution);
|
|
@@ -2360,6 +2427,16 @@ var createClient = (options, hooks = {}) => {
|
|
|
2360
2427
|
request: context?.request
|
|
2361
2428
|
});
|
|
2362
2429
|
event.message = redactEnvSecrets(event.message);
|
|
2430
|
+
const burstKey = computeBurstKey(
|
|
2431
|
+
event.type,
|
|
2432
|
+
event.message,
|
|
2433
|
+
event.code,
|
|
2434
|
+
frames,
|
|
2435
|
+
context?.request?.route,
|
|
2436
|
+
context?.request?.method
|
|
2437
|
+
);
|
|
2438
|
+
const groupingRef = groupingRefs.getOrCreate(burstKey);
|
|
2439
|
+
event.grouping_ref = groupingRef;
|
|
2363
2440
|
const decision = suppression.decide({
|
|
2364
2441
|
errorType: event.type,
|
|
2365
2442
|
message: event.message,
|
|
@@ -2369,7 +2446,8 @@ var createClient = (options, hooks = {}) => {
|
|
|
2369
2446
|
code: event.code,
|
|
2370
2447
|
environment,
|
|
2371
2448
|
release,
|
|
2372
|
-
route: context?.request?.route
|
|
2449
|
+
route: context?.request?.route,
|
|
2450
|
+
method: context?.request?.method
|
|
2373
2451
|
});
|
|
2374
2452
|
if (decision.action !== "send") return;
|
|
2375
2453
|
const runtimeState = resolveRuntimeEvidence(context, hooks);
|
|
@@ -2380,13 +2458,15 @@ var createClient = (options, hooks = {}) => {
|
|
|
2380
2458
|
} catch {
|
|
2381
2459
|
encodedBytes = void 0;
|
|
2382
2460
|
}
|
|
2383
|
-
transport.enqueue(event, decision.priority, rawFrames, encodedBytes);
|
|
2461
|
+
transport.enqueue(event, decision.priority, rawFrames, encodedBytes, burstKey);
|
|
2384
2462
|
} catch {
|
|
2385
2463
|
}
|
|
2386
2464
|
};
|
|
2387
2465
|
enqueuePendingSummaries = () => {
|
|
2388
|
-
for (const { summary, rawFrames } of suppression.drainSummaries()) {
|
|
2389
|
-
|
|
2466
|
+
for (const { summary, rawFrames, burstKey } of suppression.drainSummaries()) {
|
|
2467
|
+
const groupingRef = groupingRefs.getOrCreate(burstKey);
|
|
2468
|
+
summary.grouping_ref = groupingRef;
|
|
2469
|
+
transport.enqueueSummary(summary, rawFrames, burstKey);
|
|
2390
2470
|
}
|
|
2391
2471
|
};
|
|
2392
2472
|
const flush = async (timeoutMs) => {
|