@devkitio/faultlens 0.1.5 → 1.0.1

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.
@@ -1,535 +0,0 @@
1
- // ../security/dist/redaction.js
2
- var REDACTED = "[\u5DF2\u8131\u654F]";
3
- var MAX_STRING_LENGTH = 32768;
4
- var SENSITIVE_KEY = /^(?:pass(?:word|wd)?|pwd|cookie|set[-_]?cookie|authorization|proxy[-_]?authorization|(?:access|refresh|id)?[-_]?token|api[-_]?key|secret|client[-_]?secret|private[-_]?key|card(?:number)?|pan|cvv|cvc|payment(?:data)?|oauth[-_]?(?:state|nonce)|state|nonce|registration[-_]?draft)$/i;
5
- var DANGEROUS_OBJECT_KEY = /^(?:__proto__|constructor|prototype)$/;
6
- var AUTH_VALUE = /^(?:Bearer|Basic|Digest)\s+\S+/i;
7
- var PRIVATE_KEY_VALUE = /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/;
8
- var JWT_VALUE = /^eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
9
- var ABSOLUTE_HTTP_URL = /https?:\/\/[^\s<>"'`]+/gi;
10
- var SENSITIVE_PATH = /(\/(?:token|secret|password|key|reset|invite|download|verify|verification|activate|activation|credential)s?\/)([^/?#\s]+)/gi;
11
- var EMBEDDED_AUTH_VALUE = /\b((?:authorization|proxy[-_]?authorization)\s*[:=]\s*)?(?:Bearer|Basic|Digest)\s+[^\s,;"']+/gi;
12
- var EMBEDDED_SENSITIVE_VALUE = /\b((?:(?:access|refresh|id)[-_]?token|token|api[-_]?key|secret|client[-_]?secret|password|passwd|pwd)\s*[:=]\s*)[^\s,;"']+/gi;
13
- var EMBEDDED_JWT_VALUE = /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g;
14
- function looksLikePaymentCard(value) {
15
- const digits = value.replace(/[\s-]/g, "");
16
- if (!/^\d{13,19}$/.test(digits))
17
- return false;
18
- let sum = 0;
19
- let alternate = false;
20
- for (let index = digits.length - 1; index >= 0; index -= 1) {
21
- let digit = Number(digits[index]);
22
- if (alternate) {
23
- digit *= 2;
24
- if (digit > 9)
25
- digit -= 9;
26
- }
27
- sum += digit;
28
- alternate = !alternate;
29
- }
30
- return sum % 10 === 0;
31
- }
32
- function redactHttpUrl(value) {
33
- try {
34
- const url = new URL(value);
35
- const segments = url.pathname.split("/");
36
- for (let index = 0; index < segments.length - 1; index += 1) {
37
- if (/^(?:token|secret|password|key|reset|invite|download|verify|verification|activate|activation|credential)s?$/i.test(segments[index] ?? "")) {
38
- segments[index + 1] = REDACTED;
39
- }
40
- }
41
- return `${url.origin}${segments.join("/")}`;
42
- } catch {
43
- return REDACTED;
44
- }
45
- }
46
- function redactSensitiveString(value) {
47
- if (AUTH_VALUE.test(value) || PRIVATE_KEY_VALUE.test(value) || JWT_VALUE.test(value) || looksLikePaymentCard(value)) {
48
- return REDACTED;
49
- }
50
- return value.slice(0, MAX_STRING_LENGTH).replace(ABSOLUTE_HTTP_URL, redactHttpUrl).replace(SENSITIVE_PATH, `$1${REDACTED}`).replace(EMBEDDED_AUTH_VALUE, (_matched, prefix) => `${prefix ?? ""}${REDACTED}`).replace(EMBEDDED_SENSITIVE_VALUE, `$1${REDACTED}`).replace(EMBEDDED_JWT_VALUE, REDACTED);
51
- }
52
- function shouldRedactObjectKey(key) {
53
- return SENSITIVE_KEY.test(key) || DANGEROUS_OBJECT_KEY.test(key);
54
- }
55
-
56
- // src/sanitize.ts
57
- var REDACTED2 = "[\u5DF2\u8131\u654F]";
58
- function clientRedactString(value) {
59
- return redactSensitiveString(value);
60
- }
61
- function clientRedact(value) {
62
- const seen = /* @__PURE__ */ new WeakSet();
63
- let keys = 0;
64
- function visit(current, depth) {
65
- if (typeof current === "string") {
66
- return clientRedactString(current);
67
- }
68
- if (current === null || typeof current !== "object") return current;
69
- if (depth > 10 || seen.has(current)) return "[\u5DF2\u622A\u65AD]";
70
- seen.add(current);
71
- if (Array.isArray(current)) return current.slice(0, 100).map((item) => visit(item, depth + 1));
72
- const result = {};
73
- for (const [key, item] of Object.entries(current)) {
74
- keys += 1;
75
- if (keys > 400) {
76
- result.__truncated__ = "[\u5DF2\u622A\u65AD]";
77
- break;
78
- }
79
- Object.defineProperty(result, key, {
80
- value: shouldRedactObjectKey(key) ? REDACTED2 : visit(item, depth + 1),
81
- enumerable: true,
82
- configurable: true,
83
- writable: true
84
- });
85
- }
86
- return result;
87
- }
88
- return visit(value, 0);
89
- }
90
-
91
- // src/transport.ts
92
- function parseDirectDsn(dsn) {
93
- const url = new URL(dsn);
94
- const localHostname = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
95
- if (url.protocol !== "https:" && !(url.protocol === "http:" && localHostname)) {
96
- throw new Error("Direct DSN \u5FC5\u987B\u4F7F\u7528 HTTPS");
97
- }
98
- if (!url.username || url.password || url.pathname !== "/v1/direct/envelope" || url.search || url.hash) {
99
- throw new Error("Direct DSN \u683C\u5F0F\u65E0\u6548");
100
- }
101
- const publicDsn = decodeURIComponent(url.username);
102
- url.username = "";
103
- url.password = "";
104
- return { endpoint: url.toString(), publicDsn };
105
- }
106
- function retryAfterMs(response, body) {
107
- const value = response.headers.get("retry-after");
108
- if (value && /^\d+$/.test(value)) return Number(value) * 1e3;
109
- if (typeof body === "object" && body !== null && "retryAfterMs" in body && typeof body.retryAfterMs === "number") {
110
- return Math.max(0, body.retryAfterMs);
111
- }
112
- return 1e3;
113
- }
114
- var FetchTransport = class {
115
- constructor(ingest, fetcher = fetch) {
116
- this.ingest = ingest;
117
- this.fetcher = fetcher;
118
- }
119
- async send(envelope) {
120
- try {
121
- const direct = this.ingest.mode === "direct" ? parseDirectDsn(this.ingest.dsn) : void 0;
122
- const endpoint = direct?.endpoint ?? this.ingest.endpoint;
123
- const response = await this.fetcher(endpoint, {
124
- method: "POST",
125
- headers: {
126
- "content-type": "application/json",
127
- ...direct ? { "x-faultlens-dsn": direct.publicDsn } : {}
128
- },
129
- body: JSON.stringify(envelope),
130
- credentials: "omit",
131
- cache: "no-store",
132
- redirect: "error",
133
- keepalive: true
134
- });
135
- let body;
136
- try {
137
- body = await response.json();
138
- } catch {
139
- body = void 0;
140
- }
141
- if (response.status === 202) return { kind: "accepted" };
142
- if (response.status === 429 || response.status >= 500) {
143
- return { kind: "retryable", retryAfterMs: retryAfterMs(response, body) };
144
- }
145
- const code = typeof body === "object" && body !== null && "code" in body ? String(body.code) : `http_${response.status}`;
146
- return { kind: "permanent_error", code };
147
- } catch {
148
- return { kind: "network_error" };
149
- }
150
- }
151
- async loadRemoteConfig() {
152
- try {
153
- const direct = this.ingest.mode === "direct" ? parseDirectDsn(this.ingest.dsn) : void 0;
154
- const base = direct?.endpoint ?? this.ingest.endpoint;
155
- const endpoint = `${base.replace(/\/$/, "")}/config`;
156
- const response = await this.fetcher(endpoint, {
157
- method: "GET",
158
- headers: direct ? { "x-faultlens-dsn": direct.publicDsn } : {},
159
- credentials: "omit",
160
- cache: "no-store",
161
- redirect: "error"
162
- });
163
- if (!response.ok) return void 0;
164
- const value = await response.json();
165
- return {
166
- ...typeof value.enabled === "boolean" ? { enabled: value.enabled } : {},
167
- ...typeof value.sampleRate === "number" ? { sampleRate: value.sampleRate } : {},
168
- ...Array.isArray(value.errorTypeAllowlist) ? { errorTypeAllowlist: value.errorTypeAllowlist.filter((item) => typeof item === "string") } : {},
169
- ...typeof value.queueLimit === "number" ? { queueLimit: value.queueLimit } : {},
170
- ...typeof value.retryBaseMs === "number" ? { retryBaseMs: value.retryBaseMs } : {}
171
- };
172
- } catch {
173
- return void 0;
174
- }
175
- }
176
- };
177
-
178
- // src/monitor.ts
179
- var SDK_VERSION = "0.1.5";
180
- var PROTOCOL_VERSION = "1.0";
181
- var REVOKED_CREDENTIAL_CODES = /* @__PURE__ */ new Set(["invalid_dsn", "relay_key_revoked", "credential_revoked"]);
182
- function eventId() {
183
- if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
184
- return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}`;
185
- }
186
- function normalizedFilename(filename) {
187
- try {
188
- const url = new URL(filename);
189
- url.search = "";
190
- url.hash = "";
191
- return url.toString();
192
- } catch {
193
- return filename.split(/[?#]/, 1)[0] ?? filename;
194
- }
195
- }
196
- function rawStackFrames(stack) {
197
- return stack.split("\n").slice(1, 101).map((line) => {
198
- const trimmed = line.trim();
199
- const match = trimmed.match(/(?:at\s+(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/);
200
- return {
201
- filename: match?.[2] ?? trimmed.slice(0, 2048),
202
- ...match?.[1] ? { function: match[1].slice(0, 1024) } : {},
203
- ...match?.[3] ? { line: Number(match[3]) } : {},
204
- ...match?.[4] ? { column: Number(match[4]) } : {},
205
- inApp: match?.[2] ? !/node_modules|<anonymous>/.test(match[2]) : false
206
- };
207
- });
208
- }
209
- function runtimeDebugIds() {
210
- const value = globalThis.__FAULTLENS_DEBUG_IDS__;
211
- const result = /* @__PURE__ */ new Map();
212
- if (!value || typeof value !== "object") return result;
213
- for (const [stack, debugId] of Object.entries(value)) {
214
- if (typeof debugId !== "string") continue;
215
- const frame = rawStackFrames(stack).find((item) => item.filename);
216
- if (frame) result.set(normalizedFilename(frame.filename), debugId);
217
- }
218
- return result;
219
- }
220
- function parseErrorStack(error) {
221
- let stack = "";
222
- try {
223
- stack = typeof error.stack === "string" ? error.stack : "";
224
- } catch {
225
- return [];
226
- }
227
- const debugIds = runtimeDebugIds();
228
- return stack ? rawStackFrames(stack).map((frame) => {
229
- const debugId = debugIds.get(normalizedFilename(frame.filename));
230
- return debugId ? { ...frame, debugId } : frame;
231
- }) : [];
232
- }
233
- function errorShape(error) {
234
- let isError = false;
235
- try {
236
- isError = error instanceof Error;
237
- } catch {
238
- isError = false;
239
- }
240
- if (isError) {
241
- let name = "Error";
242
- let message = "\u65E0\u6CD5\u5B89\u5168\u8BFB\u53D6\u5F02\u5E38\u5185\u5BB9";
243
- try {
244
- if (typeof error.name === "string" && error.name) {
245
- name = error.name;
246
- }
247
- } catch {
248
- name = "Error";
249
- }
250
- try {
251
- if (typeof error.message === "string") {
252
- message = clientRedactString(error.message);
253
- }
254
- } catch {
255
- message = "\u65E0\u6CD5\u5B89\u5168\u8BFB\u53D6\u5F02\u5E38\u5185\u5BB9";
256
- }
257
- return {
258
- type: name,
259
- message,
260
- stack: parseErrorStack(error)
261
- };
262
- }
263
- if (typeof error === "string") return { type: "Error", message: clientRedactString(error), stack: [] };
264
- return {
265
- type: "NonErrorException",
266
- message: "\u6355\u83B7\u5230\u975E Error \u7C7B\u578B\u5F02\u5E38",
267
- stack: []
268
- };
269
- }
270
- function shallowFingerprint(event) {
271
- const frame = event.stack.find((item) => item.inApp) ?? event.stack[0];
272
- return `${event.type}${event.message}${frame?.filename ?? ""}${frame?.line ?? ""}`;
273
- }
274
- function createMonitorRuntime(options, overrides = {}) {
275
- const dependencies = {
276
- transport: overrides.transport ?? new FetchTransport(options.ingest),
277
- now: overrides.now ?? (() => /* @__PURE__ */ new Date()),
278
- random: overrides.random ?? Math.random,
279
- createEventId: overrides.createEventId ?? eventId
280
- };
281
- const queue = [];
282
- const pending = /* @__PURE__ */ new Set();
283
- const pendingEventIds = /* @__PURE__ */ new Set();
284
- const breadcrumbs = [];
285
- const namedContext = /* @__PURE__ */ new Map();
286
- const seenErrors = /* @__PURE__ */ new WeakSet();
287
- const recentFingerprints = /* @__PURE__ */ new Map();
288
- const droppedReasons = {};
289
- let queueLimit = Math.min(50, Math.max(1, options.queueLimit ?? 50));
290
- let sampleRate = Math.min(1, Math.max(0, options.sampleRate ?? 1));
291
- let errorTypeAllowlist;
292
- let user = null;
293
- let closed = false;
294
- let sendTimer;
295
- let recentSendStatus = "idle";
296
- let lastRateLimitedAt = null;
297
- let remotelyDisabled = false;
298
- const persistentQueue = overrides.persistentQueue;
299
- const sendLease = overrides.sendLease;
300
- const restorePromise = persistentQueue?.load(dependencies.now().getTime()).then((items) => {
301
- for (const item of items) {
302
- if (!queue.some((queued) => queued.event.eventId === item.id)) {
303
- queue.push({ event: item.event, enqueuedAt: item.enqueuedAt });
304
- }
305
- }
306
- }).catch(() => drop("indexeddb_unavailable"));
307
- void dependencies.transport.loadRemoteConfig?.().then((config) => {
308
- if (!config) return;
309
- if (config.enabled === false) remotelyDisabled = true;
310
- if (typeof config.sampleRate === "number") sampleRate = Math.min(sampleRate, Math.max(0, config.sampleRate));
311
- if (typeof config.queueLimit === "number") queueLimit = Math.min(queueLimit, Math.max(1, config.queueLimit));
312
- if (config.errorTypeAllowlist) errorTypeAllowlist = new Set(config.errorTypeAllowlist);
313
- }).catch(() => void 0);
314
- function drop(reason) {
315
- droppedReasons[reason] = (droppedReasons[reason] ?? 0) + 1;
316
- }
317
- function scheduleFlush() {
318
- if (sendTimer || closed) return;
319
- sendTimer = setTimeout(() => {
320
- sendTimer = void 0;
321
- void flushInternal();
322
- }, 100);
323
- }
324
- async function prepare(event) {
325
- try {
326
- const beforeSendResult = options.beforeSend ? await options.beforeSend(event) : event;
327
- if (!beforeSendResult) {
328
- drop("before_send");
329
- return;
330
- }
331
- const sanitized = clientRedact(beforeSendResult);
332
- const size = new TextEncoder().encode(JSON.stringify(sanitized)).byteLength;
333
- if (size > 128 * 1024) {
334
- drop("payload_too_large");
335
- return;
336
- }
337
- while (queue.length >= queueLimit) {
338
- queue.shift();
339
- drop("queue_limit");
340
- }
341
- queue.push({ event: sanitized, enqueuedAt: dependencies.now().getTime() });
342
- void persistentQueue?.put({
343
- id: sanitized.eventId,
344
- event: sanitized,
345
- enqueuedAt: dependencies.now().getTime(),
346
- bytes: size
347
- }).catch(() => drop("indexeddb_write_failed"));
348
- scheduleFlush();
349
- } catch {
350
- drop("before_send_error");
351
- }
352
- }
353
- function enqueue(event) {
354
- pendingEventIds.add(event.eventId);
355
- const promise = prepare(event).finally(() => {
356
- pending.delete(promise);
357
- pendingEventIds.delete(event.eventId);
358
- });
359
- pending.add(promise);
360
- return event.eventId;
361
- }
362
- function baseEvent(shape, capture = {}) {
363
- const context = {
364
- ...user ? { user } : {},
365
- contexts: Object.fromEntries(namedContext),
366
- breadcrumbs: breadcrumbs.slice(-50),
367
- ...capture.context === void 0 ? {} : { captured: capture.context },
368
- contextMode: options.contextMode ?? "minimal"
369
- };
370
- return {
371
- eventId: dependencies.createEventId(),
372
- occurredAt: dependencies.now().toISOString(),
373
- type: shape.type,
374
- message: shape.message,
375
- handled: capture.handled ?? false,
376
- mechanism: capture.mechanism ?? "manual",
377
- level: capture.level ?? "error",
378
- stack: shape.stack,
379
- tags: clientRedact(capture.tags ?? {}),
380
- context: clientRedact(context)
381
- };
382
- }
383
- function shouldIgnore(error, event) {
384
- if (closed || remotelyDisabled) {
385
- drop(closed ? "closed" : "remote_disabled");
386
- return true;
387
- }
388
- if (options.ignoreError?.(error)) {
389
- drop("ignored");
390
- return true;
391
- }
392
- if (errorTypeAllowlist && !errorTypeAllowlist.has(event.type)) {
393
- drop("remote_type_filter");
394
- return true;
395
- }
396
- if (typeof error === "object" && error !== null) {
397
- if (seenErrors.has(error)) {
398
- drop("duplicate");
399
- return true;
400
- }
401
- seenErrors.add(error);
402
- }
403
- const fingerprint = shallowFingerprint(event);
404
- const seenAt = recentFingerprints.get(fingerprint);
405
- const now = dependencies.now().getTime();
406
- if (seenAt !== void 0 && now - seenAt < 2e3) {
407
- drop("duplicate");
408
- return true;
409
- }
410
- recentFingerprints.set(fingerprint, now);
411
- if (dependencies.random() >= sampleRate) {
412
- drop("sampled");
413
- return true;
414
- }
415
- return false;
416
- }
417
- function envelope(events) {
418
- return {
419
- protocolVersion: PROTOCOL_VERSION,
420
- sdkName: "@devkitio/faultlens",
421
- sdkVersion: SDK_VERSION,
422
- clientType: options.ingest.mode === "relay" ? "relay" : "browser",
423
- expectedEnvironment: options.expectedEnvironment,
424
- release: options.release,
425
- dist: options.dist ?? "default",
426
- buildId: options.buildId ?? options.release,
427
- clientBatchId: dependencies.createEventId(),
428
- events
429
- };
430
- }
431
- async function flushInternal() {
432
- await restorePromise;
433
- await Promise.all([...pending]);
434
- const expiredBefore = dependencies.now().getTime() - 24 * 60 * 60 * 1e3;
435
- while (queue[0] && queue[0].enqueuedAt < expiredBefore) {
436
- queue.shift();
437
- drop("queue_expired");
438
- }
439
- if (queue.length === 0) return true;
440
- const batch = queue.slice(0, 20);
441
- if (sendLease && !sendLease.acquire(dependencies.now().getTime())) return false;
442
- recentSendStatus = "sending";
443
- const outcome = await dependencies.transport.send(envelope(batch.map((item) => item.event)));
444
- sendLease?.release();
445
- if (outcome.kind === "accepted" || outcome.kind === "permanent_error") {
446
- const credentialRevoked = outcome.kind === "permanent_error" && REVOKED_CREDENTIAL_CODES.has(outcome.code);
447
- if (credentialRevoked) {
448
- remotelyDisabled = true;
449
- queue.splice(0, queue.length);
450
- void persistentQueue?.clear().catch(() => void 0);
451
- } else {
452
- queue.splice(0, batch.length);
453
- void persistentQueue?.remove(batch.map((item) => item.event.eventId)).catch(() => void 0);
454
- }
455
- recentSendStatus = outcome.kind === "accepted" ? "accepted" : "permanent_error";
456
- if (outcome.kind === "permanent_error") drop(outcome.code);
457
- return queue.length === 0;
458
- }
459
- recentSendStatus = "retryable";
460
- if (outcome.kind === "retryable") lastRateLimitedAt = dependencies.now().toISOString();
461
- return false;
462
- }
463
- async function flush(timeout) {
464
- if (sendTimer) {
465
- clearTimeout(sendTimer);
466
- sendTimer = void 0;
467
- }
468
- let timer;
469
- try {
470
- return await Promise.race([
471
- flushInternal(),
472
- new Promise((resolve) => {
473
- timer = setTimeout(() => resolve(false), Math.max(0, timeout));
474
- })
475
- ]);
476
- } finally {
477
- if (timer) clearTimeout(timer);
478
- }
479
- }
480
- const monitor = {
481
- captureException(error, capture = {}) {
482
- const event = baseEvent(errorShape(error), capture);
483
- if (shouldIgnore(error, event)) return event.eventId;
484
- return enqueue(event);
485
- },
486
- captureMessage(message, capture = {}) {
487
- const event = baseEvent({ type: "Message", message: message.slice(0, 32768), stack: [] }, capture);
488
- if (shouldIgnore(message, event)) return event.eventId;
489
- return enqueue(event);
490
- },
491
- addBreadcrumb(breadcrumb) {
492
- breadcrumbs.push(
493
- clientRedact({
494
- ...breadcrumb,
495
- timestamp: breadcrumb.timestamp ?? dependencies.now().toISOString()
496
- })
497
- );
498
- if (breadcrumbs.length > 50) breadcrumbs.shift();
499
- },
500
- setUser(value) {
501
- user = value ? clientRedact(value) : null;
502
- },
503
- setContext(name, value) {
504
- namedContext.set(name.slice(0, 128), clientRedact(value));
505
- },
506
- getStatus() {
507
- const queuedEventIds = new Set(queue.map((item) => item.event.eventId));
508
- return {
509
- queueLength: (/* @__PURE__ */ new Set([...queuedEventIds, ...pendingEventIds])).size,
510
- recentSendStatus,
511
- droppedReasons: { ...droppedReasons },
512
- protocolVersion: PROTOCOL_VERSION,
513
- sdkVersion: SDK_VERSION,
514
- lastRateLimitedAt,
515
- remotelyDisabled
516
- };
517
- },
518
- flush,
519
- async close() {
520
- closed = true;
521
- if (sendTimer) clearTimeout(sendTimer);
522
- return flush(2e3);
523
- }
524
- };
525
- const transportPath = options.ingest.mode === "relay" ? options.ingest.endpoint : new URL(options.ingest.dsn).pathname;
526
- const removeGlobalHandlers = overrides.installGlobalHandlers?.(monitor, transportPath) ?? (() => void 0);
527
- const originalClose = monitor.close;
528
- monitor.close = async () => {
529
- removeGlobalHandlers();
530
- return originalClose();
531
- };
532
- return monitor;
533
- }
534
-
535
- export { SDK_VERSION, clientRedact, createMonitorRuntime, parseErrorStack };
@@ -1,136 +0,0 @@
1
- interface StackFrame {
2
- filename: string;
3
- function?: string;
4
- line?: number;
5
- column?: number;
6
- inApp: boolean;
7
- debugId?: string;
8
- }
9
- interface ErrorEvent {
10
- eventId: string;
11
- occurredAt: string;
12
- type: string;
13
- message: string;
14
- handled: boolean;
15
- mechanism: string;
16
- level: "debug" | "info" | "warning" | "error" | "fatal";
17
- stack: StackFrame[];
18
- tags: Record<string, string>;
19
- context?: unknown;
20
- fingerprint?: string[];
21
- }
22
- interface EventEnvelope {
23
- protocolVersion: string;
24
- sdkName: string;
25
- sdkVersion: string;
26
- clientType: "browser" | "relay" | "nitro" | "testing";
27
- expectedEnvironment?: string;
28
- release: string;
29
- dist?: string;
30
- buildId: string;
31
- clientBatchId: string;
32
- events: ErrorEvent[];
33
- }
34
-
35
- type ContextMode = "minimal" | "full";
36
- type IngestConfig = {
37
- mode: "direct";
38
- dsn: string;
39
- } | {
40
- mode: "relay";
41
- endpoint: string;
42
- };
43
- interface CaptureOptions {
44
- handled?: boolean;
45
- mechanism?: string;
46
- context?: unknown;
47
- level?: ErrorEvent["level"];
48
- tags?: Record<string, string>;
49
- }
50
- interface Breadcrumb {
51
- category: string;
52
- message: string;
53
- level?: "debug" | "info" | "warning" | "error";
54
- timestamp?: string;
55
- data?: unknown;
56
- }
57
- interface MonitorOptions {
58
- ingest: IngestConfig;
59
- expectedEnvironment: string;
60
- release: string;
61
- dist?: string;
62
- buildId?: string;
63
- contextMode?: ContextMode;
64
- ignoreError?: (error: unknown) => boolean;
65
- beforeSend?: (event: ErrorEvent) => ErrorEvent | null | Promise<ErrorEvent | null>;
66
- sampleRate?: number;
67
- queueLimit?: number;
68
- }
69
- interface MonitorStatus {
70
- queueLength: number;
71
- recentSendStatus: "idle" | "sending" | "accepted" | "retryable" | "permanent_error";
72
- droppedReasons: Readonly<Record<string, number>>;
73
- protocolVersion: string;
74
- sdkVersion: string;
75
- lastRateLimitedAt: string | null;
76
- remotelyDisabled: boolean;
77
- }
78
- interface ErrorMonitor {
79
- captureException(error: unknown, options?: CaptureOptions): string;
80
- captureMessage(message: string, options?: CaptureOptions): string;
81
- addBreadcrumb(breadcrumb: Breadcrumb): void;
82
- setUser(user: Record<string, unknown> | null): void;
83
- setContext(name: string, value: unknown): void;
84
- getStatus(): MonitorStatus;
85
- flush(timeout: number): Promise<boolean>;
86
- close(): Promise<boolean>;
87
- }
88
- type TransportOutcome = {
89
- kind: "accepted";
90
- } | {
91
- kind: "retryable";
92
- retryAfterMs: number;
93
- } | {
94
- kind: "permanent_error";
95
- code: string;
96
- } | {
97
- kind: "network_error";
98
- };
99
- interface EventTransport {
100
- send(envelope: EventEnvelope): Promise<TransportOutcome>;
101
- loadRemoteConfig?(): Promise<RemoteSdkConfig | undefined>;
102
- }
103
- interface RemoteSdkConfig {
104
- enabled?: boolean;
105
- sampleRate?: number;
106
- errorTypeAllowlist?: string[];
107
- queueLimit?: number;
108
- retryBaseMs?: number;
109
- }
110
- interface RuntimeDependencies {
111
- transport: EventTransport;
112
- now: () => Date;
113
- random: () => number;
114
- createEventId: () => string;
115
- persistentQueue: PersistentEventQueue;
116
- sendLease: SendLease;
117
- installGlobalHandlers: (monitor: ErrorMonitor, transportPath: string) => () => void;
118
- }
119
- interface PersistentEventQueue {
120
- load(now: number): Promise<PersistedQueueItem[]>;
121
- put(item: PersistedQueueItem): Promise<void>;
122
- remove(ids: string[]): Promise<void>;
123
- clear(): Promise<void>;
124
- }
125
- interface PersistedQueueItem {
126
- id: string;
127
- event: ErrorEvent;
128
- enqueuedAt: number;
129
- bytes: number;
130
- }
131
- interface SendLease {
132
- acquire(now: number): boolean;
133
- release(): void;
134
- }
135
-
136
- export type { Breadcrumb as B, CaptureOptions as C, ErrorMonitor as E, IngestConfig as I, MonitorOptions as M, RuntimeDependencies as R, TransportOutcome as T, ContextMode as a, MonitorStatus as b, EventTransport as c, EventEnvelope as d };