@allstak/react 0.1.3 → 0.2.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.
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,1273 @@ 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: () => import_react.AllStak,
24
- AllStakErrorBoundary: () => import_react.AllStakErrorBoundary,
25
- useAllStak: () => import_react.useAllStak,
26
- withAllStakProfiler: () => import_react.withAllStakProfiler
33
+ AllStak: () => AllStak,
34
+ AllStakClient: () => AllStakClient,
35
+ AllStakErrorBoundary: () => AllStakErrorBoundary,
36
+ INGEST_HOST: () => INGEST_HOST,
37
+ ReplayRecorder: () => ReplayRecorder,
38
+ SDK_NAME: () => SDK_NAME,
39
+ SDK_VERSION: () => SDK_VERSION,
40
+ Scope: () => Scope,
41
+ instrumentBrowserNavigation: () => instrumentBrowserNavigation,
42
+ instrumentConsole: () => instrumentConsole,
43
+ instrumentFetch: () => instrumentFetch,
44
+ instrumentNextRouter: () => instrumentNextRouter,
45
+ instrumentReactRouter: () => instrumentReactRouter,
46
+ useAllStak: () => useAllStak,
47
+ withAllStakProfiler: () => withAllStakProfiler
27
48
  });
28
49
  module.exports = __toCommonJS(index_exports);
