@allstak/react 0.1.3 → 0.3.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 +95 -2
- package/dist/index.d.mts +672 -1
- package/dist/index.d.ts +672 -1
- package/dist/index.js +1839 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1825 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -10
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,16 +17,1848 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
21
31
|
var index_exports = {};
|
|
22
32
|
__export(index_exports, {
|
|
23
|
-
AllStak: () =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
AllStak: () => AllStak,
|
|
34
|
+
AllStakClient: () => AllStakClient,
|
|
35
|
+
AllStakErrorBoundary: () => AllStakErrorBoundary,
|
|
36
|
+
HttpRequestModule: () => HttpRequestModule,
|
|
37
|
+
INGEST_HOST: () => INGEST_HOST,
|
|
38
|
+
ReplayRecorder: () => ReplayRecorder,
|
|
39
|
+
SDK_NAME: () => SDK_NAME,
|
|
40
|
+
SDK_VERSION: () => SDK_VERSION,
|
|
41
|
+
Scope: () => Scope,
|
|
42
|
+
instrumentBrowserNavigation: () => instrumentBrowserNavigation,
|
|
43
|
+
instrumentConsole: () => instrumentConsole,
|
|
44
|
+
instrumentFetch: () => instrumentFetch,
|
|
45
|
+
instrumentNextRouter: () => instrumentNextRouter,
|
|
46
|
+
instrumentReactRouter: () => instrumentReactRouter,
|
|
47
|
+
useAllStak: () => useAllStak,
|
|
48
|
+
withAllStakProfiler: () => withAllStakProfiler
|
|
27
49
|
});
|
|
28
50
|
module.exports = __toCommonJS(index_exports);
|
|
29
|
-
var
|
|
51
|
+
var React = __toESM(require("react"));
|
|
52
|
+
|
|
53
|
+
// src/transport.ts
|
|
54
|
+
var REQUEST_TIMEOUT = 3e3;
|
|
55
|
+
var MAX_BUFFER = 100;
|
|
56
|
+
var HttpTransport = class {
|
|
57
|
+
constructor(baseUrl, apiKey) {
|
|
58
|
+
this.baseUrl = baseUrl;
|
|
59
|
+
this.apiKey = apiKey;
|
|
60
|
+
this.buffer = [];
|
|
61
|
+
this.flushing = false;
|
|
62
|
+
}
|
|
63
|
+
async send(path, payload) {
|
|
64
|
+
try {
|
|
65
|
+
await this.doFetch(path, payload);
|
|
66
|
+
await this.flushBuffer();
|
|
67
|
+
} catch {
|
|
68
|
+
if (this.buffer.length >= MAX_BUFFER) this.buffer.shift();
|
|
69
|
+
this.buffer.push({ path, payload });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async doFetch(path, payload) {
|
|
73
|
+
const url = `${this.baseUrl}${path}`;
|
|
74
|
+
const controller = new AbortController();
|
|
75
|
+
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
|
|
76
|
+
try {
|
|
77
|
+
const res = await fetch(url, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: {
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
"X-AllStak-Key": this.apiKey
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify(payload),
|
|
84
|
+
signal: controller.signal
|
|
85
|
+
});
|
|
86
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
87
|
+
} finally {
|
|
88
|
+
clearTimeout(timeoutId);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async flushBuffer() {
|
|
92
|
+
if (this.flushing || this.buffer.length === 0) return;
|
|
93
|
+
this.flushing = true;
|
|
94
|
+
try {
|
|
95
|
+
const items = this.buffer.splice(0, this.buffer.length);
|
|
96
|
+
for (const item of items) {
|
|
97
|
+
try {
|
|
98
|
+
await this.doFetch(item.path, item.payload);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} finally {
|
|
103
|
+
this.flushing = false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
getBufferSize() {
|
|
107
|
+
return this.buffer.length;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Wait for the in-flight retry-buffer to drain. Resolves `true` if the
|
|
111
|
+
* buffer empties within `timeoutMs` (default 2000ms), `false` otherwise.
|
|
112
|
+
* Useful at process exit / before navigation away.
|
|
113
|
+
*/
|
|
114
|
+
async flush(timeoutMs = 2e3) {
|
|
115
|
+
const deadline = Date.now() + timeoutMs;
|
|
116
|
+
await this.flushBuffer();
|
|
117
|
+
while (this.buffer.length > 0 || this.flushing) {
|
|
118
|
+
if (Date.now() >= deadline) return false;
|
|
119
|
+
await new Promise((r) => setTimeout(r, 25));
|
|
120
|
+
await this.flushBuffer();
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// src/stack.ts
|
|
127
|
+
var V8_FRAME_RE = /^\s*at\s+(?:(.+?)\s+\()?((?:.+?):(\d+):(\d+))\)?\s*$/;
|
|
128
|
+
var GECKO_FRAME_RE = /^\s*(?:(.*?)@)?(.+?):(\d+):(\d+)\s*$/;
|
|
129
|
+
var NODE_INTERNAL_RE = /^(node:|internal\/|node_modules\/)/;
|
|
130
|
+
function parseStack(stack) {
|
|
131
|
+
if (!stack || typeof stack !== "string") return [];
|
|
132
|
+
const frames = [];
|
|
133
|
+
for (const raw of stack.split("\n")) {
|
|
134
|
+
const line = raw.trim();
|
|
135
|
+
if (!line) continue;
|
|
136
|
+
let m = V8_FRAME_RE.exec(line);
|
|
137
|
+
if (m) {
|
|
138
|
+
const filename = stripQueryHash(m[2].replace(/:\d+:\d+$/, ""));
|
|
139
|
+
frames.push({
|
|
140
|
+
filename,
|
|
141
|
+
absPath: filename,
|
|
142
|
+
function: m[1] ? m[1].trim() : void 0,
|
|
143
|
+
lineno: parseInt(m[3], 10),
|
|
144
|
+
colno: parseInt(m[4], 10),
|
|
145
|
+
inApp: isInApp(filename)
|
|
146
|
+
});
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
m = GECKO_FRAME_RE.exec(line);
|
|
150
|
+
if (m && m[2]) {
|
|
151
|
+
const filename = stripQueryHash(m[2]);
|
|
152
|
+
frames.push({
|
|
153
|
+
filename,
|
|
154
|
+
absPath: filename,
|
|
155
|
+
function: m[1] ? m[1].trim() : void 0,
|
|
156
|
+
lineno: parseInt(m[3], 10),
|
|
157
|
+
colno: parseInt(m[4], 10),
|
|
158
|
+
inApp: isInApp(filename)
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return frames;
|
|
163
|
+
}
|
|
164
|
+
function stripQueryHash(url) {
|
|
165
|
+
const q = url.indexOf("?");
|
|
166
|
+
const h = url.indexOf("#");
|
|
167
|
+
let cut = url.length;
|
|
168
|
+
if (q >= 0) cut = Math.min(cut, q);
|
|
169
|
+
if (h >= 0) cut = Math.min(cut, h);
|
|
170
|
+
return url.slice(0, cut);
|
|
171
|
+
}
|
|
172
|
+
function isInApp(filename) {
|
|
173
|
+
if (!filename) return true;
|
|
174
|
+
if (NODE_INTERNAL_RE.test(filename)) return false;
|
|
175
|
+
if (filename.includes("/node_modules/")) return false;
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/auto-breadcrumbs.ts
|
|
180
|
+
var FETCH_FLAG = "__allstak_fetch_patched__";
|
|
181
|
+
var CONSOLE_FLAG = "__allstak_console_patched__";
|
|
182
|
+
function instrumentFetch(addBreadcrumb, ownBaseUrl) {
|
|
183
|
+
const g = globalThis;
|
|
184
|
+
if (typeof g.fetch !== "function") return;
|
|
185
|
+
if (g.fetch[FETCH_FLAG]) return;
|
|
186
|
+
const originalFetch = g.fetch;
|
|
187
|
+
const wrapped = async function(input, init) {
|
|
188
|
+
const method = (init?.method || input && typeof input === "object" && input.method || "GET").toUpperCase();
|
|
189
|
+
let url;
|
|
190
|
+
if (typeof input === "string") url = input;
|
|
191
|
+
else if (input && typeof input.href === "string") url = input.href;
|
|
192
|
+
else if (input && typeof input.url === "string") url = input.url;
|
|
193
|
+
else url = String(input);
|
|
194
|
+
const safePath = url.split("?")[0];
|
|
195
|
+
const isOwnIngest2 = !!(ownBaseUrl && url.startsWith(ownBaseUrl));
|
|
196
|
+
const start = Date.now();
|
|
197
|
+
try {
|
|
198
|
+
const response = await originalFetch.call(this, input, init);
|
|
199
|
+
const durationMs = Date.now() - start;
|
|
200
|
+
if (!isOwnIngest2) {
|
|
201
|
+
addBreadcrumb(
|
|
202
|
+
"http",
|
|
203
|
+
`${method} ${safePath} -> ${response.status}`,
|
|
204
|
+
response.status >= 400 ? "error" : "info",
|
|
205
|
+
{ method, url: safePath, statusCode: response.status, durationMs }
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
return response;
|
|
209
|
+
} catch (err) {
|
|
210
|
+
const durationMs = Date.now() - start;
|
|
211
|
+
if (!isOwnIngest2) {
|
|
212
|
+
addBreadcrumb("http", `${method} ${safePath} -> failed`, "error", {
|
|
213
|
+
method,
|
|
214
|
+
url: safePath,
|
|
215
|
+
error: String(err),
|
|
216
|
+
durationMs
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
throw err;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
wrapped[FETCH_FLAG] = true;
|
|
223
|
+
g.fetch = wrapped;
|
|
224
|
+
}
|
|
225
|
+
function instrumentConsole(addBreadcrumb) {
|
|
226
|
+
if (typeof console === "undefined") return;
|
|
227
|
+
if (console[CONSOLE_FLAG]) return;
|
|
228
|
+
const origWarn = console.warn;
|
|
229
|
+
const origError = console.error;
|
|
230
|
+
console.warn = function(...args) {
|
|
231
|
+
try {
|
|
232
|
+
addBreadcrumb("log", args.map(safeString).join(" "), "warn");
|
|
233
|
+
} catch {
|
|
234
|
+
}
|
|
235
|
+
return origWarn.apply(console, args);
|
|
236
|
+
};
|
|
237
|
+
console.error = function(...args) {
|
|
238
|
+
try {
|
|
239
|
+
addBreadcrumb("log", args.map(safeString).join(" "), "error");
|
|
240
|
+
} catch {
|
|
241
|
+
}
|
|
242
|
+
return origError.apply(console, args);
|
|
243
|
+
};
|
|
244
|
+
console[CONSOLE_FLAG] = true;
|
|
245
|
+
}
|
|
246
|
+
function safeString(v) {
|
|
247
|
+
if (v == null) return String(v);
|
|
248
|
+
if (typeof v === "string") return v;
|
|
249
|
+
if (v instanceof Error) return `${v.name}: ${v.message}`;
|
|
250
|
+
try {
|
|
251
|
+
return typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
252
|
+
} catch {
|
|
253
|
+
return Object.prototype.toString.call(v);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/navigation.ts
|
|
258
|
+
var FLAG = "__allstak_history_patched__";
|
|
259
|
+
function instrumentBrowserNavigation(addBreadcrumb) {
|
|
260
|
+
if (typeof window === "undefined" || typeof history === "undefined") return;
|
|
261
|
+
if (history[FLAG]) return;
|
|
262
|
+
const emit = (from, to) => {
|
|
263
|
+
if (from === to) return;
|
|
264
|
+
try {
|
|
265
|
+
addBreadcrumb("navigation", `${from} -> ${to}`, "info", { from, to });
|
|
266
|
+
} catch {
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
const origPush = history.pushState.bind(history);
|
|
270
|
+
const origReplace = history.replaceState.bind(history);
|
|
271
|
+
history.pushState = function(state, unused, url) {
|
|
272
|
+
const from = location.pathname + location.search;
|
|
273
|
+
const ret = origPush(state, unused, url ?? null);
|
|
274
|
+
emit(from, location.pathname + location.search);
|
|
275
|
+
return ret;
|
|
276
|
+
};
|
|
277
|
+
history.replaceState = function(state, unused, url) {
|
|
278
|
+
const from = location.pathname + location.search;
|
|
279
|
+
const ret = origReplace(state, unused, url ?? null);
|
|
280
|
+
emit(from, location.pathname + location.search);
|
|
281
|
+
return ret;
|
|
282
|
+
};
|
|
283
|
+
let last = location.pathname + location.search;
|
|
284
|
+
window.addEventListener("popstate", () => {
|
|
285
|
+
const next = location.pathname + location.search;
|
|
286
|
+
emit(last, next);
|
|
287
|
+
last = next;
|
|
288
|
+
});
|
|
289
|
+
history[FLAG] = true;
|
|
290
|
+
}
|
|
291
|
+
var lastReactRouterPath;
|
|
292
|
+
function instrumentReactRouter(location2, addBreadcrumb = (...args) => __defaultBreadcrumb(...args)) {
|
|
293
|
+
const next = `${location2.pathname}${location2.search ?? ""}`;
|
|
294
|
+
if (next === lastReactRouterPath) return;
|
|
295
|
+
const from = lastReactRouterPath ?? "<initial>";
|
|
296
|
+
lastReactRouterPath = next;
|
|
297
|
+
try {
|
|
298
|
+
addBreadcrumb("navigation", `${from} -> ${next}`, "info", { router: "react-router", from, to: next });
|
|
299
|
+
} catch {
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
var lastNextPath;
|
|
303
|
+
function instrumentNextRouter(url, addBreadcrumb = (...args) => __defaultBreadcrumb(...args)) {
|
|
304
|
+
if (url === lastNextPath) return;
|
|
305
|
+
const from = lastNextPath ?? "<initial>";
|
|
306
|
+
lastNextPath = url;
|
|
307
|
+
try {
|
|
308
|
+
addBreadcrumb("navigation", `${from} -> ${url}`, "info", { router: "next", from, to: url });
|
|
309
|
+
} catch {
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
var __defaultBreadcrumb = () => {
|
|
313
|
+
};
|
|
314
|
+
function __setDefaultBreadcrumbForwarder(fn) {
|
|
315
|
+
__defaultBreadcrumb = fn;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/scope.ts
|
|
319
|
+
var Scope = class {
|
|
320
|
+
constructor() {
|
|
321
|
+
this.tags = {};
|
|
322
|
+
this.extras = {};
|
|
323
|
+
this.contexts = {};
|
|
324
|
+
}
|
|
325
|
+
setUser(user) {
|
|
326
|
+
this.user = user;
|
|
327
|
+
return this;
|
|
328
|
+
}
|
|
329
|
+
setTag(key, value) {
|
|
330
|
+
this.tags[key] = value;
|
|
331
|
+
return this;
|
|
332
|
+
}
|
|
333
|
+
setTags(tags) {
|
|
334
|
+
Object.assign(this.tags, tags);
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
setExtra(key, value) {
|
|
338
|
+
this.extras[key] = value;
|
|
339
|
+
return this;
|
|
340
|
+
}
|
|
341
|
+
setExtras(extras) {
|
|
342
|
+
Object.assign(this.extras, extras);
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
setContext(name, ctx) {
|
|
346
|
+
if (ctx === null) delete this.contexts[name];
|
|
347
|
+
else this.contexts[name] = ctx;
|
|
348
|
+
return this;
|
|
349
|
+
}
|
|
350
|
+
setLevel(level) {
|
|
351
|
+
this.level = level;
|
|
352
|
+
return this;
|
|
353
|
+
}
|
|
354
|
+
setFingerprint(fingerprint) {
|
|
355
|
+
this.fingerprint = fingerprint && fingerprint.length > 0 ? fingerprint : void 0;
|
|
356
|
+
return this;
|
|
357
|
+
}
|
|
358
|
+
clear() {
|
|
359
|
+
this.user = void 0;
|
|
360
|
+
this.tags = {};
|
|
361
|
+
this.extras = {};
|
|
362
|
+
this.contexts = {};
|
|
363
|
+
this.fingerprint = void 0;
|
|
364
|
+
this.level = void 0;
|
|
365
|
+
return this;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
function mergeScopes(base, stack) {
|
|
369
|
+
const out = { ...base };
|
|
370
|
+
out.tags = { ...base.tags ?? {} };
|
|
371
|
+
out.extras = { ...base.extras ?? {} };
|
|
372
|
+
out.contexts = { ...base.contexts ?? {} };
|
|
373
|
+
for (const scope of stack) {
|
|
374
|
+
if (scope.user) out.user = scope.user;
|
|
375
|
+
Object.assign(out.tags, scope.tags);
|
|
376
|
+
Object.assign(out.extras, scope.extras);
|
|
377
|
+
Object.assign(out.contexts, scope.contexts);
|
|
378
|
+
if (scope.fingerprint) out.fingerprint = scope.fingerprint;
|
|
379
|
+
if (scope.level) out.level = scope.level;
|
|
380
|
+
}
|
|
381
|
+
return out;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/tracing.ts
|
|
385
|
+
var SPAN_INGEST_PATH = "/ingest/v1/spans";
|
|
386
|
+
var FLUSH_INTERVAL_MS = 5e3;
|
|
387
|
+
var BATCH_SIZE_THRESHOLD = 20;
|
|
388
|
+
function id() {
|
|
389
|
+
const hex = (n) => Math.floor(Math.random() * n).toString(16).padStart(1, "0");
|
|
390
|
+
const seg = (len) => Array.from({ length: len }, () => hex(16)).join("");
|
|
391
|
+
return `${seg(8)}-${seg(4)}-4${seg(3)}-${(8 + Math.floor(Math.random() * 4)).toString(16)}${seg(3)}-${seg(12)}`;
|
|
392
|
+
}
|
|
393
|
+
var Span = class {
|
|
394
|
+
constructor(_traceId, _spanId, _parentSpanId, _operation, _description, _service, _environment, _tags, _onFinish) {
|
|
395
|
+
this._traceId = _traceId;
|
|
396
|
+
this._spanId = _spanId;
|
|
397
|
+
this._parentSpanId = _parentSpanId;
|
|
398
|
+
this._operation = _operation;
|
|
399
|
+
this._description = _description;
|
|
400
|
+
this._service = _service;
|
|
401
|
+
this._environment = _environment;
|
|
402
|
+
this._tags = _tags;
|
|
403
|
+
this._onFinish = _onFinish;
|
|
404
|
+
this._finished = false;
|
|
405
|
+
this._data = "";
|
|
406
|
+
this._startTimeMillis = Date.now();
|
|
407
|
+
}
|
|
408
|
+
setTag(key, value) {
|
|
409
|
+
this._tags[key] = value;
|
|
410
|
+
return this;
|
|
411
|
+
}
|
|
412
|
+
setData(data) {
|
|
413
|
+
this._data = data;
|
|
414
|
+
return this;
|
|
415
|
+
}
|
|
416
|
+
setDescription(description) {
|
|
417
|
+
this._description = description;
|
|
418
|
+
return this;
|
|
419
|
+
}
|
|
420
|
+
finish(status = "ok") {
|
|
421
|
+
if (this._finished) return;
|
|
422
|
+
this._finished = true;
|
|
423
|
+
const endTimeMillis = Date.now();
|
|
424
|
+
this._onFinish({
|
|
425
|
+
traceId: this._traceId,
|
|
426
|
+
spanId: this._spanId,
|
|
427
|
+
parentSpanId: this._parentSpanId,
|
|
428
|
+
operation: this._operation,
|
|
429
|
+
description: this._description,
|
|
430
|
+
status,
|
|
431
|
+
durationMs: endTimeMillis - this._startTimeMillis,
|
|
432
|
+
startTimeMillis: this._startTimeMillis,
|
|
433
|
+
endTimeMillis,
|
|
434
|
+
service: this._service,
|
|
435
|
+
environment: this._environment,
|
|
436
|
+
tags: this._tags,
|
|
437
|
+
data: this._data
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
get traceId() {
|
|
441
|
+
return this._traceId;
|
|
442
|
+
}
|
|
443
|
+
get spanId() {
|
|
444
|
+
return this._spanId;
|
|
445
|
+
}
|
|
446
|
+
get isFinished() {
|
|
447
|
+
return this._finished;
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
var NoopSpan = class extends Span {
|
|
451
|
+
constructor(traceId, spanId) {
|
|
452
|
+
super(traceId, spanId, "", "", "", "", "", {}, () => {
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
finish() {
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
var TracingModule = class {
|
|
459
|
+
constructor(transport, opts) {
|
|
460
|
+
this.transport = transport;
|
|
461
|
+
this.opts = opts;
|
|
462
|
+
this.spans = [];
|
|
463
|
+
this.flushTimer = null;
|
|
464
|
+
this.currentTraceId = null;
|
|
465
|
+
this.spanStack = [];
|
|
466
|
+
this.destroyed = false;
|
|
467
|
+
}
|
|
468
|
+
/** Get (and lazily create) the active trace ID. */
|
|
469
|
+
getTraceId() {
|
|
470
|
+
if (!this.currentTraceId) this.currentTraceId = id();
|
|
471
|
+
return this.currentTraceId;
|
|
472
|
+
}
|
|
473
|
+
/** Override the active trace ID, e.g. from an inbound request header. */
|
|
474
|
+
setTraceId(traceId) {
|
|
475
|
+
this.currentTraceId = traceId;
|
|
476
|
+
}
|
|
477
|
+
/** Get the active span's ID, or null if no span is active. */
|
|
478
|
+
getCurrentSpanId() {
|
|
479
|
+
return this.spanStack.length > 0 ? this.spanStack[this.spanStack.length - 1].spanId : null;
|
|
480
|
+
}
|
|
481
|
+
/** Reset both the trace ID and the in-flight span stack. */
|
|
482
|
+
resetTrace() {
|
|
483
|
+
this.currentTraceId = null;
|
|
484
|
+
this.spanStack = [];
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Start a new span. The returned Span automatically inherits the active
|
|
488
|
+
* span as its parent. If `tracesSampleRate` drops this trace, returns a
|
|
489
|
+
* no-op Span so the call site doesn't have to null-check.
|
|
490
|
+
*/
|
|
491
|
+
startSpan(operation, options = {}) {
|
|
492
|
+
const traceId = this.getTraceId();
|
|
493
|
+
const spanId = id();
|
|
494
|
+
const parentSpanId = this.getCurrentSpanId() ?? "";
|
|
495
|
+
if (!this.passesSampleRate()) {
|
|
496
|
+
return new NoopSpan(traceId, spanId);
|
|
497
|
+
}
|
|
498
|
+
const span = new Span(
|
|
499
|
+
traceId,
|
|
500
|
+
spanId,
|
|
501
|
+
parentSpanId,
|
|
502
|
+
operation,
|
|
503
|
+
options.description ?? "",
|
|
504
|
+
this.opts.service ?? "",
|
|
505
|
+
this.opts.environment ?? "",
|
|
506
|
+
{ ...options.tags ?? {} },
|
|
507
|
+
(data) => this.enqueue(data, span)
|
|
508
|
+
);
|
|
509
|
+
this.spanStack.push(span);
|
|
510
|
+
return span;
|
|
511
|
+
}
|
|
512
|
+
passesSampleRate() {
|
|
513
|
+
const r = this.opts.tracesSampleRate;
|
|
514
|
+
if (typeof r !== "number" || r >= 1) return true;
|
|
515
|
+
if (r <= 0) return false;
|
|
516
|
+
return Math.random() < r;
|
|
517
|
+
}
|
|
518
|
+
enqueue(data, span) {
|
|
519
|
+
if (this.destroyed) return;
|
|
520
|
+
const idx = this.spanStack.lastIndexOf(span);
|
|
521
|
+
if (idx >= 0) this.spanStack.splice(idx, 1);
|
|
522
|
+
this.spans.push(data);
|
|
523
|
+
if (this.spans.length >= BATCH_SIZE_THRESHOLD) {
|
|
524
|
+
this.flush();
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
if (!this.flushTimer) {
|
|
528
|
+
this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
flush() {
|
|
532
|
+
if (this.spans.length === 0) return;
|
|
533
|
+
const batch = this.spans;
|
|
534
|
+
this.spans = [];
|
|
535
|
+
this.transport.send(SPAN_INGEST_PATH, { spans: batch });
|
|
536
|
+
}
|
|
537
|
+
destroy() {
|
|
538
|
+
this.destroyed = true;
|
|
539
|
+
if (this.flushTimer) {
|
|
540
|
+
clearInterval(this.flushTimer);
|
|
541
|
+
this.flushTimer = null;
|
|
542
|
+
}
|
|
543
|
+
this.flush();
|
|
544
|
+
this.currentTraceId = null;
|
|
545
|
+
this.spanStack = [];
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// src/replay.ts
|
|
550
|
+
var REPLAY_INGEST_PATH = "/ingest/v1/replay";
|
|
551
|
+
var FLUSH_INTERVAL_MS2 = 1e4;
|
|
552
|
+
var DEFAULT_MASK_ATTR = "data-allstak-mask";
|
|
553
|
+
var FLAG2 = "__allstak_replay_started__";
|
|
554
|
+
var ReplayRecorder = class {
|
|
555
|
+
constructor(transport, sessionId, addBreadcrumb, options = {}) {
|
|
556
|
+
this.transport = transport;
|
|
557
|
+
this.addBreadcrumb = addBreadcrumb;
|
|
558
|
+
this.buffer = [];
|
|
559
|
+
this.flushTimer = null;
|
|
560
|
+
this.observer = null;
|
|
561
|
+
this.inputListener = null;
|
|
562
|
+
this.destroyed = false;
|
|
563
|
+
this.sessionId = sessionId;
|
|
564
|
+
this.opts = {
|
|
565
|
+
enabled: options.enabled ?? true,
|
|
566
|
+
sampleRate: options.sampleRate ?? 0,
|
|
567
|
+
maskAllInputs: options.maskAllInputs ?? true,
|
|
568
|
+
maskAttribute: options.maskAttribute ?? DEFAULT_MASK_ATTR,
|
|
569
|
+
maxBufferedEvents: options.maxBufferedEvents ?? 200
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
start() {
|
|
573
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
574
|
+
if (!this.opts.enabled) return;
|
|
575
|
+
if (Math.random() >= this.opts.sampleRate) return;
|
|
576
|
+
if (document[FLAG2]) return;
|
|
577
|
+
document[FLAG2] = true;
|
|
578
|
+
this.push({
|
|
579
|
+
ts: Date.now(),
|
|
580
|
+
k: "snap",
|
|
581
|
+
data: { html: this.snapshotBody(), url: location.href }
|
|
582
|
+
});
|
|
583
|
+
this.observer = new MutationObserver((records) => {
|
|
584
|
+
for (const r of records) {
|
|
585
|
+
if (r.type === "childList") {
|
|
586
|
+
this.push({
|
|
587
|
+
ts: Date.now(),
|
|
588
|
+
k: "mut",
|
|
589
|
+
data: {
|
|
590
|
+
kind: "childList",
|
|
591
|
+
target: this.describePath(r.target),
|
|
592
|
+
added: Array.from(r.addedNodes).map((n) => this.describeNode(n)),
|
|
593
|
+
removed: r.removedNodes.length
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
} else if (r.type === "attributes") {
|
|
597
|
+
this.push({
|
|
598
|
+
ts: Date.now(),
|
|
599
|
+
k: "mut",
|
|
600
|
+
data: {
|
|
601
|
+
kind: "attr",
|
|
602
|
+
target: this.describePath(r.target),
|
|
603
|
+
name: r.attributeName,
|
|
604
|
+
value: this.safeAttribute(r.target, r.attributeName ?? "")
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
this.observer.observe(document.body ?? document.documentElement, {
|
|
611
|
+
childList: true,
|
|
612
|
+
attributes: true,
|
|
613
|
+
subtree: true
|
|
614
|
+
});
|
|
615
|
+
this.inputListener = (ev) => {
|
|
616
|
+
const target = ev.target;
|
|
617
|
+
if (!target || !("value" in target)) return;
|
|
618
|
+
const masked = this.maskInputValue(target);
|
|
619
|
+
this.push({
|
|
620
|
+
ts: Date.now(),
|
|
621
|
+
k: "input",
|
|
622
|
+
data: { target: this.describePath(target), value: masked, type: target.type }
|
|
623
|
+
});
|
|
624
|
+
};
|
|
625
|
+
document.addEventListener("input", this.inputListener, true);
|
|
626
|
+
this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS2);
|
|
627
|
+
try {
|
|
628
|
+
this.addBreadcrumb("default", "Replay recording started", "info", { sessionId: this.sessionId });
|
|
629
|
+
} catch {
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
destroy() {
|
|
633
|
+
this.destroyed = true;
|
|
634
|
+
if (this.observer) {
|
|
635
|
+
this.observer.disconnect();
|
|
636
|
+
this.observer = null;
|
|
637
|
+
}
|
|
638
|
+
if (this.inputListener) {
|
|
639
|
+
document.removeEventListener("input", this.inputListener, true);
|
|
640
|
+
this.inputListener = null;
|
|
641
|
+
}
|
|
642
|
+
if (this.flushTimer) {
|
|
643
|
+
clearInterval(this.flushTimer);
|
|
644
|
+
this.flushTimer = null;
|
|
645
|
+
}
|
|
646
|
+
this.flush();
|
|
647
|
+
if (typeof document !== "undefined") document[FLAG2] = false;
|
|
648
|
+
}
|
|
649
|
+
/** @internal — exposed for tests. */
|
|
650
|
+
getBuffer() {
|
|
651
|
+
return this.buffer;
|
|
652
|
+
}
|
|
653
|
+
push(ev) {
|
|
654
|
+
if (this.destroyed) return;
|
|
655
|
+
this.buffer.push(ev);
|
|
656
|
+
if (this.buffer.length >= this.opts.maxBufferedEvents) this.flush();
|
|
657
|
+
}
|
|
658
|
+
flush() {
|
|
659
|
+
if (this.buffer.length === 0) return;
|
|
660
|
+
const events = this.buffer;
|
|
661
|
+
this.buffer = [];
|
|
662
|
+
this.transport.send(REPLAY_INGEST_PATH, {
|
|
663
|
+
sessionId: this.sessionId,
|
|
664
|
+
events
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
// ── Sanitization helpers ──────────────────────────────────────────
|
|
668
|
+
snapshotBody() {
|
|
669
|
+
if (!document.body) return "";
|
|
670
|
+
const clone = document.body.cloneNode(true);
|
|
671
|
+
this.maskTree(clone);
|
|
672
|
+
return clone.outerHTML;
|
|
673
|
+
}
|
|
674
|
+
maskTree(root) {
|
|
675
|
+
if (this.opts.maskAttribute && root.hasAttribute?.(this.opts.maskAttribute)) {
|
|
676
|
+
root.textContent = "***";
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const tag = root.tagName?.toLowerCase();
|
|
680
|
+
if (tag === "input" || tag === "textarea" || tag === "select") {
|
|
681
|
+
root.value = this.maskInputValue(root);
|
|
682
|
+
root.setAttribute("value", root.value);
|
|
683
|
+
}
|
|
684
|
+
for (const child of Array.from(root.children)) this.maskTree(child);
|
|
685
|
+
}
|
|
686
|
+
maskInputValue(el) {
|
|
687
|
+
if (el.type === "password") return "***";
|
|
688
|
+
if (this.opts.maskAllInputs) return "***";
|
|
689
|
+
return String(el.value ?? "");
|
|
690
|
+
}
|
|
691
|
+
describeNode(node) {
|
|
692
|
+
if (node.nodeType === 1) {
|
|
693
|
+
return { type: "element", tag: node.tagName?.toLowerCase() };
|
|
694
|
+
}
|
|
695
|
+
if (node.nodeType === 3) {
|
|
696
|
+
return { type: "text", length: (node.nodeValue ?? "").length };
|
|
697
|
+
}
|
|
698
|
+
return { type: "other" };
|
|
699
|
+
}
|
|
700
|
+
describePath(el) {
|
|
701
|
+
if (!el || el.nodeType !== 1) return "";
|
|
702
|
+
const parts = [];
|
|
703
|
+
let cur = el;
|
|
704
|
+
let depth = 0;
|
|
705
|
+
while (cur && depth < 8) {
|
|
706
|
+
let p = cur.tagName.toLowerCase();
|
|
707
|
+
if (cur.id) {
|
|
708
|
+
p += `#${cur.id}`;
|
|
709
|
+
parts.unshift(p);
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
if (cur.classList?.length) p += "." + Array.from(cur.classList).slice(0, 2).join(".");
|
|
713
|
+
parts.unshift(p);
|
|
714
|
+
cur = cur.parentElement;
|
|
715
|
+
depth += 1;
|
|
716
|
+
}
|
|
717
|
+
return parts.join(">");
|
|
718
|
+
}
|
|
719
|
+
safeAttribute(el, name) {
|
|
720
|
+
if (name === "value" || name === "defaultValue") return "***";
|
|
721
|
+
return el.getAttribute(name) ?? "";
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
// src/debug-id.ts
|
|
726
|
+
function getRegistry() {
|
|
727
|
+
return globalThis._allstakDebugIds ?? {};
|
|
728
|
+
}
|
|
729
|
+
var cache = /* @__PURE__ */ new Map();
|
|
730
|
+
function resolveDebugId(filename) {
|
|
731
|
+
if (!filename) return void 0;
|
|
732
|
+
if (cache.has(filename)) return cache.get(filename);
|
|
733
|
+
const registry = getRegistry();
|
|
734
|
+
let id2 = registry[filename];
|
|
735
|
+
if (!id2) {
|
|
736
|
+
for (const key of Object.keys(registry)) {
|
|
737
|
+
if (filename.endsWith(key)) {
|
|
738
|
+
id2 = registry[key];
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
cache.set(filename, id2);
|
|
744
|
+
return id2;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// src/http-requests.ts
|
|
748
|
+
var INGEST_PATH = "/ingest/v1/http-requests";
|
|
749
|
+
var FLUSH_INTERVAL_MS3 = 5e3;
|
|
750
|
+
var BATCH_SIZE_THRESHOLD2 = 20;
|
|
751
|
+
var RECENT_FAILED_BUFFER_SIZE = 10;
|
|
752
|
+
function genTraceId() {
|
|
753
|
+
const hex = (n) => Math.floor(Math.random() * n).toString(16).padStart(1, "0");
|
|
754
|
+
const seg = (len) => Array.from({ length: len }, () => hex(16)).join("");
|
|
755
|
+
return `${seg(8)}-${seg(4)}-4${seg(3)}-${(8 + Math.floor(Math.random() * 4)).toString(16)}${seg(3)}-${seg(12)}`;
|
|
756
|
+
}
|
|
757
|
+
function splitHostPath(url) {
|
|
758
|
+
try {
|
|
759
|
+
const u = new URL(url);
|
|
760
|
+
return { host: u.host, path: u.pathname || "/" };
|
|
761
|
+
} catch {
|
|
762
|
+
return { host: "", path: url.split("?")[0] };
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
var HttpRequestModule = class {
|
|
766
|
+
constructor(transport) {
|
|
767
|
+
this.transport = transport;
|
|
768
|
+
this.queue = [];
|
|
769
|
+
this.recentFailed = [];
|
|
770
|
+
this.flushTimer = null;
|
|
771
|
+
this.destroyed = false;
|
|
772
|
+
this.defaults = {};
|
|
773
|
+
}
|
|
774
|
+
setDefaults(defaults) {
|
|
775
|
+
this.defaults = { ...this.defaults, ...defaults };
|
|
776
|
+
}
|
|
777
|
+
capture(ev) {
|
|
778
|
+
if (this.destroyed) return;
|
|
779
|
+
const { host, path } = splitHostPath(ev.url);
|
|
780
|
+
const item = {
|
|
781
|
+
type: "http_request",
|
|
782
|
+
traceId: ev.traceId ?? genTraceId(),
|
|
783
|
+
direction: "outbound",
|
|
784
|
+
method: (ev.method || "GET").toUpperCase(),
|
|
785
|
+
host,
|
|
786
|
+
path,
|
|
787
|
+
url: ev.url,
|
|
788
|
+
statusCode: ev.statusCode ?? 0,
|
|
789
|
+
durationMs: Math.max(0, Math.floor(ev.durationMs)),
|
|
790
|
+
requestSize: ev.requestSize,
|
|
791
|
+
responseSize: ev.responseSize,
|
|
792
|
+
requestBody: ev.requestBody,
|
|
793
|
+
responseBody: ev.responseBody,
|
|
794
|
+
requestHeaders: ev.requestHeaders,
|
|
795
|
+
responseHeaders: ev.responseHeaders,
|
|
796
|
+
error: ev.error,
|
|
797
|
+
environment: this.defaults.environment,
|
|
798
|
+
release: this.defaults.release,
|
|
799
|
+
dist: this.defaults.dist,
|
|
800
|
+
platform: this.defaults.platform,
|
|
801
|
+
"sdk.name": this.defaults.sdkName,
|
|
802
|
+
"sdk.version": this.defaults.sdkVersion,
|
|
803
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
804
|
+
};
|
|
805
|
+
this.queue.push(item);
|
|
806
|
+
const isFailed = item.statusCode >= 400 || !!item.error;
|
|
807
|
+
if (isFailed) {
|
|
808
|
+
this.recentFailed.push(item);
|
|
809
|
+
if (this.recentFailed.length > RECENT_FAILED_BUFFER_SIZE) this.recentFailed.shift();
|
|
810
|
+
}
|
|
811
|
+
if (this.queue.length >= BATCH_SIZE_THRESHOLD2) {
|
|
812
|
+
this.flush();
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (!this.flushTimer) this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS3);
|
|
816
|
+
}
|
|
817
|
+
/** Snapshot of the last failed requests for error-linking. Newest last. */
|
|
818
|
+
getRecentFailed() {
|
|
819
|
+
return this.recentFailed;
|
|
820
|
+
}
|
|
821
|
+
flush() {
|
|
822
|
+
if (this.queue.length === 0) return;
|
|
823
|
+
const batch = this.queue;
|
|
824
|
+
this.queue = [];
|
|
825
|
+
const payload = { requests: batch };
|
|
826
|
+
this.transport.send(INGEST_PATH, payload);
|
|
827
|
+
}
|
|
828
|
+
destroy() {
|
|
829
|
+
this.destroyed = true;
|
|
830
|
+
if (this.flushTimer) {
|
|
831
|
+
clearInterval(this.flushTimer);
|
|
832
|
+
this.flushTimer = null;
|
|
833
|
+
}
|
|
834
|
+
this.flush();
|
|
835
|
+
this.recentFailed = [];
|
|
836
|
+
}
|
|
837
|
+
/** @internal — for tests. */
|
|
838
|
+
getQueueSize() {
|
|
839
|
+
return this.queue.length;
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
// src/http-redact.ts
|
|
844
|
+
var ALWAYS_REDACT_HEADERS = /* @__PURE__ */ new Set([
|
|
845
|
+
"authorization",
|
|
846
|
+
"cookie",
|
|
847
|
+
"set-cookie",
|
|
848
|
+
"x-api-key",
|
|
849
|
+
"x-auth-token",
|
|
850
|
+
"proxy-authorization"
|
|
851
|
+
]);
|
|
852
|
+
var ALWAYS_REDACT_QUERY = /* @__PURE__ */ new Set([
|
|
853
|
+
"token",
|
|
854
|
+
"password",
|
|
855
|
+
"api_key",
|
|
856
|
+
"apikey",
|
|
857
|
+
"authorization",
|
|
858
|
+
"auth",
|
|
859
|
+
"secret",
|
|
860
|
+
"access_token",
|
|
861
|
+
"refresh_token",
|
|
862
|
+
"session",
|
|
863
|
+
"sessionid",
|
|
864
|
+
"jwt"
|
|
865
|
+
]);
|
|
866
|
+
var REDACTED = "[REDACTED]";
|
|
867
|
+
function shouldCaptureUrl(url, opts) {
|
|
868
|
+
if (!url) return false;
|
|
869
|
+
const lower = url.toLowerCase();
|
|
870
|
+
if (opts.allowedUrls && opts.allowedUrls.length > 0) {
|
|
871
|
+
return opts.allowedUrls.some((p) => matches(p, url, lower));
|
|
872
|
+
}
|
|
873
|
+
if (opts.ignoredUrls && opts.ignoredUrls.length > 0) {
|
|
874
|
+
return !opts.ignoredUrls.some((p) => matches(p, url, lower));
|
|
875
|
+
}
|
|
876
|
+
return true;
|
|
877
|
+
}
|
|
878
|
+
function matches(pattern, url, lower) {
|
|
879
|
+
if (pattern instanceof RegExp) return pattern.test(url);
|
|
880
|
+
if (typeof pattern !== "string") return false;
|
|
881
|
+
return lower.includes(pattern.toLowerCase());
|
|
882
|
+
}
|
|
883
|
+
function redactUrl(url, opts) {
|
|
884
|
+
if (!url) return url;
|
|
885
|
+
const extra = (opts.redactQueryParams ?? []).map((s) => s.toLowerCase());
|
|
886
|
+
const redactSet = /* @__PURE__ */ new Set([...ALWAYS_REDACT_QUERY, ...extra]);
|
|
887
|
+
let parsed = null;
|
|
888
|
+
try {
|
|
889
|
+
parsed = new URL(url);
|
|
890
|
+
} catch {
|
|
891
|
+
}
|
|
892
|
+
if (parsed) {
|
|
893
|
+
const params = parsed.searchParams;
|
|
894
|
+
let mutated = false;
|
|
895
|
+
const keysToRedact = [];
|
|
896
|
+
params.forEach((_v, k) => {
|
|
897
|
+
if (redactSet.has(k.toLowerCase())) keysToRedact.push(k);
|
|
898
|
+
});
|
|
899
|
+
for (const k of keysToRedact) {
|
|
900
|
+
params.set(k, REDACTED);
|
|
901
|
+
mutated = true;
|
|
902
|
+
}
|
|
903
|
+
if (mutated) parsed.search = params.toString();
|
|
904
|
+
return parsed.toString();
|
|
905
|
+
}
|
|
906
|
+
const qIdx = url.indexOf("?");
|
|
907
|
+
if (qIdx < 0) return url;
|
|
908
|
+
const head = url.slice(0, qIdx);
|
|
909
|
+
const queryAndHash = url.slice(qIdx + 1);
|
|
910
|
+
const hashIdx = queryAndHash.indexOf("#");
|
|
911
|
+
const query = hashIdx < 0 ? queryAndHash : queryAndHash.slice(0, hashIdx);
|
|
912
|
+
const hash = hashIdx < 0 ? "" : queryAndHash.slice(hashIdx);
|
|
913
|
+
const parts = query.split("&").map((pair) => {
|
|
914
|
+
const eq = pair.indexOf("=");
|
|
915
|
+
if (eq < 0) return pair;
|
|
916
|
+
const key = pair.slice(0, eq);
|
|
917
|
+
return redactSet.has(key.toLowerCase()) ? `${key}=${REDACTED}` : pair;
|
|
918
|
+
});
|
|
919
|
+
return `${head}?${parts.join("&")}${hash ? "#" + hash : ""}`;
|
|
920
|
+
}
|
|
921
|
+
function sanitizeHeaders(headers, opts) {
|
|
922
|
+
if (!opts.captureHeaders) return void 0;
|
|
923
|
+
if (!headers) return {};
|
|
924
|
+
const extra = (opts.redactHeaders ?? []).map((s) => s.toLowerCase());
|
|
925
|
+
const redactSet = /* @__PURE__ */ new Set([...ALWAYS_REDACT_HEADERS, ...extra]);
|
|
926
|
+
const out = {};
|
|
927
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
928
|
+
if (v == null) continue;
|
|
929
|
+
const lower = k.toLowerCase();
|
|
930
|
+
out[lower] = redactSet.has(lower) ? REDACTED : Array.isArray(v) ? v.join(", ") : String(v);
|
|
931
|
+
}
|
|
932
|
+
return out;
|
|
933
|
+
}
|
|
934
|
+
function captureBody(body, enabled, maxBodyBytes) {
|
|
935
|
+
if (!enabled) return void 0;
|
|
936
|
+
if (body == null) return void 0;
|
|
937
|
+
let str;
|
|
938
|
+
if (typeof body === "string") str = body;
|
|
939
|
+
else if (typeof body === "number" || typeof body === "boolean") str = String(body);
|
|
940
|
+
else if (typeof body === "object") {
|
|
941
|
+
const tag = Object.prototype.toString.call(body);
|
|
942
|
+
if (tag !== "[object Object]" && tag !== "[object Array]") return "<binary>";
|
|
943
|
+
try {
|
|
944
|
+
str = JSON.stringify(body);
|
|
945
|
+
} catch {
|
|
946
|
+
return "<unserializable>";
|
|
947
|
+
}
|
|
948
|
+
} else {
|
|
949
|
+
return "<binary>";
|
|
950
|
+
}
|
|
951
|
+
if (str.length > maxBodyBytes) return str.slice(0, maxBodyBytes) + "\u2026[truncated]";
|
|
952
|
+
return str;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// src/http-instrumentation.ts
|
|
956
|
+
var FETCH_FLAG2 = "__allstak_http_fetch_patched__";
|
|
957
|
+
var XHR_FLAG = "__allstak_http_xhr_patched__";
|
|
958
|
+
var AXIOS_FLAG = /* @__PURE__ */ Symbol.for("allstak.http.axios.instrumented");
|
|
959
|
+
var DEFAULT_MAX_BODY = 4096;
|
|
960
|
+
var _currentModule = null;
|
|
961
|
+
var _currentOpts = null;
|
|
962
|
+
function currentModule() {
|
|
963
|
+
return _currentModule;
|
|
964
|
+
}
|
|
965
|
+
function currentOpts() {
|
|
966
|
+
return _currentOpts;
|
|
967
|
+
}
|
|
968
|
+
function safeCapture(ev) {
|
|
969
|
+
try {
|
|
970
|
+
currentModule()?.capture(ev);
|
|
971
|
+
} catch {
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
function bind(opts, ownIngestHost) {
|
|
975
|
+
return {
|
|
976
|
+
captureRequestBody: opts.captureRequestBody ?? false,
|
|
977
|
+
captureResponseBody: opts.captureResponseBody ?? false,
|
|
978
|
+
captureHeaders: opts.captureHeaders ?? false,
|
|
979
|
+
redactHeaders: opts.redactHeaders ?? [],
|
|
980
|
+
redactQueryParams: opts.redactQueryParams ?? [],
|
|
981
|
+
ignoredUrls: opts.ignoredUrls ?? [],
|
|
982
|
+
allowedUrls: opts.allowedUrls ?? [],
|
|
983
|
+
maxBodyBytes: opts.maxBodyBytes ?? DEFAULT_MAX_BODY,
|
|
984
|
+
ownIngestPrefix: ownIngestHost.replace(/\/$/, "")
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
function isOwnIngest(url, prefix) {
|
|
988
|
+
return !!prefix && url.startsWith(prefix);
|
|
989
|
+
}
|
|
990
|
+
function urlString(input) {
|
|
991
|
+
if (typeof input === "string") return input;
|
|
992
|
+
if (input && typeof input.href === "string") return input.href;
|
|
993
|
+
if (input && typeof input.url === "string") return input.url;
|
|
994
|
+
return String(input);
|
|
995
|
+
}
|
|
996
|
+
function safeByteLength(s) {
|
|
997
|
+
if (s == null) return void 0;
|
|
998
|
+
if (typeof s === "string") {
|
|
999
|
+
let n = 0;
|
|
1000
|
+
for (let i = 0; i < s.length; i++) {
|
|
1001
|
+
const c = s.charCodeAt(i);
|
|
1002
|
+
if (c < 128) n += 1;
|
|
1003
|
+
else if (c < 2048) n += 2;
|
|
1004
|
+
else if (c >= 55296 && c <= 56319) {
|
|
1005
|
+
n += 4;
|
|
1006
|
+
i += 1;
|
|
1007
|
+
} else n += 3;
|
|
1008
|
+
}
|
|
1009
|
+
return n;
|
|
1010
|
+
}
|
|
1011
|
+
return void 0;
|
|
1012
|
+
}
|
|
1013
|
+
function headersToObject(h) {
|
|
1014
|
+
if (!h) return {};
|
|
1015
|
+
if (typeof h.entries === "function") {
|
|
1016
|
+
const out = {};
|
|
1017
|
+
for (const [k, v] of h.entries()) out[String(k).toLowerCase()] = String(v);
|
|
1018
|
+
return out;
|
|
1019
|
+
}
|
|
1020
|
+
if (typeof h === "object") {
|
|
1021
|
+
const out = {};
|
|
1022
|
+
for (const [k, v] of Object.entries(h)) {
|
|
1023
|
+
if (v == null) continue;
|
|
1024
|
+
out[String(k).toLowerCase()] = Array.isArray(v) ? v.join(", ") : String(v);
|
|
1025
|
+
}
|
|
1026
|
+
return out;
|
|
1027
|
+
}
|
|
1028
|
+
return {};
|
|
1029
|
+
}
|
|
1030
|
+
function patchFetch() {
|
|
1031
|
+
const g = globalThis;
|
|
1032
|
+
if (typeof g.fetch !== "function") return;
|
|
1033
|
+
if (g.fetch[FETCH_FLAG2]) return;
|
|
1034
|
+
const original = g.fetch;
|
|
1035
|
+
const wrapped = async function(input, init) {
|
|
1036
|
+
const opts = currentOpts();
|
|
1037
|
+
if (!opts || !currentModule()) {
|
|
1038
|
+
return original.call(this, input, init);
|
|
1039
|
+
}
|
|
1040
|
+
const rawUrl = urlString(input);
|
|
1041
|
+
const sanitizedUrl = redactUrl(rawUrl, opts);
|
|
1042
|
+
const method = (init?.method || input && typeof input === "object" && input.method || "GET").toUpperCase();
|
|
1043
|
+
if (isOwnIngest(rawUrl, opts.ownIngestPrefix) || !shouldCaptureUrl(rawUrl, opts)) {
|
|
1044
|
+
return original.call(this, input, init);
|
|
1045
|
+
}
|
|
1046
|
+
const start = Date.now();
|
|
1047
|
+
const reqHeaders = sanitizeHeaders(headersToObject(init?.headers ?? (input && input.headers)), opts);
|
|
1048
|
+
const reqBody = captureBody(init?.body, opts.captureRequestBody, opts.maxBodyBytes);
|
|
1049
|
+
const reqSize = safeByteLength(typeof init?.body === "string" ? init.body : void 0);
|
|
1050
|
+
let response;
|
|
1051
|
+
try {
|
|
1052
|
+
response = await original.call(this, input, init);
|
|
1053
|
+
} catch (err) {
|
|
1054
|
+
safeCapture({
|
|
1055
|
+
type: "http_request",
|
|
1056
|
+
method,
|
|
1057
|
+
url: sanitizedUrl,
|
|
1058
|
+
statusCode: 0,
|
|
1059
|
+
durationMs: Date.now() - start,
|
|
1060
|
+
requestBody: reqBody,
|
|
1061
|
+
requestHeaders: reqHeaders,
|
|
1062
|
+
requestSize: reqSize,
|
|
1063
|
+
error: String(err?.message ?? err)
|
|
1064
|
+
});
|
|
1065
|
+
throw err;
|
|
1066
|
+
}
|
|
1067
|
+
const durationMs = Date.now() - start;
|
|
1068
|
+
let respBody;
|
|
1069
|
+
let respSize;
|
|
1070
|
+
let respHeaders;
|
|
1071
|
+
try {
|
|
1072
|
+
respHeaders = sanitizeHeaders(headersToObject(response.headers), opts);
|
|
1073
|
+
const lenHeader = typeof response.headers?.get === "function" ? response.headers.get("content-length") : null;
|
|
1074
|
+
if (lenHeader) respSize = parseInt(lenHeader, 10) || void 0;
|
|
1075
|
+
if (opts.captureResponseBody && typeof response.clone === "function") {
|
|
1076
|
+
try {
|
|
1077
|
+
const cloned = response.clone();
|
|
1078
|
+
const text = await cloned.text();
|
|
1079
|
+
respBody = captureBody(text, true, opts.maxBodyBytes);
|
|
1080
|
+
if (respSize == null) respSize = safeByteLength(text);
|
|
1081
|
+
} catch {
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
} catch {
|
|
1085
|
+
}
|
|
1086
|
+
safeCapture({
|
|
1087
|
+
type: "http_request",
|
|
1088
|
+
method,
|
|
1089
|
+
url: sanitizedUrl,
|
|
1090
|
+
statusCode: response.status,
|
|
1091
|
+
durationMs,
|
|
1092
|
+
requestBody: reqBody,
|
|
1093
|
+
requestHeaders: reqHeaders,
|
|
1094
|
+
requestSize: reqSize,
|
|
1095
|
+
responseBody: respBody,
|
|
1096
|
+
responseHeaders: respHeaders,
|
|
1097
|
+
responseSize: respSize
|
|
1098
|
+
});
|
|
1099
|
+
return response;
|
|
1100
|
+
};
|
|
1101
|
+
wrapped[FETCH_FLAG2] = true;
|
|
1102
|
+
g.fetch = wrapped;
|
|
1103
|
+
}
|
|
1104
|
+
function patchXhr() {
|
|
1105
|
+
const g = globalThis;
|
|
1106
|
+
const X = g.XMLHttpRequest;
|
|
1107
|
+
if (!X || X.prototype[XHR_FLAG]) return;
|
|
1108
|
+
const origOpen = X.prototype.open;
|
|
1109
|
+
const origSend = X.prototype.send;
|
|
1110
|
+
const origSetRequestHeader = X.prototype.setRequestHeader;
|
|
1111
|
+
X.prototype.open = function(method, url, ...rest) {
|
|
1112
|
+
this.__allstak_method__ = method;
|
|
1113
|
+
this.__allstak_url__ = url;
|
|
1114
|
+
this.__allstak_headers__ = {};
|
|
1115
|
+
return origOpen.call(this, method, url, ...rest);
|
|
1116
|
+
};
|
|
1117
|
+
X.prototype.setRequestHeader = function(name, value) {
|
|
1118
|
+
try {
|
|
1119
|
+
this.__allstak_headers__[String(name).toLowerCase()] = String(value);
|
|
1120
|
+
} catch {
|
|
1121
|
+
}
|
|
1122
|
+
return origSetRequestHeader.call(this, name, value);
|
|
1123
|
+
};
|
|
1124
|
+
X.prototype.send = function(body) {
|
|
1125
|
+
const opts = currentOpts();
|
|
1126
|
+
if (!opts || !currentModule()) return origSend.call(this, body);
|
|
1127
|
+
const start = Date.now();
|
|
1128
|
+
const method = String(this.__allstak_method__ || "GET").toUpperCase();
|
|
1129
|
+
const rawUrl = String(this.__allstak_url__ || "");
|
|
1130
|
+
const sanitizedUrl = redactUrl(rawUrl, opts);
|
|
1131
|
+
if (isOwnIngest(rawUrl, opts.ownIngestPrefix) || !shouldCaptureUrl(rawUrl, opts)) {
|
|
1132
|
+
return origSend.call(this, body);
|
|
1133
|
+
}
|
|
1134
|
+
const reqHeaders = sanitizeHeaders(this.__allstak_headers__, opts);
|
|
1135
|
+
const reqBody = captureBody(body, opts.captureRequestBody, opts.maxBodyBytes);
|
|
1136
|
+
const reqSize = safeByteLength(typeof body === "string" ? body : void 0);
|
|
1137
|
+
const finish = (statusCode, error) => {
|
|
1138
|
+
const durationMs = Date.now() - start;
|
|
1139
|
+
let respHeaders;
|
|
1140
|
+
let respBody;
|
|
1141
|
+
let respSize;
|
|
1142
|
+
try {
|
|
1143
|
+
const liveOpts = currentOpts() ?? opts;
|
|
1144
|
+
if (liveOpts.captureHeaders && typeof this.getAllResponseHeaders === "function") {
|
|
1145
|
+
const raw = this.getAllResponseHeaders() || "";
|
|
1146
|
+
const dict = {};
|
|
1147
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
1148
|
+
const idx = line.indexOf(":");
|
|
1149
|
+
if (idx > 0) dict[line.slice(0, idx).trim().toLowerCase()] = line.slice(idx + 1).trim();
|
|
1150
|
+
}
|
|
1151
|
+
respHeaders = sanitizeHeaders(dict, liveOpts);
|
|
1152
|
+
}
|
|
1153
|
+
if (liveOpts.captureResponseBody) {
|
|
1154
|
+
const text = this.responseText;
|
|
1155
|
+
if (typeof text === "string") {
|
|
1156
|
+
respBody = captureBody(text, true, liveOpts.maxBodyBytes);
|
|
1157
|
+
respSize = safeByteLength(text);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
} catch {
|
|
1161
|
+
}
|
|
1162
|
+
safeCapture({
|
|
1163
|
+
type: "http_request",
|
|
1164
|
+
method,
|
|
1165
|
+
url: sanitizedUrl,
|
|
1166
|
+
statusCode,
|
|
1167
|
+
durationMs,
|
|
1168
|
+
requestBody: reqBody,
|
|
1169
|
+
requestHeaders: reqHeaders,
|
|
1170
|
+
requestSize: reqSize,
|
|
1171
|
+
responseBody: respBody,
|
|
1172
|
+
responseHeaders: respHeaders,
|
|
1173
|
+
responseSize: respSize,
|
|
1174
|
+
error
|
|
1175
|
+
});
|
|
1176
|
+
};
|
|
1177
|
+
this.addEventListener?.("load", () => finish(this.status || 0));
|
|
1178
|
+
this.addEventListener?.("error", () => finish(0, "network"));
|
|
1179
|
+
this.addEventListener?.("abort", () => finish(0, "abort"));
|
|
1180
|
+
this.addEventListener?.("timeout", () => finish(0, "timeout"));
|
|
1181
|
+
return origSend.call(this, body);
|
|
1182
|
+
};
|
|
1183
|
+
X.prototype[XHR_FLAG] = true;
|
|
1184
|
+
}
|
|
1185
|
+
function instrumentAxiosInstance(axiosInstance, module2, opts) {
|
|
1186
|
+
if (!axiosInstance || typeof axiosInstance.interceptors !== "object") return axiosInstance;
|
|
1187
|
+
if (axiosInstance[AXIOS_FLAG]) return axiosInstance;
|
|
1188
|
+
axiosInstance[AXIOS_FLAG] = true;
|
|
1189
|
+
const reqStarts = /* @__PURE__ */ new WeakMap();
|
|
1190
|
+
axiosInstance.interceptors.request.use((config) => {
|
|
1191
|
+
try {
|
|
1192
|
+
const rawUrl = (config.baseURL ? config.baseURL.replace(/\/$/, "") : "") + (config.url || "");
|
|
1193
|
+
reqStarts.set(config, {
|
|
1194
|
+
start: Date.now(),
|
|
1195
|
+
method: String(config.method || "GET").toUpperCase(),
|
|
1196
|
+
rawUrl
|
|
1197
|
+
});
|
|
1198
|
+
} catch {
|
|
1199
|
+
}
|
|
1200
|
+
return config;
|
|
1201
|
+
});
|
|
1202
|
+
const finalize = (cfg, statusCode, response, error) => {
|
|
1203
|
+
const meta = reqStarts.get(cfg);
|
|
1204
|
+
if (!meta) return;
|
|
1205
|
+
reqStarts.delete(cfg);
|
|
1206
|
+
if (isOwnIngest(meta.rawUrl, opts.ownIngestPrefix)) return;
|
|
1207
|
+
if (!shouldCaptureUrl(meta.rawUrl, opts)) return;
|
|
1208
|
+
const sanitizedUrl = redactUrl(meta.rawUrl, opts);
|
|
1209
|
+
const reqHeaders = sanitizeHeaders(headersToObject(cfg.headers), opts);
|
|
1210
|
+
const reqBody = captureBody(cfg.data, opts.captureRequestBody, opts.maxBodyBytes);
|
|
1211
|
+
const respHeaders = sanitizeHeaders(headersToObject(response?.headers), opts);
|
|
1212
|
+
const respBody = captureBody(response?.data, opts.captureResponseBody, opts.maxBodyBytes);
|
|
1213
|
+
try {
|
|
1214
|
+
module2.capture({
|
|
1215
|
+
type: "http_request",
|
|
1216
|
+
method: meta.method,
|
|
1217
|
+
url: sanitizedUrl,
|
|
1218
|
+
statusCode,
|
|
1219
|
+
durationMs: Date.now() - meta.start,
|
|
1220
|
+
requestBody: reqBody,
|
|
1221
|
+
requestHeaders: reqHeaders,
|
|
1222
|
+
responseBody: respBody,
|
|
1223
|
+
responseHeaders: respHeaders,
|
|
1224
|
+
error
|
|
1225
|
+
});
|
|
1226
|
+
} catch {
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
axiosInstance.interceptors.response.use(
|
|
1230
|
+
(response) => {
|
|
1231
|
+
finalize(response.config, response.status);
|
|
1232
|
+
return response;
|
|
1233
|
+
},
|
|
1234
|
+
(err) => {
|
|
1235
|
+
const cfg = err?.config;
|
|
1236
|
+
const status = err?.response?.status ?? 0;
|
|
1237
|
+
finalize(cfg, status, err?.response, String(err?.message ?? err));
|
|
1238
|
+
throw err;
|
|
1239
|
+
}
|
|
1240
|
+
);
|
|
1241
|
+
return axiosInstance;
|
|
1242
|
+
}
|
|
1243
|
+
function tryAutoInstrumentAxios(module2, opts) {
|
|
1244
|
+
try {
|
|
1245
|
+
const axios = globalThis.require?.("axios") ?? null;
|
|
1246
|
+
if (axios) instrumentAxiosInstance(axios.default ?? axios, module2, opts);
|
|
1247
|
+
} catch {
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
function installHttpInstrumentation(module2, options, ownIngestHost) {
|
|
1251
|
+
const bound = bind(options, ownIngestHost);
|
|
1252
|
+
_currentModule = module2;
|
|
1253
|
+
_currentOpts = bound;
|
|
1254
|
+
try {
|
|
1255
|
+
patchFetch();
|
|
1256
|
+
} catch {
|
|
1257
|
+
}
|
|
1258
|
+
try {
|
|
1259
|
+
patchXhr();
|
|
1260
|
+
} catch {
|
|
1261
|
+
}
|
|
1262
|
+
try {
|
|
1263
|
+
tryAutoInstrumentAxios(module2, bound);
|
|
1264
|
+
} catch {
|
|
1265
|
+
}
|
|
1266
|
+
return {
|
|
1267
|
+
instrumentAxios: (axios) => instrumentAxiosInstance(axios, module2, bound)
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// src/client.ts
|
|
1272
|
+
var INGEST_HOST = "https://api.allstak.sa";
|
|
1273
|
+
var SDK_NAME = "allstak-react";
|
|
1274
|
+
var SDK_VERSION = "0.3.0";
|
|
1275
|
+
var ERRORS_PATH = "/ingest/v1/errors";
|
|
1276
|
+
var LOGS_PATH = "/ingest/v1/logs";
|
|
1277
|
+
var VALID_BREADCRUMB_TYPES = /* @__PURE__ */ new Set(["http", "log", "ui", "navigation", "query", "default"]);
|
|
1278
|
+
var VALID_BREADCRUMB_LEVELS = /* @__PURE__ */ new Set(["info", "warn", "error", "debug"]);
|
|
1279
|
+
var DEFAULT_MAX_BREADCRUMBS = 50;
|
|
1280
|
+
function frameToString(f) {
|
|
1281
|
+
const fn = f.function && f.function.length > 0 ? f.function : "<anonymous>";
|
|
1282
|
+
const file = f.filename || f.absPath || "<anonymous>";
|
|
1283
|
+
return ` at ${fn} (${file}:${f.lineno ?? 0}:${f.colno ?? 0})`;
|
|
1284
|
+
}
|
|
1285
|
+
function generateId() {
|
|
1286
|
+
const hex = (n) => Math.floor(Math.random() * n).toString(16).padStart(1, "0");
|
|
1287
|
+
const seg = (len) => Array.from({ length: len }, () => hex(16)).join("");
|
|
1288
|
+
return `${seg(8)}-${seg(4)}-4${seg(3)}-${(8 + Math.floor(Math.random() * 4)).toString(16)}${seg(3)}-${seg(12)}`;
|
|
1289
|
+
}
|
|
1290
|
+
function browserRequestContext() {
|
|
1291
|
+
if (typeof window === "undefined" || typeof location === "undefined") return void 0;
|
|
1292
|
+
return {
|
|
1293
|
+
method: "GET",
|
|
1294
|
+
path: location.pathname || "/",
|
|
1295
|
+
host: location.host || "",
|
|
1296
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
var AllStakClient = class {
|
|
1300
|
+
constructor(config) {
|
|
1301
|
+
this.breadcrumbs = [];
|
|
1302
|
+
this.scopeStack = [];
|
|
1303
|
+
this.replay = null;
|
|
1304
|
+
this.httpRequests = null;
|
|
1305
|
+
this._instrumentAxios = null;
|
|
1306
|
+
this.onErrorHandler = null;
|
|
1307
|
+
this.onRejectionHandler = null;
|
|
1308
|
+
if (!config.apiKey) throw new Error("AllStak: config.apiKey is required");
|
|
1309
|
+
this.config = { ...config };
|
|
1310
|
+
if (!this.config.environment) this.config.environment = "production";
|
|
1311
|
+
if (!this.config.sdkName) this.config.sdkName = SDK_NAME;
|
|
1312
|
+
if (!this.config.sdkVersion) this.config.sdkVersion = SDK_VERSION;
|
|
1313
|
+
if (!this.config.platform) this.config.platform = "browser";
|
|
1314
|
+
this.sessionId = generateId();
|
|
1315
|
+
this.maxBreadcrumbs = config.maxBreadcrumbs ?? DEFAULT_MAX_BREADCRUMBS;
|
|
1316
|
+
const baseUrl = (config.host ?? INGEST_HOST).replace(/\/$/, "");
|
|
1317
|
+
this.transport = new HttpTransport(baseUrl, config.apiKey);
|
|
1318
|
+
this.tracing = new TracingModule(this.transport, {
|
|
1319
|
+
service: config.service ?? config.release ?? "",
|
|
1320
|
+
environment: this.config.environment ?? "production",
|
|
1321
|
+
tracesSampleRate: config.tracesSampleRate
|
|
1322
|
+
});
|
|
1323
|
+
if (config.autoCaptureBrowserErrors !== false && typeof window !== "undefined") {
|
|
1324
|
+
this.installBrowserHandlers();
|
|
1325
|
+
}
|
|
1326
|
+
if (config.autoBreadcrumbsFetch !== false) {
|
|
1327
|
+
try {
|
|
1328
|
+
instrumentFetch(safeAddBreadcrumb, baseUrl);
|
|
1329
|
+
} catch {
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
if (config.autoBreadcrumbsConsole !== false) {
|
|
1333
|
+
try {
|
|
1334
|
+
instrumentConsole(safeAddBreadcrumb);
|
|
1335
|
+
} catch {
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
if (config.autoBreadcrumbsNavigation !== false) {
|
|
1339
|
+
try {
|
|
1340
|
+
instrumentBrowserNavigation(safeAddBreadcrumb);
|
|
1341
|
+
} catch {
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
if (config.replay && (config.replay.enabled ?? true)) {
|
|
1345
|
+
try {
|
|
1346
|
+
this.replay = new ReplayRecorder(this.transport, this.sessionId, safeAddBreadcrumb, config.replay);
|
|
1347
|
+
this.replay.start();
|
|
1348
|
+
} catch {
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
if (config.enableHttpTracking) {
|
|
1352
|
+
try {
|
|
1353
|
+
this.httpRequests = new HttpRequestModule(this.transport);
|
|
1354
|
+
this.httpRequests.setDefaults({
|
|
1355
|
+
environment: this.config.environment,
|
|
1356
|
+
release: this.config.release,
|
|
1357
|
+
dist: this.config.dist,
|
|
1358
|
+
platform: this.config.platform,
|
|
1359
|
+
sdkName: this.config.sdkName,
|
|
1360
|
+
sdkVersion: this.config.sdkVersion
|
|
1361
|
+
});
|
|
1362
|
+
const { instrumentAxios } = installHttpInstrumentation(
|
|
1363
|
+
this.httpRequests,
|
|
1364
|
+
config.httpTracking ?? {},
|
|
1365
|
+
baseUrl
|
|
1366
|
+
);
|
|
1367
|
+
this._instrumentAxios = instrumentAxios;
|
|
1368
|
+
} catch {
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
/** Manually instrument an axios instance. No-op when HTTP tracking is off. */
|
|
1373
|
+
instrumentAxios(axios) {
|
|
1374
|
+
if (!this._instrumentAxios) return axios;
|
|
1375
|
+
return this._instrumentAxios(axios);
|
|
1376
|
+
}
|
|
1377
|
+
/** Snapshot of recent failed HTTP requests for error-linking. */
|
|
1378
|
+
getRecentFailedHttp() {
|
|
1379
|
+
return this.httpRequests?.getRecentFailed() ?? [];
|
|
1380
|
+
}
|
|
1381
|
+
captureException(error, context) {
|
|
1382
|
+
if (!this.passesSampleRate()) return;
|
|
1383
|
+
const frames = parseStack(error.stack).map((f) => ({
|
|
1384
|
+
...f,
|
|
1385
|
+
platform: this.config.platform,
|
|
1386
|
+
debugId: resolveDebugId(f.filename)
|
|
1387
|
+
}));
|
|
1388
|
+
const stackTrace = frames.length > 0 ? frames.map(frameToString) : void 0;
|
|
1389
|
+
const currentBreadcrumbs = this.breadcrumbs.length > 0 ? [...this.breadcrumbs] : void 0;
|
|
1390
|
+
this.breadcrumbs = [];
|
|
1391
|
+
const exceptionClass = (error.name && error.name !== "Error" ? error.name : void 0) || error.constructor?.name || "Error";
|
|
1392
|
+
const eff = this.effective();
|
|
1393
|
+
const traceContext = {};
|
|
1394
|
+
const traceId = this.tracing.getTraceId();
|
|
1395
|
+
if (traceId) traceContext.traceId = traceId;
|
|
1396
|
+
const spanId = this.tracing.getCurrentSpanId();
|
|
1397
|
+
if (spanId) traceContext.spanId = spanId;
|
|
1398
|
+
const recentFailed = this.httpRequests?.getRecentFailed() ?? [];
|
|
1399
|
+
if (recentFailed.length > 0) {
|
|
1400
|
+
traceContext["http.recentFailed"] = recentFailed.map((r) => ({
|
|
1401
|
+
method: r.method,
|
|
1402
|
+
url: r.url,
|
|
1403
|
+
statusCode: r.statusCode,
|
|
1404
|
+
durationMs: r.durationMs,
|
|
1405
|
+
error: r.error
|
|
1406
|
+
}));
|
|
1407
|
+
}
|
|
1408
|
+
const payload = {
|
|
1409
|
+
exceptionClass,
|
|
1410
|
+
message: error.message,
|
|
1411
|
+
stackTrace,
|
|
1412
|
+
frames: frames.length > 0 ? frames : void 0,
|
|
1413
|
+
platform: this.config.platform,
|
|
1414
|
+
sdkName: this.config.sdkName,
|
|
1415
|
+
sdkVersion: this.config.sdkVersion,
|
|
1416
|
+
dist: this.config.dist,
|
|
1417
|
+
level: eff.level ?? "error",
|
|
1418
|
+
environment: this.config.environment,
|
|
1419
|
+
release: this.config.release,
|
|
1420
|
+
sessionId: this.sessionId,
|
|
1421
|
+
user: eff.user,
|
|
1422
|
+
metadata: { ...this.buildMetadata(context), ...traceContext },
|
|
1423
|
+
breadcrumbs: currentBreadcrumbs,
|
|
1424
|
+
requestContext: browserRequestContext(),
|
|
1425
|
+
fingerprint: eff.fingerprint
|
|
1426
|
+
};
|
|
1427
|
+
this.sendThroughBeforeSend(payload);
|
|
1428
|
+
}
|
|
1429
|
+
/** Start a new span — auto-parented to any currently-active span. */
|
|
1430
|
+
startSpan(operation, options) {
|
|
1431
|
+
return this.tracing.startSpan(operation, options);
|
|
1432
|
+
}
|
|
1433
|
+
/** Get (and lazily create) the active trace ID. */
|
|
1434
|
+
getTraceId() {
|
|
1435
|
+
return this.tracing.getTraceId();
|
|
1436
|
+
}
|
|
1437
|
+
/** Override the active trace ID, e.g. from an inbound request header. */
|
|
1438
|
+
setTraceId(traceId) {
|
|
1439
|
+
this.tracing.setTraceId(traceId);
|
|
1440
|
+
}
|
|
1441
|
+
/** ID of the currently-active span, or null. */
|
|
1442
|
+
getCurrentSpanId() {
|
|
1443
|
+
return this.tracing.getCurrentSpanId();
|
|
1444
|
+
}
|
|
1445
|
+
/** Reset the trace ID and the active span stack. */
|
|
1446
|
+
resetTrace() {
|
|
1447
|
+
this.tracing.resetTrace();
|
|
1448
|
+
}
|
|
1449
|
+
captureMessage(message, level = "info", options = {}) {
|
|
1450
|
+
const as = options.as ?? (level === "fatal" || level === "error" ? "both" : "log");
|
|
1451
|
+
if (as === "log" || as === "both") {
|
|
1452
|
+
this.sendLog(level === "warning" ? "warn" : level, message);
|
|
1453
|
+
}
|
|
1454
|
+
if (as === "error" || as === "both") {
|
|
1455
|
+
if (!this.passesSampleRate()) return;
|
|
1456
|
+
const eff = this.effective();
|
|
1457
|
+
const payload = {
|
|
1458
|
+
exceptionClass: "Message",
|
|
1459
|
+
message,
|
|
1460
|
+
platform: this.config.platform,
|
|
1461
|
+
sdkName: this.config.sdkName,
|
|
1462
|
+
sdkVersion: this.config.sdkVersion,
|
|
1463
|
+
dist: this.config.dist,
|
|
1464
|
+
level,
|
|
1465
|
+
environment: this.config.environment,
|
|
1466
|
+
release: this.config.release,
|
|
1467
|
+
sessionId: this.sessionId,
|
|
1468
|
+
user: eff.user,
|
|
1469
|
+
metadata: this.buildMetadata(),
|
|
1470
|
+
requestContext: browserRequestContext(),
|
|
1471
|
+
fingerprint: eff.fingerprint
|
|
1472
|
+
};
|
|
1473
|
+
this.sendThroughBeforeSend(payload);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
addBreadcrumb(type, message, level, data) {
|
|
1477
|
+
const crumb = {
|
|
1478
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1479
|
+
type: VALID_BREADCRUMB_TYPES.has(type) ? type : "default",
|
|
1480
|
+
message,
|
|
1481
|
+
level: level && VALID_BREADCRUMB_LEVELS.has(level) ? level : "info",
|
|
1482
|
+
...data ? { data } : {}
|
|
1483
|
+
};
|
|
1484
|
+
if (this.breadcrumbs.length >= this.maxBreadcrumbs) this.breadcrumbs.shift();
|
|
1485
|
+
this.breadcrumbs.push(crumb);
|
|
1486
|
+
}
|
|
1487
|
+
clearBreadcrumbs() {
|
|
1488
|
+
this.breadcrumbs = [];
|
|
1489
|
+
}
|
|
1490
|
+
setUser(user) {
|
|
1491
|
+
this.config.user = user;
|
|
1492
|
+
}
|
|
1493
|
+
setTag(key, value) {
|
|
1494
|
+
if (!this.config.tags) this.config.tags = {};
|
|
1495
|
+
this.config.tags[key] = value;
|
|
1496
|
+
}
|
|
1497
|
+
/** Bulk-set tags. Merges with existing tags. */
|
|
1498
|
+
setTags(tags) {
|
|
1499
|
+
if (!this.config.tags) this.config.tags = {};
|
|
1500
|
+
Object.assign(this.config.tags, tags);
|
|
1501
|
+
}
|
|
1502
|
+
/** Set a single extra value. */
|
|
1503
|
+
setExtra(key, value) {
|
|
1504
|
+
if (!this.config.extras) this.config.extras = {};
|
|
1505
|
+
this.config.extras[key] = value;
|
|
1506
|
+
}
|
|
1507
|
+
/** Bulk-set extras. Merges with existing extras. */
|
|
1508
|
+
setExtras(extras) {
|
|
1509
|
+
if (!this.config.extras) this.config.extras = {};
|
|
1510
|
+
Object.assign(this.config.extras, extras);
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Attach a named context bag (e.g. `app`, `device`, `runtime`) — appears
|
|
1514
|
+
* under `metadata['context.<name>']` on every subsequent event. Pass
|
|
1515
|
+
* `null` to remove a previously-set context.
|
|
1516
|
+
*/
|
|
1517
|
+
setContext(name, ctx) {
|
|
1518
|
+
if (!this.config.contexts) this.config.contexts = {};
|
|
1519
|
+
if (ctx === null) delete this.config.contexts[name];
|
|
1520
|
+
else this.config.contexts[name] = ctx;
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Wait for the in-flight retry-buffer to drain. Resolves `true` if the
|
|
1524
|
+
* buffer empties within `timeoutMs` (default 2000ms), `false` otherwise.
|
|
1525
|
+
*/
|
|
1526
|
+
flush(timeoutMs) {
|
|
1527
|
+
return this.transport.flush(timeoutMs);
|
|
1528
|
+
}
|
|
1529
|
+
/** Set the default severity level applied to subsequent captures. */
|
|
1530
|
+
setLevel(level) {
|
|
1531
|
+
this.config.level = level;
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Set a custom grouping fingerprint applied to subsequent events.
|
|
1535
|
+
* Pass `null` or an empty array to clear and revert to default grouping.
|
|
1536
|
+
*/
|
|
1537
|
+
setFingerprint(fingerprint) {
|
|
1538
|
+
this.config.fingerprint = fingerprint && fingerprint.length > 0 ? fingerprint : void 0;
|
|
1539
|
+
}
|
|
1540
|
+
setIdentity(identity) {
|
|
1541
|
+
if (identity.sdkName) this.config.sdkName = identity.sdkName;
|
|
1542
|
+
if (identity.sdkVersion) this.config.sdkVersion = identity.sdkVersion;
|
|
1543
|
+
if (identity.platform) this.config.platform = identity.platform;
|
|
1544
|
+
if (identity.dist) this.config.dist = identity.dist;
|
|
1545
|
+
}
|
|
1546
|
+
getSessionId() {
|
|
1547
|
+
return this.sessionId;
|
|
1548
|
+
}
|
|
1549
|
+
getConfig() {
|
|
1550
|
+
return this.config;
|
|
1551
|
+
}
|
|
1552
|
+
destroy() {
|
|
1553
|
+
if (typeof window !== "undefined") {
|
|
1554
|
+
if (this.onErrorHandler) window.removeEventListener("error", this.onErrorHandler);
|
|
1555
|
+
if (this.onRejectionHandler) window.removeEventListener("unhandledrejection", this.onRejectionHandler);
|
|
1556
|
+
}
|
|
1557
|
+
this.onErrorHandler = null;
|
|
1558
|
+
this.onRejectionHandler = null;
|
|
1559
|
+
this.tracing.destroy();
|
|
1560
|
+
if (this.replay) {
|
|
1561
|
+
this.replay.destroy();
|
|
1562
|
+
this.replay = null;
|
|
1563
|
+
}
|
|
1564
|
+
if (this.httpRequests) {
|
|
1565
|
+
this.httpRequests.destroy();
|
|
1566
|
+
this.httpRequests = null;
|
|
1567
|
+
}
|
|
1568
|
+
this._instrumentAxios = null;
|
|
1569
|
+
this.breadcrumbs = [];
|
|
1570
|
+
}
|
|
1571
|
+
installBrowserHandlers() {
|
|
1572
|
+
this.onErrorHandler = (ev) => {
|
|
1573
|
+
const err = ev.error instanceof Error ? ev.error : new Error(ev.message || "Unknown error");
|
|
1574
|
+
this.captureException(err, { source: "window.onerror" });
|
|
1575
|
+
};
|
|
1576
|
+
this.onRejectionHandler = (ev) => {
|
|
1577
|
+
const err = ev.reason instanceof Error ? ev.reason : new Error(String(ev.reason));
|
|
1578
|
+
this.captureException(err, { source: "window.unhandledrejection" });
|
|
1579
|
+
};
|
|
1580
|
+
window.addEventListener("error", this.onErrorHandler);
|
|
1581
|
+
window.addEventListener("unhandledrejection", this.onRejectionHandler);
|
|
1582
|
+
}
|
|
1583
|
+
sendLog(level, message) {
|
|
1584
|
+
this.transport.send(LOGS_PATH, {
|
|
1585
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1586
|
+
level,
|
|
1587
|
+
message,
|
|
1588
|
+
sessionId: this.sessionId,
|
|
1589
|
+
environment: this.config.environment,
|
|
1590
|
+
release: this.config.release,
|
|
1591
|
+
platform: this.config.platform,
|
|
1592
|
+
sdkName: this.config.sdkName,
|
|
1593
|
+
sdkVersion: this.config.sdkVersion,
|
|
1594
|
+
metadata: { ...this.releaseTags(), ...this.config.tags }
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
passesSampleRate() {
|
|
1598
|
+
const r = this.config.sampleRate;
|
|
1599
|
+
if (typeof r !== "number" || r >= 1) return true;
|
|
1600
|
+
if (r <= 0) return false;
|
|
1601
|
+
return Math.random() < r;
|
|
1602
|
+
}
|
|
1603
|
+
/**
|
|
1604
|
+
* Returns the effective config layer = base config + every active scope.
|
|
1605
|
+
* Scope-only overrides (set inside `withScope`) flow into the wire
|
|
1606
|
+
* payload without leaking out of the callback.
|
|
1607
|
+
*/
|
|
1608
|
+
effective() {
|
|
1609
|
+
return mergeScopes(this.config, this.scopeStack);
|
|
1610
|
+
}
|
|
1611
|
+
buildMetadata(perCallContext) {
|
|
1612
|
+
const eff = this.effective();
|
|
1613
|
+
const out = {
|
|
1614
|
+
...this.releaseTags(),
|
|
1615
|
+
...eff.tags,
|
|
1616
|
+
...eff.extras ?? {},
|
|
1617
|
+
...perCallContext ?? {}
|
|
1618
|
+
};
|
|
1619
|
+
if (eff.contexts) {
|
|
1620
|
+
for (const [name, ctx] of Object.entries(eff.contexts)) {
|
|
1621
|
+
out[`context.${name}`] = ctx;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
return out;
|
|
1625
|
+
}
|
|
1626
|
+
/**
|
|
1627
|
+
* Run `callback` with a fresh, temporary {@link Scope} that isolates
|
|
1628
|
+
* any user/tag/extra/context/fingerprint/level it sets. The scope is
|
|
1629
|
+
* popped automatically when the callback returns or throws — including
|
|
1630
|
+
* for `Promise`-returning callbacks (the pop runs in `.finally`).
|
|
1631
|
+
*
|
|
1632
|
+
* Use this on the server (SSR / RSC / API route handlers) to attach
|
|
1633
|
+
* per-request user/tags without leaking that data into another request
|
|
1634
|
+
* being processed concurrently.
|
|
1635
|
+
*/
|
|
1636
|
+
withScope(callback) {
|
|
1637
|
+
const scope = new Scope();
|
|
1638
|
+
this.scopeStack.push(scope);
|
|
1639
|
+
let popped = false;
|
|
1640
|
+
const pop = () => {
|
|
1641
|
+
if (!popped) {
|
|
1642
|
+
popped = true;
|
|
1643
|
+
this.scopeStack.pop();
|
|
1644
|
+
}
|
|
1645
|
+
};
|
|
1646
|
+
try {
|
|
1647
|
+
const result = callback(scope);
|
|
1648
|
+
if (result && typeof result.then === "function") {
|
|
1649
|
+
return result.then(
|
|
1650
|
+
(v) => {
|
|
1651
|
+
pop();
|
|
1652
|
+
return v;
|
|
1653
|
+
},
|
|
1654
|
+
(e) => {
|
|
1655
|
+
pop();
|
|
1656
|
+
throw e;
|
|
1657
|
+
}
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1660
|
+
pop();
|
|
1661
|
+
return result;
|
|
1662
|
+
} catch (err) {
|
|
1663
|
+
pop();
|
|
1664
|
+
throw err;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
/** Direct access to the topmost active scope, or null. @internal */
|
|
1668
|
+
getCurrentScope() {
|
|
1669
|
+
return this.scopeStack[this.scopeStack.length - 1] ?? null;
|
|
1670
|
+
}
|
|
1671
|
+
async sendThroughBeforeSend(payload) {
|
|
1672
|
+
let final = payload;
|
|
1673
|
+
if (this.config.beforeSend) {
|
|
1674
|
+
try {
|
|
1675
|
+
final = await this.config.beforeSend(payload);
|
|
1676
|
+
} catch {
|
|
1677
|
+
final = payload;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
if (!final) return;
|
|
1681
|
+
this.transport.send(ERRORS_PATH, final);
|
|
1682
|
+
}
|
|
1683
|
+
releaseTags() {
|
|
1684
|
+
const out = {};
|
|
1685
|
+
if (this.config.sdkName) out["sdk.name"] = this.config.sdkName;
|
|
1686
|
+
if (this.config.sdkVersion) out["sdk.version"] = this.config.sdkVersion;
|
|
1687
|
+
if (this.config.platform) out["platform"] = this.config.platform;
|
|
1688
|
+
if (this.config.dist) out["dist"] = this.config.dist;
|
|
1689
|
+
if (this.config.commitSha) out["commit.sha"] = this.config.commitSha;
|
|
1690
|
+
if (this.config.branch) out["commit.branch"] = this.config.branch;
|
|
1691
|
+
return out;
|
|
1692
|
+
}
|
|
1693
|
+
};
|
|
1694
|
+
var instance = null;
|
|
1695
|
+
function ensureInit() {
|
|
1696
|
+
if (!instance) throw new Error("AllStak.init() must be called before using the SDK");
|
|
1697
|
+
return instance;
|
|
1698
|
+
}
|
|
1699
|
+
function safeAddBreadcrumb(type, message, level, data) {
|
|
1700
|
+
try {
|
|
1701
|
+
instance?.addBreadcrumb(type, message, level, data);
|
|
1702
|
+
} catch {
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
__setDefaultBreadcrumbForwarder(safeAddBreadcrumb);
|
|
1706
|
+
var AllStak = {
|
|
1707
|
+
init(config) {
|
|
1708
|
+
if (instance) instance.destroy();
|
|
1709
|
+
instance = new AllStakClient(config);
|
|
1710
|
+
return instance;
|
|
1711
|
+
},
|
|
1712
|
+
captureException(error, context) {
|
|
1713
|
+
ensureInit().captureException(error, context);
|
|
1714
|
+
},
|
|
1715
|
+
captureMessage(message, level = "info", options) {
|
|
1716
|
+
ensureInit().captureMessage(message, level, options);
|
|
1717
|
+
},
|
|
1718
|
+
addBreadcrumb(type, message, level, data) {
|
|
1719
|
+
ensureInit().addBreadcrumb(type, message, level, data);
|
|
1720
|
+
},
|
|
1721
|
+
clearBreadcrumbs() {
|
|
1722
|
+
ensureInit().clearBreadcrumbs();
|
|
1723
|
+
},
|
|
1724
|
+
setUser(user) {
|
|
1725
|
+
ensureInit().setUser(user);
|
|
1726
|
+
},
|
|
1727
|
+
setTag(key, value) {
|
|
1728
|
+
ensureInit().setTag(key, value);
|
|
1729
|
+
},
|
|
1730
|
+
setTags(tags) {
|
|
1731
|
+
ensureInit().setTags(tags);
|
|
1732
|
+
},
|
|
1733
|
+
setExtra(key, value) {
|
|
1734
|
+
ensureInit().setExtra(key, value);
|
|
1735
|
+
},
|
|
1736
|
+
setExtras(extras) {
|
|
1737
|
+
ensureInit().setExtras(extras);
|
|
1738
|
+
},
|
|
1739
|
+
setContext(name, ctx) {
|
|
1740
|
+
ensureInit().setContext(name, ctx);
|
|
1741
|
+
},
|
|
1742
|
+
setLevel(level) {
|
|
1743
|
+
ensureInit().setLevel(level);
|
|
1744
|
+
},
|
|
1745
|
+
setFingerprint(fingerprint) {
|
|
1746
|
+
ensureInit().setFingerprint(fingerprint);
|
|
1747
|
+
},
|
|
1748
|
+
flush(timeoutMs) {
|
|
1749
|
+
return ensureInit().flush(timeoutMs);
|
|
1750
|
+
},
|
|
1751
|
+
setIdentity(identity) {
|
|
1752
|
+
ensureInit().setIdentity(identity);
|
|
1753
|
+
},
|
|
1754
|
+
/**
|
|
1755
|
+
* Run `callback` with a fresh, temporary {@link Scope}. Any user/tag/
|
|
1756
|
+
* extra/context/fingerprint/level set on the scope is visible only inside
|
|
1757
|
+
* the callback. Pop is automatic (sync, async, throwing).
|
|
1758
|
+
*/
|
|
1759
|
+
withScope(callback) {
|
|
1760
|
+
return ensureInit().withScope(callback);
|
|
1761
|
+
},
|
|
1762
|
+
startSpan(operation, options) {
|
|
1763
|
+
return ensureInit().startSpan(operation, options);
|
|
1764
|
+
},
|
|
1765
|
+
getTraceId() {
|
|
1766
|
+
return ensureInit().getTraceId();
|
|
1767
|
+
},
|
|
1768
|
+
setTraceId(traceId) {
|
|
1769
|
+
ensureInit().setTraceId(traceId);
|
|
1770
|
+
},
|
|
1771
|
+
getCurrentSpanId() {
|
|
1772
|
+
return ensureInit().getCurrentSpanId();
|
|
1773
|
+
},
|
|
1774
|
+
resetTrace() {
|
|
1775
|
+
ensureInit().resetTrace();
|
|
1776
|
+
},
|
|
1777
|
+
/** Manually instrument an axios instance. No-op when HTTP tracking is off. */
|
|
1778
|
+
instrumentAxios(axios) {
|
|
1779
|
+
return ensureInit().instrumentAxios(axios);
|
|
1780
|
+
},
|
|
1781
|
+
getSessionId() {
|
|
1782
|
+
return ensureInit().getSessionId();
|
|
1783
|
+
},
|
|
1784
|
+
getConfig() {
|
|
1785
|
+
return instance?.getConfig() ?? null;
|
|
1786
|
+
},
|
|
1787
|
+
destroy() {
|
|
1788
|
+
instance?.destroy();
|
|
1789
|
+
instance = null;
|
|
1790
|
+
},
|
|
1791
|
+
/** @internal */
|
|
1792
|
+
_getInstance() {
|
|
1793
|
+
return instance;
|
|
1794
|
+
}
|
|
1795
|
+
};
|
|
1796
|
+
|
|
1797
|
+
// src/index.ts
|
|
1798
|
+
var AllStakErrorBoundary = class extends React.Component {
|
|
1799
|
+
constructor() {
|
|
1800
|
+
super(...arguments);
|
|
1801
|
+
this.state = { error: null };
|
|
1802
|
+
this.reset = () => this.setState({ error: null });
|
|
1803
|
+
}
|
|
1804
|
+
static getDerivedStateFromError(error) {
|
|
1805
|
+
return { error };
|
|
1806
|
+
}
|
|
1807
|
+
componentDidCatch(error, info) {
|
|
1808
|
+
try {
|
|
1809
|
+
AllStak.addBreadcrumb("ui", "React error boundary caught error", "error", {
|
|
1810
|
+
componentStack: info.componentStack ?? ""
|
|
1811
|
+
});
|
|
1812
|
+
const context = {
|
|
1813
|
+
componentStack: info.componentStack ?? "",
|
|
1814
|
+
source: "react-error-boundary"
|
|
1815
|
+
};
|
|
1816
|
+
if (this.props.tags) {
|
|
1817
|
+
for (const [k, v] of Object.entries(this.props.tags)) {
|
|
1818
|
+
context[`tag.${k}`] = v;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
AllStak.captureException(error, context);
|
|
1822
|
+
} catch {
|
|
1823
|
+
}
|
|
1824
|
+
try {
|
|
1825
|
+
this.props.onError?.(error, info);
|
|
1826
|
+
} catch {
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
render() {
|
|
1830
|
+
if (this.state.error) {
|
|
1831
|
+
const { fallback } = this.props;
|
|
1832
|
+
if (typeof fallback === "function") {
|
|
1833
|
+
return fallback({ error: this.state.error, reset: this.reset });
|
|
1834
|
+
}
|
|
1835
|
+
if (fallback !== void 0) return fallback;
|
|
1836
|
+
return null;
|
|
1837
|
+
}
|
|
1838
|
+
return this.props.children;
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
function useAllStak() {
|
|
1842
|
+
return React.useMemo(
|
|
1843
|
+
() => ({
|
|
1844
|
+
captureException: (error, ctx) => AllStak.captureException(error, ctx),
|
|
1845
|
+
captureMessage: (msg, level = "info") => AllStak.captureMessage(msg, level),
|
|
1846
|
+
setUser: (user) => AllStak.setUser(user),
|
|
1847
|
+
setTag: (key, value) => AllStak.setTag(key, value),
|
|
1848
|
+
addBreadcrumb: (type, message, level, data) => AllStak.addBreadcrumb(type, message, level, data)
|
|
1849
|
+
}),
|
|
1850
|
+
[]
|
|
1851
|
+
);
|
|
1852
|
+
}
|
|
1853
|
+
function withAllStakProfiler(Component2, name) {
|
|
1854
|
+
const displayName = name ?? Component2.displayName ?? Component2.name ?? "AnonymousComponent";
|
|
1855
|
+
const Wrapped = (props) => {
|
|
1856
|
+
React.useEffect(() => {
|
|
1857
|
+
AllStak.addBreadcrumb("navigation", `Mounted <${displayName}>`, "info");
|
|
1858
|
+
}, []);
|
|
1859
|
+
return React.createElement(Component2, props);
|
|
1860
|
+
};
|
|
1861
|
+
Wrapped.displayName = `withAllStakProfiler(${displayName})`;
|
|
1862
|
+
return Wrapped;
|
|
1863
|
+
}
|
|
30
1864
|
//# sourceMappingURL=index.js.map
|