29
- var import_react = require("@allstak/js/react");
50
+ var React = __toESM(require("react"));
51
+
52
+ // src/transport.ts
53
+ var REQUEST_TIMEOUT = 3e3;
54
+ var MAX_BUFFER = 100;
55
+ var HttpTransport = class {
56
+ constructor(baseUrl, apiKey) {
57
+ this.baseUrl = baseUrl;
58
+ this.apiKey = apiKey;
59
+ this.buffer = [];
60
+ this.flushing = false;
61
+ }
62
+ async send(path, payload) {
63
+ try {
64
+ await this.doFetch(path, payload);
65
+ await this.flushBuffer();
66
+ } catch {
67
+ if (this.buffer.length >= MAX_BUFFER) this.buffer.shift();
68
+ this.buffer.push({ path, payload });
69
+ }
70
+ }
71
+ async doFetch(path, payload) {
72
+ const url = `${this.baseUrl}${path}`;
73
+ const controller = new AbortController();
74
+ const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
75
+ try {
76
+ const res = await fetch(url, {
77
+ method: "POST",
78
+ headers: {
79
+ "Content-Type": "application/json",
80
+ "X-AllStak-Key": this.apiKey
81
+ },
82
+ body: JSON.stringify(payload),
83
+ signal: controller.signal
84
+ });
85
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
86
+ } finally {
87
+ clearTimeout(timeoutId);
88
+ }
89
+ }
90
+ async flushBuffer() {
91
+ if (this.flushing || this.buffer.length === 0) return;
92
+ this.flushing = true;
93
+ try {
94
+ const items = this.buffer.splice(0, this.buffer.length);
95
+ for (const item of items) {
96
+ try {
97
+ await this.doFetch(item.path, item.payload);
98
+ } catch {
99
+ }
100
+ }
101
+ } finally {
102
+ this.flushing = false;
103
+ }
104
+ }
105
+ getBufferSize() {
106
+ return this.buffer.length;
107
+ }
108
+ /**
109
+ * Wait for the in-flight retry-buffer to drain. Resolves `true` if the
110
+ * buffer empties within `timeoutMs` (default 2000ms), `false` otherwise.
111
+ * Useful at process exit / before navigation away.
112
+ */
113
+ async flush(timeoutMs = 2e3) {
114
+ const deadline = Date.now() + timeoutMs;
115
+ await this.flushBuffer();
116
+ while (this.buffer.length > 0 || this.flushing) {
117
+ if (Date.now() >= deadline) return false;
118
+ await new Promise((r) => setTimeout(r, 25));
119
+ await this.flushBuffer();
120
+ }
121
+ return true;
122
+ }
123
+ };
124
+
125
+ // src/stack.ts
126
+ var V8_FRAME_RE = /^\s*at\s+(?:(.+?)\s+\()?((?:.+?):(\d+):(\d+))\)?\s*$/;
127
+ var GECKO_FRAME_RE = /^\s*(?:(.*?)@)?(.+?):(\d+):(\d+)\s*$/;
128
+ var NODE_INTERNAL_RE = /^(node:|internal\/|node_modules\/)/;
129
+ function parseStack(stack) {
130
+ if (!stack || typeof stack !== "string") return [];
131
+ const frames = [];
132
+ for (const raw of stack.split("\n")) {
133
+ const line = raw.trim();
134
+ if (!line) continue;
135
+ let m = V8_FRAME_RE.exec(line);
136
+ if (m) {
137
+ const filename = stripQueryHash(m[2].replace(/:\d+:\d+$/, ""));
138
+ frames.push({
139
+ filename,
140
+ absPath: filename,
141
+ function: m[1] ? m[1].trim() : void 0,
142
+ lineno: parseInt(m[3], 10),
143
+ colno: parseInt(m[4], 10),
144
+ inApp: isInApp(filename)
145
+ });
146
+ continue;
147
+ }
148
+ m = GECKO_FRAME_RE.exec(line);
149
+ if (m && m[2]) {
150
+ const filename = stripQueryHash(m[2]);
151
+ frames.push({
152
+ filename,
153
+ absPath: filename,
154
+ function: m[1] ? m[1].trim() : void 0,
155
+ lineno: parseInt(m[3], 10),
156
+ colno: parseInt(m[4], 10),
157
+ inApp: isInApp(filename)
158
+ });
159
+ }
160
+ }
161
+ return frames;
162
+ }
163
+ function stripQueryHash(url) {
164
+ const q = url.indexOf("?");
165
+ const h = url.indexOf("#");
166
+ let cut = url.length;
167
+ if (q >= 0) cut = Math.min(cut, q);
168
+ if (h >= 0) cut = Math.min(cut, h);
169
+ return url.slice(0, cut);
170
+ }
171
+ function isInApp(filename) {
172
+ if (!filename) return true;
173
+ if (NODE_INTERNAL_RE.test(filename)) return false;
174
+ if (filename.includes("/node_modules/")) return false;
175
+ return true;
176
+ }
177
+
178
+ // src/auto-breadcrumbs.ts
179
+ var FETCH_FLAG = "__allstak_fetch_patched__";
180
+ var CONSOLE_FLAG = "__allstak_console_patched__";
181
+ function instrumentFetch(addBreadcrumb, ownBaseUrl) {
182
+ const g = globalThis;
183
+ if (typeof g.fetch !== "function") return;
184
+ if (g.fetch[FETCH_FLAG]) return;
185
+ const originalFetch = g.fetch;
186
+ const wrapped = async function(input, init) {
187
+ const method = (init?.method || input && typeof input === "object" && input.method || "GET").toUpperCase();
188
+ let url;
189
+ if (typeof input === "string") url = input;
190
+ else if (input && typeof input.href === "string") url = input.href;
191
+ else if (input && typeof input.url === "string") url = input.url;
192
+ else url = String(input);
193
+ const safePath = url.split("?")[0];
194
+ const isOwnIngest = !!(ownBaseUrl && url.startsWith(ownBaseUrl));
195
+ const start = Date.now();
196
+ try {
197
+ const response = await originalFetch.call(this, input, init);
198
+ const durationMs = Date.now() - start;
199
+ if (!isOwnIngest) {
200
+ addBreadcrumb(
201
+ "http",
202
+ `${method} ${safePath} -> ${response.status}`,
203
+ response.status >= 400 ? "error" : "info",
204
+ { method, url: safePath, statusCode: response.status, durationMs }
205
+ );
206
+ }
207
+ return response;
208
+ } catch (err) {
209
+ const durationMs = Date.now() - start;
210
+ if (!isOwnIngest) {
211
+ addBreadcrumb("http", `${method} ${safePath} -> failed`, "error", {
212
+ method,
213
+ url: safePath,
214
+ error: String(err),
215
+ durationMs
216
+ });
217
+ }
218
+ throw err;
219
+ }
220
+ };
221
+ wrapped[FETCH_FLAG] = true;
222
+ g.fetch = wrapped;
223
+ }
224
+ function instrumentConsole(addBreadcrumb) {
225
+ if (typeof console === "undefined") return;
226
+ if (console[CONSOLE_FLAG]) return;
227
+ const origWarn = console.warn;
228
+ const origError = console.error;
229
+ console.warn = function(...args) {
230
+ try {
231
+ addBreadcrumb("log", args.map(safeString).join(" "), "warn");
232
+ } catch {
233
+ }
234
+ return origWarn.apply(console, args);
235
+ };
236
+ console.error = function(...args) {
237
+ try {
238
+ addBreadcrumb("log", args.map(safeString).join(" "), "error");
239
+ } catch {
240
+ }
241
+ return origError.apply(console, args);
242
+ };
243
+ console[CONSOLE_FLAG] = true;
244
+ }
245
+ function safeString(v) {
246
+ if (v == null) return String(v);
247
+ if (typeof v === "string") return v;
248
+ if (v instanceof Error) return `${v.name}: ${v.message}`;
249
+ try {
250
+ return typeof v === "object" ? JSON.stringify(v) : String(v);
251
+ } catch {
252
+ return Object.prototype.toString.call(v);
253
+ }
254
+ }
255
+
256
+ // src/navigation.ts
257
+ var FLAG = "__allstak_history_patched__";
258
+ function instrumentBrowserNavigation(addBreadcrumb) {
259
+ if (typeof window === "undefined" || typeof history === "undefined") return;
260
+ if (history[FLAG]) return;
261
+ const emit = (from, to) => {
262
+ if (from === to) return;
263
+ try {
264
+ addBreadcrumb("navigation", `${from} -> ${to}`, "info", { from, to });
265
+ } catch {
266
+ }
267
+ };
268
+ const origPush = history.pushState.bind(history);
269
+ const origReplace = history.replaceState.bind(history);
270
+ history.pushState = function(state, unused, url) {
271
+ const from = location.pathname + location.search;
272
+ const ret = origPush(state, unused, url ?? null);
273
+ emit(from, location.pathname + location.search);
274
+ return ret;
275
+ };
276
+ history.replaceState = function(state, unused, url) {
277
+ const from = location.pathname + location.search;
278
+ const ret = origReplace(state, unused, url ?? null);
279
+ emit(from, location.pathname + location.search);
280
+ return ret;
281
+ };
282
+ let last = location.pathname + location.search;
283
+ window.addEventListener("popstate", () => {
284
+ const next = location.pathname + location.search;
285
+ emit(last, next);
286
+ last = next;
287
+ });
288
+ history[FLAG] = true;
289
+ }
290
+ var lastReactRouterPath;
291
+ function instrumentReactRouter(location2, addBreadcrumb = (...args) => __defaultBreadcrumb(...args)) {
292
+ const next = `${location2.pathname}${location2.search ?? ""}`;
293
+ if (next === lastReactRouterPath) return;
294
+ const from = lastReactRouterPath ?? "<initial>";
295
+ lastReactRouterPath = next;
296
+ try {
297
+ addBreadcrumb("navigation", `${from} -> ${next}`, "info", { router: "react-router", from, to: next });
298
+ } catch {
299
+ }
300
+ }
301
+ var lastNextPath;
302
+ function instrumentNextRouter(url, addBreadcrumb = (...args) => __defaultBreadcrumb(...args)) {
303
+ if (url === lastNextPath) return;
304
+ const from = lastNextPath ?? "<initial>";
305
+ lastNextPath = url;
306
+ try {
307
+ addBreadcrumb("navigation", `${from} -> ${url}`, "info", { router: "next", from, to: url });
308
+ } catch {
309
+ }
310
+ }
311
+ var __defaultBreadcrumb = () => {
312
+ };
313
+ function __setDefaultBreadcrumbForwarder(fn) {
314
+ __defaultBreadcrumb = fn;
315
+ }
316
+
317
+ // src/scope.ts
318
+ var Scope = class {
319
+ constructor() {
320
+ this.tags = {};
321
+ this.extras = {};
322
+ this.contexts = {};
323
+ }
324
+ setUser(user) {
325
+ this.user = user;
326
+ return this;
327
+ }
328
+ setTag(key, value) {
329
+ this.tags[key] = value;
330
+ return this;
331
+ }
332
+ setTags(tags) {
333
+ Object.assign(this.tags, tags);
334
+ return this;
335
+ }
336
+ setExtra(key, value) {
337
+ this.extras[key] = value;
338
+ return this;
339
+ }
340
+ setExtras(extras) {
341
+ Object.assign(this.extras, extras);
342
+ return this;
343
+ }
344
+ setContext(name, ctx) {
345
+ if (ctx === null) delete this.contexts[name];
346
+ else this.contexts[name] = ctx;
347
+ return this;
348
+ }
349
+ setLevel(level) {
350
+ this.level = level;
351
+ return this;
352
+ }
353
+ setFingerprint(fingerprint) {
354
+ this.fingerprint = fingerprint && fingerprint.length > 0 ? fingerprint : void 0;
355
+ return this;
356
+ }
357
+ clear() {
358
+ this.user = void 0;
359
+ this.tags = {};
360
+ this.extras = {};
361
+ this.contexts = {};
362
+ this.fingerprint = void 0;
363
+ this.level = void 0;
364
+ return this;
365
+ }
366
+ };
367
+ function mergeScopes(base, stack) {
368
+ const out = { ...base };
369
+ out.tags = { ...base.tags ?? {} };
370
+ out.extras = { ...base.extras ?? {} };
371
+ out.contexts = { ...base.contexts ?? {} };
372
+ for (const scope of stack) {
373
+ if (scope.user) out.user = scope.user;
374
+ Object.assign(out.tags, scope.tags);
375
+ Object.assign(out.extras, scope.extras);
376
+ Object.assign(out.contexts, scope.contexts);
377
+ if (scope.fingerprint) out.fingerprint = scope.fingerprint;
378
+ if (scope.level) out.level = scope.level;
379
+ }
380
+ return out;
381
+ }
382
+
383
+ // src/tracing.ts
384
+ var SPAN_INGEST_PATH = "/ingest/v1/spans";
385
+ var FLUSH_INTERVAL_MS = 5e3;
386
+ var BATCH_SIZE_THRESHOLD = 20;
387
+ function id() {
388
+ const hex = (n) => Math.floor(Math.random() * n).toString(16).padStart(1, "0");
389
+ const seg = (len) => Array.from({ length: len }, () => hex(16)).join("");
390
+ return `${seg(8)}-${seg(4)}-4${seg(3)}-${(8 + Math.floor(Math.random() * 4)).toString(16)}${seg(3)}-${seg(12)}`;
391
+ }
392
+ var Span = class {
393
+ constructor(_traceId, _spanId, _parentSpanId, _operation, _description, _service, _environment, _tags, _onFinish) {
394
+ this._traceId = _traceId;
395
+ this._spanId = _spanId;
396
+ this._parentSpanId = _parentSpanId;
397
+ this._operation = _operation;
398
+ this._description = _description;
399
+ this._service = _service;
400
+ this._environment = _environment;
401
+ this._tags = _tags;
402
+ this._onFinish = _onFinish;
403
+ this._finished = false;
404
+ this._data = "";
405
+ this._startTimeMillis = Date.now();
406
+ }
407
+ setTag(key, value) {
408
+ this._tags[key] = value;
409
+ return this;
410
+ }
411
+ setData(data) {
412
+ this._data = data;
413
+ return this;
414
+ }
415
+ setDescription(description) {
416
+ this._description = description;
417
+ return this;
418
+ }
419
+ finish(status = "ok") {
420
+ if (this._finished) return;
421
+ this._finished = true;
422
+ const endTimeMillis = Date.now();
423
+ this._onFinish({
424
+ traceId: this._traceId,
425
+ spanId: this._spanId,
426
+ parentSpanId: this._parentSpanId,
427
+ operation: this._operation,
428
+ description: this._description,
429
+ status,
430
+ durationMs: endTimeMillis - this._startTimeMillis,
431
+ startTimeMillis: this._startTimeMillis,
432
+ endTimeMillis,
433
+ service: this._service,
434
+ environment: this._environment,
435
+ tags: this._tags,
436
+ data: this._data
437
+ });
438
+ }
439
+ get traceId() {
440
+ return this._traceId;
441
+ }
442
+ get spanId() {
443
+ return this._spanId;
444
+ }
445
+ get isFinished() {
446
+ return this._finished;
447
+ }
448
+ };
449
+ var NoopSpan = class extends Span {
450
+ constructor(traceId, spanId) {
451
+ super(traceId, spanId, "", "", "", "", "", {}, () => {
452
+ });
453
+ }
454
+ finish() {
455
+ }
456
+ };
457
+ var TracingModule = class {
458
+ constructor(transport, opts) {
459
+ this.transport = transport;
460
+ this.opts = opts;
461
+ this.spans = [];
462
+ this.flushTimer = null;
463
+ this.currentTraceId = null;
464
+ this.spanStack = [];
465
+ this.destroyed = false;
466
+ }
467
+ /** Get (and lazily create) the active trace ID. */
468
+ getTraceId() {
469
+ if (!this.currentTraceId) this.currentTraceId = id();
470
+ return this.currentTraceId;
471
+ }
472
+ /** Override the active trace ID, e.g. from an inbound request header. */
473
+ setTraceId(traceId) {
474
+ this.currentTraceId = traceId;
475
+ }
476
+ /** Get the active span's ID, or null if no span is active. */
477
+ getCurrentSpanId() {
478
+ return this.spanStack.length > 0 ? this.spanStack[this.spanStack.length - 1].spanId : null;
479
+ }
480
+ /** Reset both the trace ID and the in-flight span stack. */
481
+ resetTrace() {
482
+ this.currentTraceId = null;
483
+ this.spanStack = [];
484
+ }
485
+ /**
486
+ * Start a new span. The returned Span automatically inherits the active
487
+ * span as its parent. If `tracesSampleRate` drops this trace, returns a
488
+ * no-op Span so the call site doesn't have to null-check.
489
+ */
490
+ startSpan(operation, options = {}) {
491
+ const traceId = this.getTraceId();
492
+ const spanId = id();
493
+ const parentSpanId = this.getCurrentSpanId() ?? "";
494
+ if (!this.passesSampleRate()) {
495
+ return new NoopSpan(traceId, spanId);
496
+ }
497
+ const span = new Span(
498
+ traceId,
499
+ spanId,
500
+ parentSpanId,
501
+ operation,
502
+ options.description ?? "",
503
+ this.opts.service ?? "",
504
+ this.opts.environment ?? "",
505
+ { ...options.tags ?? {} },
506
+ (data) => this.enqueue(data, span)
507
+ );
508
+ this.spanStack.push(span);
509
+ return span;
510
+ }
511
+ passesSampleRate() {
512
+ const r = this.opts.tracesSampleRate;
513
+ if (typeof r !== "number" || r >= 1) return true;
514
+ if (r <= 0) return false;
515
+ return Math.random() < r;
516
+ }
517
+ enqueue(data, span) {
518
+ if (this.destroyed) return;
519
+ const idx = this.spanStack.lastIndexOf(span);
520
+ if (idx >= 0) this.spanStack.splice(idx, 1);
521
+ this.spans.push(data);
522
+ if (this.spans.length >= BATCH_SIZE_THRESHOLD) {
523
+ this.flush();
524
+ return;
525
+ }
526
+ if (!this.flushTimer) {
527
+ this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
528
+ }
529
+ }
530
+ flush() {
531
+ if (this.spans.length === 0) return;
532
+ const batch = this.spans;
533
+ this.spans = [];
534
+ this.transport.send(SPAN_INGEST_PATH, { spans: batch });
535
+ }
536
+ destroy() {
537
+ this.destroyed = true;
538
+ if (this.flushTimer) {
539
+ clearInterval(this.flushTimer);
540
+ this.flushTimer = null;
541
+ }
542
+ this.flush();
543
+ this.currentTraceId = null;
544
+ this.spanStack = [];
545
+ }
546
+ };
547
+
548
+ // src/replay.ts
549
+ var REPLAY_INGEST_PATH = "/ingest/v1/replay";
550
+ var FLUSH_INTERVAL_MS2 = 1e4;
551
+ var DEFAULT_MASK_ATTR = "data-allstak-mask";
552
+ var FLAG2 = "__allstak_replay_started__";
553
+ var ReplayRecorder = class {
554
+ constructor(transport, sessionId, addBreadcrumb, options = {}) {
555
+ this.transport = transport;
556
+ this.addBreadcrumb = addBreadcrumb;
557
+ this.buffer = [];
558
+ this.flushTimer = null;
559
+ this.observer = null;
560
+ this.inputListener = null;
561
+ this.destroyed = false;
562
+ this.sessionId = sessionId;
563
+ this.opts = {
564
+ enabled: options.enabled ?? true,
565
+ sampleRate: options.sampleRate ?? 0,
566
+ maskAllInputs: options.maskAllInputs ?? true,
567
+ maskAttribute: options.maskAttribute ?? DEFAULT_MASK_ATTR,
568
+ maxBufferedEvents: options.maxBufferedEvents ?? 200
569
+ };
570
+ }
571
+ start() {
572
+ if (typeof document === "undefined" || typeof window === "undefined") return;
573
+ if (!this.opts.enabled) return;
574
+ if (Math.random() >= this.opts.sampleRate) return;
575
+ if (document[FLAG2]) return;
576
+ document[FLAG2] = true;
577
+ this.push({
578
+ ts: Date.now(),
579
+ k: "snap",
580
+ data: { html: this.snapshotBody(), url: location.href }
581
+ });
582
+ this.observer = new MutationObserver((records) => {
583
+ for (const r of records) {
584
+ if (r.type === "childList") {
585
+ this.push({
586
+ ts: Date.now(),
587
+ k: "mut",
588
+ data: {
589
+ kind: "childList",
590
+ target: this.describePath(r.target),
591
+ added: Array.from(r.addedNodes).map((n) => this.describeNode(n)),
592
+ removed: r.removedNodes.length
593
+ }
594
+ });
595
+ } else if (r.type === "attributes") {
596
+ this.push({
597
+ ts: Date.now(),
598
+ k: "mut",
599
+ data: {
600
+ kind: "attr",
601
+ target: this.describePath(r.target),
602
+ name: r.attributeName,
603
+ value: this.safeAttribute(r.target, r.attributeName ?? "")
604
+ }
605
+ });
606
+ }
607
+ }
608
+ });
609
+ this.observer.observe(document.body ?? document.documentElement, {
610
+ childList: true,
611
+ attributes: true,
612
+ subtree: true
613
+ });
614
+ this.inputListener = (ev) => {
615
+ const target = ev.target;
616
+ if (!target || !("value" in target)) return;
617
+ const masked = this.maskInputValue(target);
618
+ this.push({
619
+ ts: Date.now(),
620
+ k: "input",
621
+ data: { target: this.describePath(target), value: masked, type: target.type }
622
+ });
623
+ };
624
+ document.addEventListener("input", this.inputListener, true);
625
+ this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS2);
626
+ try {
627
+ this.addBreadcrumb("default", "Replay recording started", "info", { sessionId: this.sessionId });
628
+ } catch {
629
+ }
630
+ }
631
+ destroy() {
632
+ this.destroyed = true;
633
+ if (this.observer) {
634
+ this.observer.disconnect();
635
+ this.observer = null;
636
+ }
637
+ if (this.inputListener) {
638
+ document.removeEventListener("input", this.inputListener, true);
639
+ this.inputListener = null;
640
+ }
641
+ if (this.flushTimer) {
642
+ clearInterval(this.flushTimer);
643
+ this.flushTimer = null;
644
+ }
645
+ this.flush();
646
+ if (typeof document !== "undefined") document[FLAG2] = false;
647
+ }
648
+ /** @internal — exposed for tests. */
649
+ getBuffer() {
650
+ return this.buffer;
651
+ }
652
+ push(ev) {
653
+ if (this.destroyed) return;
654
+ this.buffer.push(ev);
655
+ if (this.buffer.length >= this.opts.maxBufferedEvents) this.flush();
656
+ }
657
+ flush() {
658
+ if (this.buffer.length === 0) return;
659
+ const events = this.buffer;
660
+ this.buffer = [];
661
+ this.transport.send(REPLAY_INGEST_PATH, {
662
+ sessionId: this.sessionId,
663
+ events
664
+ });
665
+ }
666
+ // ── Sanitization helpers ──────────────────────────────────────────
667
+ snapshotBody() {
668
+ if (!document.body) return "";
669
+ const clone = document.body.cloneNode(true);
670
+ this.maskTree(clone);
671
+ return clone.outerHTML;
672
+ }
673
+ maskTree(root) {
674
+ if (this.opts.maskAttribute && root.hasAttribute?.(this.opts.maskAttribute)) {
675
+ root.textContent = "***";
676
+ return;
677
+ }
678
+ const tag = root.tagName?.toLowerCase();
679
+ if (tag === "input" || tag === "textarea" || tag === "select") {
680
+ root.value = this.maskInputValue(root);
681
+ root.setAttribute("value", root.value);
682
+ }
683
+ for (const child of Array.from(root.children)) this.maskTree(child);
684
+ }
685
+ maskInputValue(el) {
686
+ if (el.type === "password") return "***";
687
+ if (this.opts.maskAllInputs) return "***";
688
+ return String(el.value ?? "");
689
+ }
690
+ describeNode(node) {
691
+ if (node.nodeType === 1) {
692
+ return { type: "element", tag: node.tagName?.toLowerCase() };
693
+ }
694
+ if (node.nodeType === 3) {
695
+ return { type: "text", length: (node.nodeValue ?? "").length };
696
+ }
697
+ return { type: "other" };
698
+ }
699
+ describePath(el) {
700
+ if (!el || el.nodeType !== 1) return "";
701
+ const parts = [];
702
+ let cur = el;
703
+ let depth = 0;
704
+ while (cur && depth < 8) {
705
+ let p = cur.tagName.toLowerCase();
706
+ if (cur.id) {
707
+ p += `#${cur.id}`;
708
+ parts.unshift(p);
709
+ break;
710
+ }
711
+ if (cur.classList?.length) p += "." + Array.from(cur.classList).slice(0, 2).join(".");
712
+ parts.unshift(p);
713
+ cur = cur.parentElement;
714
+ depth += 1;
715
+ }
716
+ return parts.join(">");
717
+ }
718
+ safeAttribute(el, name) {
719
+ if (name === "value" || name === "defaultValue") return "***";
720
+ return el.getAttribute(name) ?? "";
721
+ }
722
+ };
723
+
724
+ // src/debug-id.ts
725
+ function getRegistry() {
726
+ return globalThis._allstakDebugIds ?? {};
727
+ }
728
+ var cache = /* @__PURE__ */ new Map();
729
+ function resolveDebugId(filename) {
730
+ if (!filename) return void 0;
731
+ if (cache.has(filename)) return cache.get(filename);
732
+ const registry = getRegistry();
733
+ let id2 = registry[filename];
734
+ if (!id2) {
735
+ for (const key of Object.keys(registry)) {
736
+ if (filename.endsWith(key)) {
737
+ id2 = registry[key];
738
+ break;
739
+ }
740
+ }
741
+ }
742
+ cache.set(filename, id2);
743
+ return id2;
744
+ }
745
+
746
+ // src/client.ts
747
+ var INGEST_HOST = "https://api.allstak.sa";
748
+ var SDK_NAME = "allstak-react";
749
+ var SDK_VERSION = "0.2.1";
750
+ var ERRORS_PATH = "/ingest/v1/errors";
751
+ var LOGS_PATH = "/ingest/v1/logs";
752
+ var VALID_BREADCRUMB_TYPES = /* @__PURE__ */ new Set(["http", "log", "ui", "navigation", "query", "default"]);
753
+ var VALID_BREADCRUMB_LEVELS = /* @__PURE__ */ new Set(["info", "warn", "error", "debug"]);
754
+ var DEFAULT_MAX_BREADCRUMBS = 50;
755
+ function frameToString(f) {
756
+ const fn = f.function && f.function.length > 0 ? f.function : "<anonymous>";
757
+ const file = f.filename || f.absPath || "<anonymous>";
758
+ return ` at ${fn} (${file}:${f.lineno ?? 0}:${f.colno ?? 0})`;
759
+ }
760
+ function generateId() {
761
+ const hex = (n) => Math.floor(Math.random() * n).toString(16).padStart(1, "0");
762
+ const seg = (len) => Array.from({ length: len }, () => hex(16)).join("");
763
+ return `${seg(8)}-${seg(4)}-4${seg(3)}-${(8 + Math.floor(Math.random() * 4)).toString(16)}${seg(3)}-${seg(12)}`;
764
+ }
765
+ function browserRequestContext() {
766
+ if (typeof window === "undefined" || typeof location === "undefined") return void 0;
767
+ return {
768
+ method: "GET",
769
+ path: location.pathname || "/",
770
+ host: location.host || "",
771
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0
772
+ };
773
+ }
774
+ var AllStakClient = class {
775
+ constructor(config) {
776
+ this.breadcrumbs = [];
777
+ this.scopeStack = [];
778
+ this.replay = null;
779
+ this.onErrorHandler = null;
780
+ this.onRejectionHandler = null;
781
+ if (!config.apiKey) throw new Error("AllStak: config.apiKey is required");
782
+ this.config = { ...config };
783
+ if (!this.config.environment) this.config.environment = "production";
784
+ if (!this.config.sdkName) this.config.sdkName = SDK_NAME;
785
+ if (!this.config.sdkVersion) this.config.sdkVersion = SDK_VERSION;
786
+ if (!this.config.platform) this.config.platform = "browser";
787
+ this.sessionId = generateId();
788
+ this.maxBreadcrumbs = config.maxBreadcrumbs ?? DEFAULT_MAX_BREADCRUMBS;
789
+ const baseUrl = (config.host ?? INGEST_HOST).replace(/\/$/, "");
790
+ this.transport = new HttpTransport(baseUrl, config.apiKey);
791
+ this.tracing = new TracingModule(this.transport, {
792
+ service: config.service ?? config.release ?? "",
793
+ environment: this.config.environment ?? "production",
794
+ tracesSampleRate: config.tracesSampleRate
795
+ });
796
+ if (config.autoCaptureBrowserErrors !== false && typeof window !== "undefined") {
797
+ this.installBrowserHandlers();
798
+ }
799
+ if (config.autoBreadcrumbsFetch !== false) {
800
+ try {
801
+ instrumentFetch(safeAddBreadcrumb, baseUrl);
802
+ } catch {
803
+ }
804
+ }
805
+ if (config.autoBreadcrumbsConsole !== false) {
806
+ try {
807
+ instrumentConsole(safeAddBreadcrumb);
808
+ } catch {
809
+ }
810
+ }
811
+ if (config.autoBreadcrumbsNavigation !== false) {
812
+ try {
813
+ instrumentBrowserNavigation(safeAddBreadcrumb);
814
+ } catch {
815
+ }
816
+ }
817
+ if (config.replay && (config.replay.enabled ?? true)) {
818
+ try {
819
+ this.replay = new ReplayRecorder(this.transport, this.sessionId, safeAddBreadcrumb, config.replay);
820
+ this.replay.start();
821
+ } catch {
822
+ }
823
+ }
824
+ }
825
+ captureException(error, context) {
826
+ if (!this.passesSampleRate()) return;
827
+ const frames = parseStack(error.stack).map((f) => ({
828
+ ...f,
829
+ platform: this.config.platform,
830
+ debugId: resolveDebugId(f.filename)
831
+ }));
832
+ const stackTrace = frames.length > 0 ? frames.map(frameToString) : void 0;
833
+ const currentBreadcrumbs = this.breadcrumbs.length > 0 ? [...this.breadcrumbs] : void 0;
834
+ this.breadcrumbs = [];
835
+ const exceptionClass = (error.name && error.name !== "Error" ? error.name : void 0) || error.constructor?.name || "Error";
836
+ const eff = this.effective();
837
+ const traceContext = {};
838
+ const traceId = this.tracing.getTraceId();
839
+ if (traceId) traceContext.traceId = traceId;
840
+ const spanId = this.tracing.getCurrentSpanId();
841
+ if (spanId) traceContext.spanId = spanId;
842
+ const payload = {
843
+ exceptionClass,
844
+ message: error.message,
845
+ stackTrace,
846
+ frames: frames.length > 0 ? frames : void 0,
847
+ platform: this.config.platform,
848
+ sdkName: this.config.sdkName,
849
+ sdkVersion: this.config.sdkVersion,
850
+ dist: this.config.dist,
851
+ level: eff.level ?? "error",
852
+ environment: this.config.environment,
853
+ release: this.config.release,
854
+ sessionId: this.sessionId,
855
+ user: eff.user,
856
+ metadata: { ...this.buildMetadata(context), ...traceContext },
857
+ breadcrumbs: currentBreadcrumbs,
858
+ requestContext: browserRequestContext(),
859
+ fingerprint: eff.fingerprint
860
+ };
861
+ this.sendThroughBeforeSend(payload);
862
+ }
863
+ /** Start a new span — auto-parented to any currently-active span. */
864
+ startSpan(operation, options) {
865
+ return this.tracing.startSpan(operation, options);
866
+ }
867
+ /** Get (and lazily create) the active trace ID. */
868
+ getTraceId() {
869
+ return this.tracing.getTraceId();
870
+ }
871
+ /** Override the active trace ID, e.g. from an inbound request header. */
872
+ setTraceId(traceId) {
873
+ this.tracing.setTraceId(traceId);
874
+ }
875
+ /** ID of the currently-active span, or null. */
876
+ getCurrentSpanId() {
877
+ return this.tracing.getCurrentSpanId();
878
+ }
879
+ /** Reset the trace ID and the active span stack. */
880
+ resetTrace() {
881
+ this.tracing.resetTrace();
882
+ }
883
+ captureMessage(message, level = "info", options = {}) {
884
+ const as = options.as ?? (level === "fatal" || level === "error" ? "both" : "log");
885
+ if (as === "log" || as === "both") {
886
+ this.sendLog(level === "warning" ? "warn" : level, message);
887
+ }
888
+ if (as === "error" || as === "both") {
889
+ if (!this.passesSampleRate()) return;
890
+ const eff = this.effective();
891
+ const payload = {
892
+ exceptionClass: "Message",
893
+ message,
894
+ platform: this.config.platform,
895
+ sdkName: this.config.sdkName,
896
+ sdkVersion: this.config.sdkVersion,
897
+ dist: this.config.dist,
898
+ level,
899
+ environment: this.config.environment,
900
+ release: this.config.release,
901
+ sessionId: this.sessionId,
902
+ user: eff.user,
903
+ metadata: this.buildMetadata(),
904
+ requestContext: browserRequestContext(),
905
+ fingerprint: eff.fingerprint
906
+ };
907
+ this.sendThroughBeforeSend(payload);
908
+ }
909
+ }
910
+ addBreadcrumb(type, message, level, data) {
911
+ const crumb = {
912
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
913
+ type: VALID_BREADCRUMB_TYPES.has(type) ? type : "default",
914
+ message,
915
+ level: level && VALID_BREADCRUMB_LEVELS.has(level) ? level : "info",
916
+ ...data ? { data } : {}
917
+ };
918
+ if (this.breadcrumbs.length >= this.maxBreadcrumbs) this.breadcrumbs.shift();
919
+ this.breadcrumbs.push(crumb);
920
+ }
921
+ clearBreadcrumbs() {
922
+ this.breadcrumbs = [];
923
+ }
924
+ setUser(user) {
925
+ this.config.user = user;
926
+ }
927
+ setTag(key, value) {
928
+ if (!this.config.tags) this.config.tags = {};
929
+ this.config.tags[key] = value;
930
+ }
931
+ /** Bulk-set tags. Merges with existing tags. */
932
+ setTags(tags) {
933
+ if (!this.config.tags) this.config.tags = {};
934
+ Object.assign(this.config.tags, tags);
935
+ }
936
+ /** Set a single extra value. */
937
+ setExtra(key, value) {
938
+ if (!this.config.extras) this.config.extras = {};
939
+ this.config.extras[key] = value;
940
+ }
941
+ /** Bulk-set extras. Merges with existing extras. */
942
+ setExtras(extras) {
943
+ if (!this.config.extras) this.config.extras = {};
944
+ Object.assign(this.config.extras, extras);
945
+ }
946
+ /**
947
+ * Attach a named context bag (e.g. `app`, `device`, `runtime`) — appears
948
+ * under `metadata['context.<name>']` on every subsequent event. Pass
949
+ * `null` to remove a previously-set context.
950
+ */
951
+ setContext(name, ctx) {
952
+ if (!this.config.contexts) this.config.contexts = {};
953
+ if (ctx === null) delete this.config.contexts[name];
954
+ else this.config.contexts[name] = ctx;
955
+ }
956
+ /**
957
+ * Wait for the in-flight retry-buffer to drain. Resolves `true` if the
958
+ * buffer empties within `timeoutMs` (default 2000ms), `false` otherwise.
959
+ */
960
+ flush(timeoutMs) {
961
+ return this.transport.flush(timeoutMs);
962
+ }
963
+ /** Set the default severity level applied to subsequent captures. */
964
+ setLevel(level) {
965
+ this.config.level = level;
966
+ }
967
+ /**
968
+ * Set a custom grouping fingerprint applied to subsequent events.
969
+ * Pass `null` or an empty array to clear and revert to default grouping.
970
+ */
971
+ setFingerprint(fingerprint) {
972
+ this.config.fingerprint = fingerprint && fingerprint.length > 0 ? fingerprint : void 0;
973
+ }
974
+ setIdentity(identity) {
975
+ if (identity.sdkName) this.config.sdkName = identity.sdkName;
976
+ if (identity.sdkVersion) this.config.sdkVersion = identity.sdkVersion;
977
+ if (identity.platform) this.config.platform = identity.platform;
978
+ if (identity.dist) this.config.dist = identity.dist;
979
+ }
980
+ getSessionId() {
981
+ return this.sessionId;
982
+ }
983
+ getConfig() {
984
+ return this.config;
985
+ }
986
+ destroy() {
987
+ if (typeof window !== "undefined") {
988
+ if (this.onErrorHandler) window.removeEventListener("error", this.onErrorHandler);
989
+ if (this.onRejectionHandler) window.removeEventListener("unhandledrejection", this.onRejectionHandler);
990
+ }
991
+ this.onErrorHandler = null;
992
+ this.onRejectionHandler = null;
993
+ this.tracing.destroy();
994
+ if (this.replay) {
995
+ this.replay.destroy();
996
+ this.replay = null;
997
+ }
998
+ this.breadcrumbs = [];
999
+ }
1000
+ installBrowserHandlers() {
1001
+ this.onErrorHandler = (ev) => {
1002
+ const err = ev.error instanceof Error ? ev.error : new Error(ev.message || "Unknown error");
1003
+ this.captureException(err, { source: "window.onerror" });
1004
+ };
1005
+ this.onRejectionHandler = (ev) => {
1006
+ const err = ev.reason instanceof Error ? ev.reason : new Error(String(ev.reason));
1007
+ this.captureException(err, { source: "window.unhandledrejection" });
1008
+ };
1009
+ window.addEventListener("error", this.onErrorHandler);
1010
+ window.addEventListener("unhandledrejection", this.onRejectionHandler);
1011
+ }
1012
+ sendLog(level, message) {
1013
+ this.transport.send(LOGS_PATH, {
1014
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1015
+ level,
1016
+ message,
1017
+ sessionId: this.sessionId,
1018
+ environment: this.config.environment,
1019
+ release: this.config.release,
1020
+ platform: this.config.platform,
1021
+ sdkName: this.config.sdkName,
1022
+ sdkVersion: this.config.sdkVersion,
1023
+ metadata: { ...this.releaseTags(), ...this.config.tags }
1024
+ });
1025
+ }
1026
+ passesSampleRate() {
1027
+ const r = this.config.sampleRate;
1028
+ if (typeof r !== "number" || r >= 1) return true;
1029
+ if (r <= 0) return false;
1030
+ return Math.random() < r;
1031
+ }
1032
+ /**
1033
+ * Returns the effective config layer = base config + every active scope.
1034
+ * Scope-only overrides (set inside `withScope`) flow into the wire
1035
+ * payload without leaking out of the callback.
1036
+ */
1037
+ effective() {
1038
+ return mergeScopes(this.config, this.scopeStack);
1039
+ }
1040
+ buildMetadata(perCallContext) {
1041
+ const eff = this.effective();
1042
+ const out = {
1043
+ ...this.releaseTags(),
1044
+ ...eff.tags,
1045
+ ...eff.extras ?? {},
1046
+ ...perCallContext ?? {}
1047
+ };
1048
+ if (eff.contexts) {
1049
+ for (const [name, ctx] of Object.entries(eff.contexts)) {
1050
+ out[`context.${name}`] = ctx;
1051
+ }
1052
+ }
1053
+ return out;
1054
+ }
1055
+ /**
1056
+ * Run `callback` with a fresh, temporary {@link Scope} that isolates
1057
+ * any user/tag/extra/context/fingerprint/level it sets. The scope is
1058
+ * popped automatically when the callback returns or throws — including
1059
+ * for `Promise`-returning callbacks (the pop runs in `.finally`).
1060
+ *
1061
+ * Use this on the server (SSR / RSC / API route handlers) to attach
1062
+ * per-request user/tags without leaking that data into another request
1063
+ * being processed concurrently.
1064
+ */
1065
+ withScope(callback) {
1066
+ const scope = new Scope();
1067
+ this.scopeStack.push(scope);
1068
+ let popped = false;
1069
+ const pop = () => {
1070
+ if (!popped) {
1071
+ popped = true;
1072
+ this.scopeStack.pop();
1073
+ }
1074
+ };
1075
+ try {
1076
+ const result = callback(scope);
1077
+ if (result && typeof result.then === "function") {
1078
+ return result.then(
1079
+ (v) => {
1080
+ pop();
1081
+ return v;
1082
+ },
1083
+ (e) => {
1084
+ pop();
1085
+ throw e;
1086
+ }
1087
+ );
1088
+ }
1089
+ pop();
1090
+ return result;
1091
+ } catch (err) {
1092
+ pop();
1093
+ throw err;
1094
+ }
1095
+ }
1096
+ /** Direct access to the topmost active scope, or null. @internal */
1097
+ getCurrentScope() {
1098
+ return this.scopeStack[this.scopeStack.length - 1] ?? null;
1099
+ }
1100
+ async sendThroughBeforeSend(payload) {
1101
+ let final = payload;
1102
+ if (this.config.beforeSend) {
1103
+ try {
1104
+ final = await this.config.beforeSend(payload);
1105
+ } catch {
1106
+ final = payload;
1107
+ }
1108
+ }
1109
+ if (!final) return;
1110
+ this.transport.send(ERRORS_PATH, final);
1111
+ }
1112
+ releaseTags() {
1113
+ const out = {};
1114
+ if (this.config.sdkName) out["sdk.name"] = this.config.sdkName;
1115
+ if (this.config.sdkVersion) out["sdk.version"] = this.config.sdkVersion;
1116
+ if (this.config.platform) out["platform"] = this.config.platform;
1117
+ if (this.config.dist) out["dist"] = this.config.dist;
1118
+ if (this.config.commitSha) out["commit.sha"] = this.config.commitSha;
1119
+ if (this.config.branch) out["commit.branch"] = this.config.branch;
1120
+ return out;
1121
+ }
1122
+ };
1123
+ var instance = null;
1124
+ function ensureInit() {
1125
+ if (!instance) throw new Error("AllStak.init() must be called before using the SDK");
1126
+ return instance;
1127
+ }
1128
+ function safeAddBreadcrumb(type, message, level, data) {
1129
+ try {
1130
+ instance?.addBreadcrumb(type, message, level, data);
1131
+ } catch {
1132
+ }
1133
+ }
1134
+ __setDefaultBreadcrumbForwarder(safeAddBreadcrumb);
1135
+ var AllStak = {
1136
+ init(config) {
1137
+ if (instance) instance.destroy();
1138
+ instance = new AllStakClient(config);
1139
+ return instance;
1140
+ },
1141
+ captureException(error, context) {
1142
+ ensureInit().captureException(error, context);
1143
+ },
1144
+ captureMessage(message, level = "info", options) {
1145
+ ensureInit().captureMessage(message, level, options);
1146
+ },
1147
+ addBreadcrumb(type, message, level, data) {
1148
+ ensureInit().addBreadcrumb(type, message, level, data);
1149
+ },
1150
+ clearBreadcrumbs() {
1151
+ ensureInit().clearBreadcrumbs();
1152
+ },
1153
+ setUser(user) {
1154
+ ensureInit().setUser(user);
1155
+ },
1156
+ setTag(key, value) {
1157
+ ensureInit().setTag(key, value);
1158
+ },
1159
+ setTags(tags) {
1160
+ ensureInit().setTags(tags);
1161
+ },
1162
+ setExtra(key, value) {
1163
+ ensureInit().setExtra(key, value);
1164
+ },
1165
+ setExtras(extras) {
1166
+ ensureInit().setExtras(extras);
1167
+ },
1168
+ setContext(name, ctx) {
1169
+ ensureInit().setContext(name, ctx);
1170
+ },
1171
+ setLevel(level) {
1172
+ ensureInit().setLevel(level);
1173
+ },
1174
+ setFingerprint(fingerprint) {
1175
+ ensureInit().setFingerprint(fingerprint);
1176
+ },
1177
+ flush(timeoutMs) {
1178
+ return ensureInit().flush(timeoutMs);
1179
+ },
1180
+ setIdentity(identity) {
1181
+ ensureInit().setIdentity(identity);
1182
+ },
1183
+ /**
1184
+ * Run `callback` with a fresh, temporary {@link Scope}. Any user/tag/
1185
+ * extra/context/fingerprint/level set on the scope is visible only inside
1186
+ * the callback. Pop is automatic (sync, async, throwing).
1187
+ */
1188
+ withScope(callback) {
1189
+ return ensureInit().withScope(callback);
1190
+ },
1191
+ startSpan(operation, options) {
1192
+ return ensureInit().startSpan(operation, options);
1193
+ },
1194
+ getTraceId() {
1195
+ return ensureInit().getTraceId();
1196
+ },
1197
+ setTraceId(traceId) {
1198
+ ensureInit().setTraceId(traceId);
1199
+ },
1200
+ getCurrentSpanId() {
1201
+ return ensureInit().getCurrentSpanId();
1202
+ },
1203
+ resetTrace() {
1204
+ ensureInit().resetTrace();
1205
+ },
1206
+ getSessionId() {
1207
+ return ensureInit().getSessionId();
1208
+ },
1209
+ getConfig() {
1210
+ return instance?.getConfig() ?? null;
1211
+ },
1212
+ destroy() {
1213
+ instance?.destroy();
1214
+ instance = null;
1215
+ },
1216
+ /** @internal */
1217
+ _getInstance() {
1218
+ return instance;
1219
+ }
1220
+ };
1221
+
1222
+ // src/index.ts
1223
+ var AllStakErrorBoundary = class extends React.Component {
1224
+ constructor() {
1225
+ super(...arguments);
1226
+ this.state = { error: null };
1227
+ this.reset = () => this.setState({ error: null });
1228
+ }
1229
+ static getDerivedStateFromError(error) {
1230
+ return { error };
1231
+ }
1232
+ componentDidCatch(error, info) {
1233
+ try {
1234
+ AllStak.addBreadcrumb("ui", "React error boundary caught error", "error", {
1235
+ componentStack: info.componentStack ?? ""
1236
+ });
1237
+ const context = {
1238
+ componentStack: info.componentStack ?? "",
1239
+ source: "react-error-boundary"
1240
+ };
1241
+ if (this.props.tags) {
1242
+ for (const [k, v] of Object.entries(this.props.tags)) {
1243
+ context[`tag.${k}`] = v;
1244
+ }
1245
+ }
1246
+ AllStak.captureException(error, context);
1247
+ } catch {
1248
+ }
1249
+ try {
1250
+ this.props.onError?.(error, info);
1251
+ } catch {
1252
+ }
1253
+ }
1254
+ render() {
1255
+ if (this.state.error) {
1256
+ const { fallback } = this.props;
1257
+ if (typeof fallback === "function") {
1258
+ return fallback({ error: this.state.error, reset: this.reset });
1259
+ }
1260
+ if (fallback !== void 0) return fallback;
1261
+ return null;
1262
+ }
1263
+ return this.props.children;
1264
+ }
1265
+ };
1266
+ function useAllStak() {
1267
+ return React.useMemo(
1268
+ () => ({
1269
+ captureException: (error, ctx) => AllStak.captureException(error, ctx),
1270
+ captureMessage: (msg, level = "info") => AllStak.captureMessage(msg, level),
1271
+ setUser: (user) => AllStak.setUser(user),
1272
+ setTag: (key, value) => AllStak.setTag(key, value),
1273
+ addBreadcrumb: (type, message, level, data) => AllStak.addBreadcrumb(type, message, level, data)
1274
+ }),
1275
+ []
1276
+ );
1277
+ }
1278
+ function withAllStakProfiler(Component2, name) {
1279
+ const displayName = name ?? Component2.displayName ?? Component2.name ?? "AnonymousComponent";
1280
+ const Wrapped = (props) => {
1281
+ React.useEffect(() => {
1282
+ AllStak.addBreadcrumb("navigation", `Mounted <${displayName}>`, "info");
1283
+ }, []);
1284
+ return React.createElement(Component2, props);
1285
+ };
1286
+ Wrapped.displayName = `withAllStakProfiler(${displayName})`;
1287
+ return Wrapped;
1288
+ }
30
1289
  //# sourceMappingURL=index.js.map