@botim/mp-debug-sdk 0.7.2 → 1.0.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/dist/auto.js ADDED
@@ -0,0 +1,1857 @@
1
+ import { botimConfig } from 'virtual:botim/config';
2
+ import { snapshot } from 'rrweb-snapshot';
3
+
4
+ // src/auto.ts
5
+
6
+ // src/types.ts
7
+ var SCHEMA_VERSION = 2;
8
+
9
+ // src/errors.ts
10
+ var BRAND = "@botim/debug-sdk";
11
+ var BotimConfigError = class extends Error {
12
+ constructor(message, opts = { code: "invalid-config" }) {
13
+ super(`[${BRAND}] ${message}`);
14
+ this.name = "BotimConfigError";
15
+ this.code = opts.code;
16
+ this.path = opts.path;
17
+ }
18
+ };
19
+ var BotimConsentError = class extends Error {
20
+ constructor(message) {
21
+ super(`[${BRAND}] ${message}`);
22
+ this.name = "BotimConsentError";
23
+ }
24
+ };
25
+
26
+ // src/consent-ui.ts
27
+ var STORAGE_PREFIX = "__botim_debug_consent_";
28
+ var CSS_PREFIX = "__botim-debug-consent";
29
+ var STYLE_ELEMENT_ID = `${CSS_PREFIX}-styles`;
30
+ var DEFAULT_COPY = {
31
+ title: "Enable BOTIM debug logging?",
32
+ body: [
33
+ "This shares your screen content, console logs, and network calls with the BOTIM team to help debug this mini-program.",
34
+ "No data leaves your device unless you agree. You can revoke this anytime by clearing site data or via developer tools."
35
+ ],
36
+ acceptLabel: "Allow",
37
+ declineLabel: "Skip"
38
+ };
39
+ function readConsentDecision(mpId) {
40
+ if (typeof localStorage === "undefined") return null;
41
+ try {
42
+ const raw = localStorage.getItem(STORAGE_PREFIX + mpId);
43
+ if (raw === "granted" || raw === "denied") return raw;
44
+ return null;
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+ function writeConsentDecision(mpId, decision) {
50
+ if (typeof localStorage === "undefined") return;
51
+ try {
52
+ localStorage.setItem(STORAGE_PREFIX + mpId, decision);
53
+ } catch {
54
+ }
55
+ }
56
+ function clearConsentDecision(mpId) {
57
+ if (typeof localStorage === "undefined") return;
58
+ try {
59
+ localStorage.removeItem(STORAGE_PREFIX + mpId);
60
+ } catch {
61
+ }
62
+ }
63
+ var inflightPrompt = null;
64
+ async function promptForConsent(mpId, copy = {}) {
65
+ if (inflightPrompt) return inflightPrompt;
66
+ if (typeof document === "undefined" || typeof window === "undefined") {
67
+ return "denied";
68
+ }
69
+ const merged = {
70
+ title: copy.title ?? DEFAULT_COPY.title,
71
+ body: copy.body ?? DEFAULT_COPY.body,
72
+ acceptLabel: copy.acceptLabel ?? DEFAULT_COPY.acceptLabel,
73
+ declineLabel: copy.declineLabel ?? DEFAULT_COPY.declineLabel
74
+ };
75
+ inflightPrompt = new Promise((resolve) => {
76
+ injectStylesheet();
77
+ const overlay = buildOverlay(merged, (decision) => {
78
+ writeConsentDecision(mpId, decision);
79
+ try {
80
+ overlay.remove();
81
+ } catch {
82
+ }
83
+ inflightPrompt = null;
84
+ resolve(decision);
85
+ });
86
+ document.body.appendChild(overlay);
87
+ });
88
+ return inflightPrompt;
89
+ }
90
+ function injectStylesheet() {
91
+ if (document.getElementById(STYLE_ELEMENT_ID)) return;
92
+ const style = document.createElement("style");
93
+ style.id = STYLE_ELEMENT_ID;
94
+ style.textContent = `
95
+ .${CSS_PREFIX}-overlay {
96
+ position: fixed !important;
97
+ inset: 0 !important;
98
+ z-index: 2147483647 !important;
99
+ display: flex !important;
100
+ align-items: flex-end !important;
101
+ justify-content: center !important;
102
+ background: rgba(0, 0, 0, 0.45) !important;
103
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
104
+ Roboto, "Helvetica Neue", sans-serif !important;
105
+ animation: ${CSS_PREFIX}-fade-in 160ms ease-out !important;
106
+ box-sizing: border-box !important;
107
+ padding: 16px !important;
108
+ }
109
+ @media (min-width: 480px) {
110
+ .${CSS_PREFIX}-overlay {
111
+ align-items: center !important;
112
+ }
113
+ }
114
+ @keyframes ${CSS_PREFIX}-fade-in {
115
+ from { opacity: 0; }
116
+ to { opacity: 1; }
117
+ }
118
+ .${CSS_PREFIX}-card {
119
+ background: #ffffff !important;
120
+ color: #0e1116 !important;
121
+ border-radius: 16px !important;
122
+ max-width: 420px !important;
123
+ width: 100% !important;
124
+ padding: 20px 20px 16px !important;
125
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18) !important;
126
+ box-sizing: border-box !important;
127
+ }
128
+ .${CSS_PREFIX}-title {
129
+ font-size: 17px !important;
130
+ font-weight: 600 !important;
131
+ margin: 0 0 12px !important;
132
+ line-height: 1.3 !important;
133
+ }
134
+ .${CSS_PREFIX}-body {
135
+ font-size: 14px !important;
136
+ line-height: 1.5 !important;
137
+ color: #4a5160 !important;
138
+ margin: 0 0 8px !important;
139
+ }
140
+ .${CSS_PREFIX}-actions {
141
+ display: flex !important;
142
+ justify-content: flex-end !important;
143
+ gap: 8px !important;
144
+ margin-top: 16px !important;
145
+ }
146
+ .${CSS_PREFIX}-btn {
147
+ font-family: inherit !important;
148
+ font-size: 14px !important;
149
+ font-weight: 500 !important;
150
+ padding: 9px 16px !important;
151
+ border-radius: 10px !important;
152
+ border: none !important;
153
+ cursor: pointer !important;
154
+ line-height: 1 !important;
155
+ min-height: 36px !important;
156
+ box-sizing: border-box !important;
157
+ }
158
+ .${CSS_PREFIX}-btn-decline {
159
+ background: transparent !important;
160
+ color: #4a5160 !important;
161
+ }
162
+ .${CSS_PREFIX}-btn-decline:hover {
163
+ background: #f3f4f6 !important;
164
+ }
165
+ .${CSS_PREFIX}-btn-accept {
166
+ background: #0066ff !important;
167
+ color: #ffffff !important;
168
+ }
169
+ .${CSS_PREFIX}-btn-accept:hover {
170
+ background: #0055d4 !important;
171
+ }
172
+ @media (prefers-color-scheme: dark) {
173
+ .${CSS_PREFIX}-card {
174
+ background: #1c1f24 !important;
175
+ color: #e8eaed !important;
176
+ }
177
+ .${CSS_PREFIX}-body {
178
+ color: #b9bec7 !important;
179
+ }
180
+ .${CSS_PREFIX}-btn-decline {
181
+ color: #b9bec7 !important;
182
+ }
183
+ .${CSS_PREFIX}-btn-decline:hover {
184
+ background: #2a2e35 !important;
185
+ }
186
+ }
187
+ `;
188
+ document.head.appendChild(style);
189
+ }
190
+ function buildOverlay(copy, onDecision) {
191
+ const overlay = document.createElement("div");
192
+ overlay.className = `${CSS_PREFIX}-overlay`;
193
+ overlay.setAttribute("role", "dialog");
194
+ overlay.setAttribute("aria-modal", "true");
195
+ overlay.setAttribute("aria-labelledby", `${CSS_PREFIX}-title`);
196
+ const card = document.createElement("div");
197
+ card.className = `${CSS_PREFIX}-card`;
198
+ const title = document.createElement("h2");
199
+ title.id = `${CSS_PREFIX}-title`;
200
+ title.className = `${CSS_PREFIX}-title`;
201
+ title.textContent = copy.title;
202
+ card.appendChild(title);
203
+ for (const para of copy.body) {
204
+ const p = document.createElement("p");
205
+ p.className = `${CSS_PREFIX}-body`;
206
+ p.textContent = para;
207
+ card.appendChild(p);
208
+ }
209
+ const actions = document.createElement("div");
210
+ actions.className = `${CSS_PREFIX}-actions`;
211
+ const declineBtn = document.createElement("button");
212
+ declineBtn.type = "button";
213
+ declineBtn.className = `${CSS_PREFIX}-btn ${CSS_PREFIX}-btn-decline`;
214
+ declineBtn.textContent = copy.declineLabel;
215
+ declineBtn.addEventListener("click", () => onDecision("denied"));
216
+ const acceptBtn = document.createElement("button");
217
+ acceptBtn.type = "button";
218
+ acceptBtn.className = `${CSS_PREFIX}-btn ${CSS_PREFIX}-btn-accept`;
219
+ acceptBtn.textContent = copy.acceptLabel;
220
+ acceptBtn.addEventListener("click", () => onDecision("granted"));
221
+ actions.appendChild(declineBtn);
222
+ actions.appendChild(acceptBtn);
223
+ card.appendChild(actions);
224
+ overlay.appendChild(card);
225
+ requestAnimationFrame(() => {
226
+ try {
227
+ acceptBtn.focus();
228
+ } catch {
229
+ }
230
+ });
231
+ return overlay;
232
+ }
233
+
234
+ // src/buffer.ts
235
+ var RingBuffer = class {
236
+ constructor(opts) {
237
+ this.opts = opts;
238
+ this.items = [];
239
+ this.dropped = 0;
240
+ }
241
+ push(event) {
242
+ if (this.items.length >= this.opts.capacity) {
243
+ this.items.shift();
244
+ this.dropped++;
245
+ this.opts.onDrop?.(1);
246
+ }
247
+ this.items.push(event);
248
+ }
249
+ drain(max) {
250
+ if (max >= this.items.length) {
251
+ const out = this.items;
252
+ this.items = [];
253
+ return out;
254
+ }
255
+ return this.items.splice(0, max);
256
+ }
257
+ size() {
258
+ return this.items.length;
259
+ }
260
+ droppedCount() {
261
+ return this.dropped;
262
+ }
263
+ };
264
+
265
+ // src/transport.ts
266
+ var DEFAULT_MAX_RETRIES = 3;
267
+ var DEFAULT_POLL_TIMEOUT_MS = 25e3;
268
+ var DEFAULT_MIN_BACKOFF_MS = 2e3;
269
+ var DEFAULT_MAX_BACKOFF_MS = 3e4;
270
+ var Transport = class {
271
+ constructor(opts) {
272
+ this.opts = opts;
273
+ this.timer = null;
274
+ this.inflightUpload = null;
275
+ this.inflightPoll = null;
276
+ this.commandLoopRunning = false;
277
+ this.stopped = false;
278
+ this.flushing = false;
279
+ this.internalFetch = typeof fetch === "function" ? fetch.bind(globalThis) : ((..._args) => {
280
+ throw new Error("[@botim/debug-sdk] fetch is not available in this environment");
281
+ });
282
+ }
283
+ start() {
284
+ if (this.timer) return;
285
+ this.timer = setInterval(
286
+ () => void this.flush().catch((err) => this.opts.onError?.(err)),
287
+ this.opts.flushIntervalMs
288
+ );
289
+ }
290
+ async flush() {
291
+ while (!this.stopped && this.opts.buffer.size() > 0) {
292
+ await this.flushOnce();
293
+ }
294
+ }
295
+ async flushOnce() {
296
+ if (this.flushing || this.opts.buffer.size() === 0) return;
297
+ this.flushing = true;
298
+ try {
299
+ const events = this.opts.buffer.drain(this.opts.maxBatchSize);
300
+ if (events.length === 0) return;
301
+ const batch = {
302
+ sessionToken: this.opts.deviceToken,
303
+ events
304
+ };
305
+ await this.send(batch, 0);
306
+ } finally {
307
+ this.flushing = false;
308
+ }
309
+ }
310
+ async send(batch, attempt) {
311
+ if (this.stopped) return;
312
+ this.inflightUpload = new AbortController();
313
+ try {
314
+ const res = await this.internalFetch(this.opts.ingestUrl, {
315
+ method: "POST",
316
+ headers: {
317
+ "Content-Type": "application/json",
318
+ Authorization: `Bearer ${this.opts.deviceToken}`
319
+ },
320
+ body: JSON.stringify(batch),
321
+ signal: this.inflightUpload.signal
322
+ });
323
+ if (!res.ok) {
324
+ throw new Error(`ingest http ${res.status}`);
325
+ }
326
+ } catch (err) {
327
+ if (this.stopped) return;
328
+ const max = this.opts.maxRetries ?? DEFAULT_MAX_RETRIES;
329
+ if (attempt >= max) {
330
+ this.opts.onError?.(err);
331
+ return;
332
+ }
333
+ const backoff = 250 * Math.pow(4, attempt);
334
+ await new Promise((r) => setTimeout(r, backoff));
335
+ return this.send(batch, attempt + 1);
336
+ } finally {
337
+ this.inflightUpload = null;
338
+ }
339
+ }
340
+ /**
341
+ * Start the AI command long-poll loop. Maintains AT MOST ONE in-flight
342
+ * request to `commandPollUrl` at any time. Errors are reported via
343
+ * `onError` and trigger exponential backoff. Stop with `transport.stop()`.
344
+ */
345
+ startCommandLoop(opts) {
346
+ if (this.commandLoopRunning) return;
347
+ this.commandLoopRunning = true;
348
+ const pollTimeout = opts.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
349
+ const minBackoff = opts.minBackoffMs ?? DEFAULT_MIN_BACKOFF_MS;
350
+ const maxBackoff = opts.maxBackoffMs ?? DEFAULT_MAX_BACKOFF_MS;
351
+ let attempt = 0;
352
+ const loop = async () => {
353
+ while (!this.stopped) {
354
+ this.inflightPoll = new AbortController();
355
+ const timeoutId = setTimeout(() => {
356
+ this.inflightPoll?.abort();
357
+ }, pollTimeout);
358
+ try {
359
+ const res = await this.internalFetch(opts.commandPollUrl, {
360
+ method: "GET",
361
+ headers: { Authorization: `Bearer ${this.opts.deviceToken}` },
362
+ signal: this.inflightPoll.signal
363
+ });
364
+ if (!res.ok) {
365
+ throw new Error(`command-poll http ${res.status}`);
366
+ }
367
+ if (res.status === 204) {
368
+ await sleep(minBackoff);
369
+ attempt = 0;
370
+ continue;
371
+ }
372
+ const json = await res.json();
373
+ attempt = 0;
374
+ for (const cmd of json.commands ?? []) {
375
+ try {
376
+ await opts.onCommand(cmd);
377
+ } catch (err) {
378
+ this.opts.onError?.(err);
379
+ }
380
+ }
381
+ if (typeof json.nextDelayMs === "number" && json.nextDelayMs > 0) {
382
+ await sleep(Math.min(json.nextDelayMs, maxBackoff));
383
+ }
384
+ } catch (err) {
385
+ if (this.stopped) return;
386
+ const aborted = err instanceof Error && (err.name === "AbortError" || /aborted/i.test(err.message));
387
+ if (!aborted) {
388
+ this.opts.onError?.(err);
389
+ attempt += 1;
390
+ } else {
391
+ attempt = 0;
392
+ }
393
+ const backoff = Math.min(
394
+ maxBackoff,
395
+ minBackoff * Math.pow(2, Math.max(0, attempt - 1))
396
+ );
397
+ await sleep(backoff);
398
+ } finally {
399
+ clearTimeout(timeoutId);
400
+ this.inflightPoll = null;
401
+ }
402
+ }
403
+ };
404
+ void loop();
405
+ }
406
+ async stop() {
407
+ if (this.timer) {
408
+ clearInterval(this.timer);
409
+ this.timer = null;
410
+ }
411
+ if (this.opts.buffer.size() > 0) {
412
+ const events = this.opts.buffer.drain(this.opts.buffer.size());
413
+ const body = JSON.stringify({
414
+ sessionToken: this.opts.deviceToken,
415
+ events
416
+ });
417
+ const KEEPALIVE_BODY_LIMIT = 60 * 1024;
418
+ const useKeepalive = body.length <= KEEPALIVE_BODY_LIMIT;
419
+ try {
420
+ await this.internalFetch(this.opts.ingestUrl, {
421
+ method: "POST",
422
+ headers: {
423
+ "Content-Type": "application/json",
424
+ Authorization: `Bearer ${this.opts.deviceToken}`
425
+ },
426
+ body,
427
+ ...useKeepalive ? { keepalive: true } : {}
428
+ });
429
+ } catch (err) {
430
+ this.opts.onError?.(err);
431
+ }
432
+ }
433
+ this.stopped = true;
434
+ this.commandLoopRunning = false;
435
+ this.inflightUpload?.abort();
436
+ this.inflightPoll?.abort();
437
+ }
438
+ };
439
+ function sleep(ms) {
440
+ return new Promise((r) => setTimeout(r, ms));
441
+ }
442
+
443
+ // src/attach.ts
444
+ async function attachDevice(endpoint, config, deviceInfo, consent, token) {
445
+ const base = endpoint.replace(/\/$/, "");
446
+ const url = `${base}/v1/attach`;
447
+ const body = {
448
+ miniProgramId: config.miniProgramId,
449
+ env: config.env,
450
+ buildSignature: config.buildSignature,
451
+ deviceInfo,
452
+ consent,
453
+ schemaVersion: SCHEMA_VERSION
454
+ };
455
+ const headers = { "Content-Type": "application/json" };
456
+ if (token) headers.Authorization = `Bearer ${token}`;
457
+ const res = await fetch(url, {
458
+ method: "POST",
459
+ headers,
460
+ body: JSON.stringify(body)
461
+ });
462
+ if (!res.ok) {
463
+ const text = await res.text().catch(() => "");
464
+ throw new Error(
465
+ `[@botim/debug-sdk] attach failed: ${res.status} ${res.statusText} ${text}`.trim()
466
+ );
467
+ }
468
+ const json = await res.json();
469
+ if (!json?.sid || !json?.deviceToken || !json?.ingestUrl || !json?.commandPollUrl) {
470
+ throw new Error("[@botim/debug-sdk] attach response missing required fields");
471
+ }
472
+ return {
473
+ ...json,
474
+ ingestUrl: resolveAgainstEndpoint(json.ingestUrl, base),
475
+ commandPollUrl: resolveAgainstEndpoint(json.commandPollUrl, base)
476
+ };
477
+ }
478
+ function resolveAgainstEndpoint(url, base) {
479
+ if (/^https?:\/\//i.test(url)) return url;
480
+ const path = url.startsWith("/") ? url : "/" + url;
481
+ return base + path;
482
+ }
483
+ function detectDeviceInfo(app, override) {
484
+ const ua = typeof navigator !== "undefined" ? navigator.userAgent : void 0;
485
+ return {
486
+ deviceId: override?.deviceId ?? loadOrCreateDeviceId(),
487
+ platform: override?.platform ?? detectPlatform(ua),
488
+ osVersion: override?.osVersion,
489
+ appName: override?.appName ?? app.name,
490
+ appVersion: override?.appVersion ?? app.version,
491
+ appBuild: override?.appBuild ?? app.build,
492
+ userAgent: override?.userAgent ?? ua
493
+ };
494
+ }
495
+ function detectPlatform(ua) {
496
+ if (!ua) return "unknown";
497
+ if (/Android/i.test(ua)) return "android";
498
+ if (/iPhone|iPad|iPod/i.test(ua)) return "ios";
499
+ if (/Mozilla|Chrome|Safari|Firefox/i.test(ua)) return "web";
500
+ return "unknown";
501
+ }
502
+ var DEVICE_ID_STORAGE_KEY = "botim-debug-sdk:device-id";
503
+ function loadOrCreateDeviceId() {
504
+ try {
505
+ const ls = typeof localStorage !== "undefined" ? localStorage : null;
506
+ if (ls) {
507
+ const stored = ls.getItem(DEVICE_ID_STORAGE_KEY);
508
+ if (stored && stored.length > 0) return stored;
509
+ const fresh = generateDeviceId();
510
+ ls.setItem(DEVICE_ID_STORAGE_KEY, fresh);
511
+ return fresh;
512
+ }
513
+ } catch {
514
+ }
515
+ return generateDeviceId();
516
+ }
517
+ function generateDeviceId() {
518
+ const c = typeof crypto !== "undefined" ? crypto : void 0;
519
+ if (c?.randomUUID) return c.randomUUID();
520
+ return "dev-" + Math.random().toString(36).slice(2) + Date.now().toString(36);
521
+ }
522
+
523
+ // src/redact.ts
524
+ var REDACTED = "[REDACTED]";
525
+ function redactHeaders(headers, redactList) {
526
+ if (!headers) return headers;
527
+ const lower = new Set(redactList.map((h) => h.toLowerCase()));
528
+ const out = {};
529
+ for (const [k, v] of Object.entries(headers)) {
530
+ out[k] = lower.has(k.toLowerCase()) ? REDACTED : v;
531
+ }
532
+ return out;
533
+ }
534
+ function redactBody(body, patterns, maxBytes) {
535
+ if (body === void 0) return { body: void 0, truncated: false };
536
+ let out = body;
537
+ for (const pat of patterns) {
538
+ if (pat.global) pat.lastIndex = 0;
539
+ out = out.replace(pat, REDACTED);
540
+ }
541
+ let truncated = false;
542
+ if (out.length > maxBytes) {
543
+ out = out.slice(0, maxBytes);
544
+ truncated = true;
545
+ }
546
+ return { body: out, truncated };
547
+ }
548
+ function headersFromInit(init) {
549
+ if (!init) return void 0;
550
+ const out = {};
551
+ if (init instanceof Headers) {
552
+ init.forEach((v, k) => out[k] = v);
553
+ } else if (Array.isArray(init)) {
554
+ for (const [k, v] of init) out[k] = v;
555
+ } else {
556
+ Object.assign(out, init);
557
+ }
558
+ return out;
559
+ }
560
+ function headersFromResponse(res) {
561
+ const out = {};
562
+ res.headers.forEach((v, k) => out[k] = v);
563
+ return out;
564
+ }
565
+ async function readBodyForCapture(body) {
566
+ if (body == null) return void 0;
567
+ if (typeof body === "string") return body;
568
+ if (body instanceof URLSearchParams) return body.toString();
569
+ if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
570
+ try {
571
+ return new TextDecoder().decode(body);
572
+ } catch {
573
+ return "[binary]";
574
+ }
575
+ }
576
+ if (body instanceof Blob) {
577
+ try {
578
+ return await body.text();
579
+ } catch {
580
+ return "[blob]";
581
+ }
582
+ }
583
+ if (body instanceof FormData) {
584
+ const parts = [];
585
+ body.forEach((v, k) => parts.push(`${k}=${typeof v === "string" ? v : "[file]"}`));
586
+ return parts.join("&");
587
+ }
588
+ return void 0;
589
+ }
590
+
591
+ // src/serialize.ts
592
+ var MAX_STRING_LEN = 2048;
593
+ var MAX_OBJECT_BYTES = 4096;
594
+ function serializeArg(value) {
595
+ if (value === void 0) return { k: "undefined" };
596
+ if (value === null) return { k: "primitive", v: null };
597
+ const t = typeof value;
598
+ if (t === "boolean" || t === "number") {
599
+ return { k: "primitive", v: value };
600
+ }
601
+ if (t === "bigint") {
602
+ return { k: "primitive", v: value.toString() + "n" };
603
+ }
604
+ if (t === "string") {
605
+ const s = value;
606
+ if (s.length > MAX_STRING_LEN) {
607
+ return { k: "string", v: s.slice(0, MAX_STRING_LEN), truncated: true };
608
+ }
609
+ return { k: "string", v: s };
610
+ }
611
+ if (t === "function") {
612
+ return { k: "function", name: value.name || void 0 };
613
+ }
614
+ if (value instanceof Error) {
615
+ return { k: "error", name: value.name, message: value.message, stack: value.stack };
616
+ }
617
+ try {
618
+ const seen = /* @__PURE__ */ new WeakSet();
619
+ const json = JSON.stringify(value, function replacer(_k, v) {
620
+ if (typeof v === "bigint") return v.toString() + "n";
621
+ if (typeof v === "function") return `[Function: ${v.name || "anonymous"}]`;
622
+ if (typeof v === "object" && v !== null) {
623
+ if (seen.has(v)) return "[Circular]";
624
+ seen.add(v);
625
+ }
626
+ return v;
627
+ });
628
+ if (json === void 0) {
629
+ return { k: "unserializable", reason: "undefined-result" };
630
+ }
631
+ if (json.length > MAX_OBJECT_BYTES) {
632
+ return { k: "json", v: json.slice(0, MAX_OBJECT_BYTES), truncated: true };
633
+ }
634
+ return { k: "json", v: json };
635
+ } catch (err) {
636
+ return {
637
+ k: "unserializable",
638
+ reason: err instanceof Error ? err.message : "unknown"
639
+ };
640
+ }
641
+ }
642
+
643
+ // src/interceptors/console.ts
644
+ var METHODS = ["log", "info", "warn", "error", "debug"];
645
+ var LEVEL = {
646
+ log: "info",
647
+ info: "info",
648
+ warn: "warn",
649
+ error: "error",
650
+ debug: "debug"
651
+ };
652
+ function installConsoleInterceptor(opts) {
653
+ const originals = {};
654
+ let active = false;
655
+ for (const m of METHODS) {
656
+ const original = console[m];
657
+ originals[m] = original;
658
+ console[m] = function patched(...args) {
659
+ original.apply(console, args);
660
+ if (active) return;
661
+ if (opts.sample < 1 && Math.random() > opts.sample) return;
662
+ active = true;
663
+ try {
664
+ const payload = {
665
+ method: m,
666
+ args: args.map(serializeArg)
667
+ };
668
+ opts.emit(LEVEL[m], payload);
669
+ } catch {
670
+ } finally {
671
+ active = false;
672
+ }
673
+ };
674
+ }
675
+ return () => {
676
+ for (const m of METHODS) {
677
+ const original = originals[m];
678
+ if (original) {
679
+ console[m] = original;
680
+ }
681
+ }
682
+ };
683
+ }
684
+
685
+ // src/interceptors/errors.ts
686
+ function installErrorInterceptor(opts) {
687
+ const target = resolveTarget();
688
+ if (!target) return () => {
689
+ };
690
+ const onError = (ev) => {
691
+ const e = ev;
692
+ const err = e.error;
693
+ opts.emit({
694
+ message: e.message || (err instanceof Error ? err.message : "unknown error"),
695
+ name: err instanceof Error ? err.name : void 0,
696
+ stack: err instanceof Error ? err.stack : void 0,
697
+ source: "window.error",
698
+ filename: e.filename,
699
+ lineno: e.lineno,
700
+ colno: e.colno
701
+ });
702
+ };
703
+ const onRejection = (ev) => {
704
+ const e = ev;
705
+ const reason = e.reason;
706
+ opts.emit({
707
+ message: reason instanceof Error ? reason.message : safeString(reason),
708
+ name: reason instanceof Error ? reason.name : void 0,
709
+ stack: reason instanceof Error ? reason.stack : void 0,
710
+ source: "unhandledrejection"
711
+ });
712
+ };
713
+ target.addEventListener("error", onError);
714
+ target.addEventListener("unhandledrejection", onRejection);
715
+ return () => {
716
+ target.removeEventListener("error", onError);
717
+ target.removeEventListener("unhandledrejection", onRejection);
718
+ };
719
+ }
720
+ function resolveTarget() {
721
+ const w = typeof window !== "undefined" ? window : globalThis;
722
+ if (w && typeof w.addEventListener === "function" && typeof w.removeEventListener === "function") {
723
+ return w;
724
+ }
725
+ return void 0;
726
+ }
727
+ function safeString(v) {
728
+ try {
729
+ return typeof v === "string" ? v : JSON.stringify(v) ?? String(v);
730
+ } catch {
731
+ return String(v);
732
+ }
733
+ }
734
+
735
+ // src/interceptors/network.ts
736
+ function installNetworkInterceptor(opts) {
737
+ const restoreFetch = wrapFetch(opts);
738
+ const restoreXHR = wrapXHR(opts);
739
+ return () => {
740
+ restoreFetch();
741
+ restoreXHR();
742
+ };
743
+ }
744
+ function shouldSample(rate) {
745
+ return rate >= 1 || Math.random() < rate;
746
+ }
747
+ function newReqId() {
748
+ return "r_" + Math.random().toString(36).slice(2, 10);
749
+ }
750
+ function wrapFetch(opts) {
751
+ if (typeof fetch !== "function") return () => {
752
+ };
753
+ const original = fetch;
754
+ const target = globalThis;
755
+ target.fetch = async function patchedFetch(input, init) {
756
+ if (!shouldSample(opts.sample)) return original.call(this, input, init);
757
+ const reqId = newReqId();
758
+ const start = Date.now();
759
+ const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
760
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
761
+ if (opts.shouldSkip && opts.shouldSkip(url, method)) {
762
+ return original.call(this, input, init);
763
+ }
764
+ let reqHeaders;
765
+ let reqBody;
766
+ try {
767
+ if (input instanceof Request) {
768
+ reqHeaders = headersFromResponse(input.clone());
769
+ reqBody = await input.clone().text().catch(() => void 0);
770
+ } else {
771
+ reqHeaders = headersFromInit(init?.headers);
772
+ reqBody = await readBodyForCapture(init?.body);
773
+ }
774
+ } catch {
775
+ }
776
+ opts.emit({ phase: "request", reqId, method, url, reqHeaders, reqBody });
777
+ try {
778
+ const res = await original.call(this, input, init);
779
+ let resBody;
780
+ try {
781
+ resBody = await res.clone().text();
782
+ } catch {
783
+ resBody = void 0;
784
+ }
785
+ opts.emit({
786
+ phase: "response",
787
+ reqId,
788
+ method,
789
+ url,
790
+ status: res.status,
791
+ // statusText carries the human label ("OK", "Not Found"). Pre-HTTP/2
792
+ // responses always have it; HTTP/2+ defines it as empty by spec but
793
+ // most browsers synthesize one from the code, so this is reliable
794
+ // enough to display alongside the status code.
795
+ statusText: res.statusText || void 0,
796
+ durationMs: Date.now() - start,
797
+ resHeaders: headersFromResponse(res),
798
+ resBody
799
+ });
800
+ return res;
801
+ } catch (err) {
802
+ opts.emit({
803
+ phase: "error",
804
+ reqId,
805
+ method,
806
+ url,
807
+ durationMs: Date.now() - start,
808
+ errorMessage: err instanceof Error ? err.message : String(err),
809
+ errorName: err instanceof Error ? err.name : void 0,
810
+ // Stack from the rejected promise — points into fetch internals
811
+ // and (when present) the call site that issued the request.
812
+ errorStack: err instanceof Error ? err.stack : void 0,
813
+ // undici frequently wraps the real reason in `cause` (e.g.
814
+ // `TypeError: fetch failed` outside, `Error: ECONNREFUSED` inside).
815
+ // Flatten the chain so operators don't have to dig.
816
+ errorCause: collectCauseChain(err)
817
+ });
818
+ throw err;
819
+ }
820
+ };
821
+ return () => {
822
+ target.fetch = original;
823
+ };
824
+ }
825
+ function collectCauseChain(err) {
826
+ if (!err || typeof err !== "object") return void 0;
827
+ const lines = [];
828
+ let cur = err.cause;
829
+ const seen = /* @__PURE__ */ new Set();
830
+ while (cur && lines.length < 5) {
831
+ if (seen.has(cur)) break;
832
+ seen.add(cur);
833
+ if (cur instanceof Error) {
834
+ lines.push(`${cur.name}: ${cur.message}`);
835
+ cur = cur.cause;
836
+ } else {
837
+ lines.push(String(cur));
838
+ cur = cur?.cause;
839
+ }
840
+ }
841
+ return lines.length ? lines.join("\n") : void 0;
842
+ }
843
+ function wrapXHR(opts) {
844
+ if (typeof XMLHttpRequest === "undefined") return () => {
845
+ };
846
+ const proto = XMLHttpRequest.prototype;
847
+ const states = /* @__PURE__ */ new WeakMap();
848
+ const origOpen = proto.open;
849
+ const origSend = proto.send;
850
+ const origSetReqHeader = proto.setRequestHeader;
851
+ proto.open = function patchedOpen(method, url, ...rest) {
852
+ const resolvedUrl = typeof url === "string" ? url : url.toString();
853
+ const upperMethod = method.toUpperCase();
854
+ const skipped = opts.shouldSkip ? opts.shouldSkip(resolvedUrl, upperMethod) : false;
855
+ const state = {
856
+ reqId: newReqId(),
857
+ start: 0,
858
+ method: upperMethod,
859
+ url: resolvedUrl,
860
+ reqHeaders: {},
861
+ sampled: !skipped && shouldSample(opts.sample)
862
+ };
863
+ states.set(this, state);
864
+ return origOpen.apply(this, [method, url, ...rest]);
865
+ };
866
+ proto.setRequestHeader = function patchedSetHeader(name, value) {
867
+ const s = states.get(this);
868
+ if (s) s.reqHeaders[name] = value;
869
+ return origSetReqHeader.call(this, name, value);
870
+ };
871
+ proto.send = function patchedSend(body) {
872
+ const s = states.get(this);
873
+ if (!s || !s.sampled) {
874
+ return origSend.apply(this, [body]);
875
+ }
876
+ s.start = Date.now();
877
+ s.reqBody = typeof body === "string" ? body : void 0;
878
+ const sendSiteStack = captureCallSiteStack();
879
+ opts.emit({
880
+ phase: "request",
881
+ reqId: s.reqId,
882
+ method: s.method,
883
+ url: s.url,
884
+ reqHeaders: s.reqHeaders,
885
+ reqBody: s.reqBody
886
+ });
887
+ const onLoad = () => {
888
+ const headers = parseXhrHeaders(this.getAllResponseHeaders());
889
+ const resBody = typeof this.response === "string" ? this.response : void 0;
890
+ opts.emit({
891
+ phase: "response",
892
+ reqId: s.reqId,
893
+ method: s.method,
894
+ url: s.url,
895
+ status: this.status,
896
+ // XHR exposes statusText directly; same display purpose as fetch.
897
+ statusText: this.statusText || void 0,
898
+ durationMs: Date.now() - s.start,
899
+ resHeaders: headers,
900
+ resBody
901
+ });
902
+ };
903
+ const onError = (kind) => () => {
904
+ opts.emit({
905
+ phase: "error",
906
+ reqId: s.reqId,
907
+ method: s.method,
908
+ url: s.url,
909
+ durationMs: Date.now() - s.start,
910
+ // Distinguish error/timeout/abort in the message — the standard
911
+ // XHR `statusText` is empty for `error` and unhelpful for the
912
+ // others, so we synthesise a clear label.
913
+ errorMessage: this.statusText || `xhr ${kind}`,
914
+ errorName: `XHR${kind[0].toUpperCase()}${kind.slice(1)}`,
915
+ errorStack: sendSiteStack
916
+ });
917
+ };
918
+ this.addEventListener("load", onLoad);
919
+ this.addEventListener("error", onError("error"));
920
+ this.addEventListener("timeout", onError("timeout"));
921
+ this.addEventListener("abort", onError("abort"));
922
+ return origSend.apply(this, [body]);
923
+ };
924
+ return () => {
925
+ proto.open = origOpen;
926
+ proto.send = origSend;
927
+ proto.setRequestHeader = origSetReqHeader;
928
+ };
929
+ }
930
+ function captureCallSiteStack() {
931
+ try {
932
+ throw new Error("xhr-callsite");
933
+ } catch (err) {
934
+ if (!(err instanceof Error) || !err.stack) return void 0;
935
+ const lines = err.stack.split("\n");
936
+ return lines.slice(2).join("\n") || void 0;
937
+ }
938
+ }
939
+ function parseXhrHeaders(raw) {
940
+ const out = {};
941
+ if (!raw) return out;
942
+ for (const line of raw.split(/\r?\n/)) {
943
+ const idx = line.indexOf(":");
944
+ if (idx < 0) continue;
945
+ const k = line.slice(0, idx).trim();
946
+ const v = line.slice(idx + 1).trim();
947
+ if (k) out[k] = v;
948
+ }
949
+ return out;
950
+ }
951
+
952
+ // src/commands/registry.ts
953
+ var NAME_PATTERN = /^[a-z][a-z0-9-]{1,47}$/;
954
+ var CommandRegistry = class {
955
+ constructor() {
956
+ this.handlers = /* @__PURE__ */ new Map();
957
+ this.overrideListeners = [];
958
+ }
959
+ register(name, handler) {
960
+ if (typeof name !== "string" || !NAME_PATTERN.test(name)) {
961
+ throw new Error(
962
+ `[@botim/debug-sdk] invalid command name ${JSON.stringify(name)}; expected kebab-case matching ${NAME_PATTERN}`
963
+ );
964
+ }
965
+ if (typeof handler !== "function") {
966
+ throw new Error(`[@botim/debug-sdk] command "${name}" handler must be a function`);
967
+ }
968
+ const isOverride = this.handlers.has(name);
969
+ this.handlers.set(name, handler);
970
+ if (isOverride) {
971
+ for (const fn of this.overrideListeners) {
972
+ try {
973
+ fn(name);
974
+ } catch {
975
+ }
976
+ }
977
+ }
978
+ }
979
+ unregister(name) {
980
+ return this.handlers.delete(name);
981
+ }
982
+ has(name) {
983
+ return this.handlers.has(name);
984
+ }
985
+ /** Subscribe to override events; returns an unsubscribe fn. */
986
+ onOverride(fn) {
987
+ this.overrideListeners.push(fn);
988
+ return () => {
989
+ this.overrideListeners = this.overrideListeners.filter((f) => f !== fn);
990
+ };
991
+ }
992
+ /**
993
+ * Dispatch a single command. Never throws; returns a typed result.
994
+ * Caller is responsible for mapping the result onto an event envelope.
995
+ */
996
+ async dispatch(req, signal) {
997
+ const handler = this.handlers.get(req.name);
998
+ if (!handler) {
999
+ return {
1000
+ kind: "rejected",
1001
+ payload: { command: req.name, reason: "unknown-command" }
1002
+ };
1003
+ }
1004
+ const start = Date.now();
1005
+ try {
1006
+ const args = req.args ?? {};
1007
+ const result = await Promise.resolve(
1008
+ handler(args, { commandId: req.id, command: req.name, signal })
1009
+ );
1010
+ return {
1011
+ kind: "ack",
1012
+ payload: {
1013
+ command: req.name,
1014
+ ok: true,
1015
+ result,
1016
+ durationMs: Date.now() - start
1017
+ }
1018
+ };
1019
+ } catch (err) {
1020
+ return {
1021
+ kind: "rejected",
1022
+ payload: {
1023
+ command: req.name,
1024
+ reason: "handler-threw",
1025
+ details: err instanceof Error ? err.message : String(err)
1026
+ }
1027
+ };
1028
+ }
1029
+ }
1030
+ };
1031
+ var MAX_DUMP_BYTES = 64 * 1024;
1032
+ var MAX_SCREENSHOT_BYTES = 1024 * 1024;
1033
+ var MAX_EXEC_CODE_BYTES = 8 * 1024;
1034
+ var DEFAULT_EXEC_TIMEOUT_MS = 5e3;
1035
+ var MIN_EXEC_TIMEOUT_MS = 250;
1036
+ var MAX_EXEC_TIMEOUT_MS = 3e4;
1037
+ async function defaultDomScreenshot() {
1038
+ if (typeof document === "undefined" || typeof window === "undefined") {
1039
+ throw new Error(
1040
+ "[@botim/debug-sdk] default screenshot requires a DOM. Provide builtins.screenshot for non-browser runtimes (e.g. native bridge)."
1041
+ );
1042
+ }
1043
+ const injected = injectAdoptedStyleSheets();
1044
+ let tree;
1045
+ try {
1046
+ tree = snapshot(document, {
1047
+ inlineStylesheet: true,
1048
+ inlineImages: true,
1049
+ recordCanvas: true,
1050
+ // Deliberately NOT slimming. SlimDOM drops <link rel="preload">,
1051
+ // hidden form metadata, and other "noise" that's actually relevant
1052
+ // when reproducing a layout bug.
1053
+ slimDOM: false,
1054
+ // Don't mask anything by default — debug-relay already runs a
1055
+ // top-level redactor on console payloads, and on-screen text is the
1056
+ // whole point of capturing a screenshot. Hosts that need PII masking
1057
+ // can wire their own builtins.screenshot using rrweb-snapshot's
1058
+ // `maskTextSelector` / `maskInputOptions`.
1059
+ maskAllInputs: false
1060
+ });
1061
+ } finally {
1062
+ for (const node of injected) {
1063
+ try {
1064
+ node.remove();
1065
+ } catch {
1066
+ }
1067
+ }
1068
+ }
1069
+ if (!tree) {
1070
+ throw new Error("[@botim/debug-sdk] rrweb-snapshot returned null tree");
1071
+ }
1072
+ const payload = {
1073
+ snapshot: tree,
1074
+ viewport: {
1075
+ w: window.innerWidth || document.documentElement.clientWidth || 0,
1076
+ h: window.innerHeight || document.documentElement.clientHeight || 0
1077
+ },
1078
+ url: location.href,
1079
+ capturedAt: Date.now()
1080
+ };
1081
+ return {
1082
+ data: JSON.stringify(payload),
1083
+ format: "rrweb-snapshot"
1084
+ };
1085
+ }
1086
+ function injectAdoptedStyleSheets() {
1087
+ const injected = [];
1088
+ const collect = (sheets) => {
1089
+ if (!sheets || sheets.length === 0) return "";
1090
+ const chunks = [];
1091
+ for (const sheet of sheets) {
1092
+ try {
1093
+ const rules = sheet.cssRules;
1094
+ for (const rule of Array.from(rules)) chunks.push(rule.cssText);
1095
+ } catch {
1096
+ }
1097
+ }
1098
+ return chunks.join("\n");
1099
+ };
1100
+ const inject = (parent, css) => {
1101
+ if (!css) return;
1102
+ const ownerDoc = parent instanceof Document ? parent : parent.ownerDocument;
1103
+ if (!ownerDoc) return;
1104
+ const style = ownerDoc.createElement("style");
1105
+ style.setAttribute("data-botim-adopted", "1");
1106
+ style.textContent = css;
1107
+ const target = parent instanceof Document ? parent.head ?? parent.documentElement ?? parent.body : parent;
1108
+ if (target) {
1109
+ target.insertBefore(style, target.firstChild);
1110
+ injected.push(style);
1111
+ }
1112
+ };
1113
+ try {
1114
+ inject(
1115
+ document,
1116
+ collect(document.adoptedStyleSheets)
1117
+ );
1118
+ } catch {
1119
+ }
1120
+ const walkRoot = (root) => {
1121
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
1122
+ let n = walker.currentNode;
1123
+ while (n) {
1124
+ const el = n;
1125
+ const sr = el.shadowRoot;
1126
+ if (sr) {
1127
+ try {
1128
+ inject(
1129
+ sr,
1130
+ collect(sr.adoptedStyleSheets)
1131
+ );
1132
+ } catch {
1133
+ }
1134
+ walkRoot(sr);
1135
+ }
1136
+ n = walker.nextNode();
1137
+ }
1138
+ };
1139
+ try {
1140
+ walkRoot(document);
1141
+ } catch {
1142
+ }
1143
+ return injected;
1144
+ }
1145
+ function registerBuiltins(registry, hooks = {}) {
1146
+ registry.register("ping", ping);
1147
+ registry.register("reload", makeReload(hooks.reload));
1148
+ registry.register("dump-state", makeDumpState(hooks.getState));
1149
+ registry.register("set-feature-flag", makeSetFlag(hooks.setFeatureFlag));
1150
+ registry.register("screenshot", makeScreenshot(hooks.screenshot));
1151
+ if (hooks.exec !== false) {
1152
+ registry.register("exec", makeExec(hooks.exec ?? {}));
1153
+ }
1154
+ }
1155
+ var ping = () => ({ ok: true, ts: Date.now() });
1156
+ function makeReload(reload) {
1157
+ return async () => {
1158
+ if (!reload) throw new Error("reload hook not registered by host");
1159
+ await reload();
1160
+ return { reloaded: true };
1161
+ };
1162
+ }
1163
+ function makeDumpState(getState) {
1164
+ return async () => {
1165
+ if (!getState) throw new Error("getState hook not registered by host");
1166
+ const snapshot = await getState();
1167
+ const json = safeStringify(snapshot);
1168
+ const truncated = json.length > MAX_DUMP_BYTES;
1169
+ return {
1170
+ state: truncated ? json.slice(0, MAX_DUMP_BYTES) : json,
1171
+ truncated,
1172
+ bytes: json.length
1173
+ };
1174
+ };
1175
+ }
1176
+ function makeSetFlag(setFeatureFlag) {
1177
+ return async (args) => {
1178
+ if (!setFeatureFlag) throw new Error("setFeatureFlag hook not registered by host");
1179
+ const key = args.key;
1180
+ if (typeof key !== "string" || key.length === 0) {
1181
+ throw new Error("args.key must be a non-empty string");
1182
+ }
1183
+ if (!("value" in args)) {
1184
+ throw new Error("args.value is required");
1185
+ }
1186
+ await setFeatureFlag(key, args.value);
1187
+ return { applied: true, key };
1188
+ };
1189
+ }
1190
+ function makeScreenshot(screenshot) {
1191
+ const capture = screenshot ?? defaultDomScreenshot;
1192
+ return async () => {
1193
+ const result = await capture();
1194
+ let data;
1195
+ let format;
1196
+ if (typeof result === "string") {
1197
+ data = result;
1198
+ format = "png-base64";
1199
+ } else if (result && typeof result.data === "string") {
1200
+ data = result.data;
1201
+ format = result.format ?? "png-base64";
1202
+ } else {
1203
+ throw new Error(
1204
+ "screenshot hook must return a base64 string or { data, format }"
1205
+ );
1206
+ }
1207
+ if (data.length === 0) throw new Error("screenshot hook returned empty data");
1208
+ if (data.length > MAX_SCREENSHOT_BYTES) {
1209
+ throw new Error(
1210
+ `screenshot ${data.length} bytes exceeds limit ${MAX_SCREENSHOT_BYTES}`
1211
+ );
1212
+ }
1213
+ return { format, data, bytes: data.length };
1214
+ };
1215
+ }
1216
+ var cachedBOT = void 0;
1217
+ function getBOT() {
1218
+ if (cachedBOT !== void 0) return cachedBOT;
1219
+ if (typeof window === "undefined") {
1220
+ cachedBOT = null;
1221
+ return null;
1222
+ }
1223
+ try {
1224
+ const w = window;
1225
+ if (w.BOT) {
1226
+ cachedBOT = w.BOT;
1227
+ return cachedBOT;
1228
+ }
1229
+ } catch {
1230
+ }
1231
+ try {
1232
+ const w = window;
1233
+ const ng = w.angular;
1234
+ if (ng?.element && typeof document !== "undefined") {
1235
+ const injector = ng.element(document).injector?.();
1236
+ const bot = injector?.get?.("BOT");
1237
+ if (bot != null) {
1238
+ cachedBOT = bot;
1239
+ return cachedBOT;
1240
+ }
1241
+ }
1242
+ } catch {
1243
+ }
1244
+ return null;
1245
+ }
1246
+ function makeExec(opts) {
1247
+ const extraGlobalNames = Object.keys(opts.globals ?? {});
1248
+ const extraGlobalValues = extraGlobalNames.map((k) => opts.globals[k]);
1249
+ return async (args, ctx) => {
1250
+ if (typeof args.code !== "string" || args.code.length === 0) {
1251
+ throw new Error("args.code (non-empty string) is required");
1252
+ }
1253
+ if (args.code.length > MAX_EXEC_CODE_BYTES) {
1254
+ throw new Error(`args.code ${args.code.length} bytes exceeds limit ${MAX_EXEC_CODE_BYTES}`);
1255
+ }
1256
+ const timeoutMs = clamp(
1257
+ typeof args.timeoutMs === "number" ? args.timeoutMs : DEFAULT_EXEC_TIMEOUT_MS,
1258
+ MIN_EXEC_TIMEOUT_MS,
1259
+ MAX_EXEC_TIMEOUT_MS
1260
+ );
1261
+ const start = Date.now();
1262
+ const logs = [];
1263
+ const consoleMethods = ["log", "info", "warn", "error", "debug"];
1264
+ const originals = {};
1265
+ if (typeof console !== "undefined") {
1266
+ for (const m of consoleMethods) {
1267
+ const orig = console[m];
1268
+ if (typeof orig !== "function") continue;
1269
+ originals[m] = orig;
1270
+ const captured = (...callArgs) => {
1271
+ logs.push({ method: m, args: callArgs.map(execSafeClone), ts: Date.now() });
1272
+ try {
1273
+ orig.call(console, ...callArgs);
1274
+ } catch {
1275
+ }
1276
+ };
1277
+ console[m] = captured;
1278
+ }
1279
+ }
1280
+ let value;
1281
+ let threw = void 0;
1282
+ try {
1283
+ const AsyncFunction = Object.getPrototypeOf(async function() {
1284
+ }).constructor;
1285
+ const localNames = ["BOT", "window", "document", ...extraGlobalNames];
1286
+ const localValues = [
1287
+ getBOT(),
1288
+ typeof window !== "undefined" ? window : void 0,
1289
+ typeof document !== "undefined" ? document : void 0,
1290
+ ...extraGlobalValues
1291
+ ];
1292
+ let fnTry = null;
1293
+ try {
1294
+ fnTry = new AsyncFunction(...localNames, `return (${args.code});`);
1295
+ } catch (e) {
1296
+ if (!(e instanceof SyntaxError)) throw e;
1297
+ }
1298
+ const fn = fnTry ?? new AsyncFunction(...localNames, args.code);
1299
+ let timer = null;
1300
+ let abortReject = null;
1301
+ const onAbort = () => abortReject?.(new Error("exec cancelled"));
1302
+ ctx.signal?.addEventListener?.("abort", onAbort);
1303
+ try {
1304
+ value = await Promise.race([
1305
+ fn(...localValues),
1306
+ new Promise((_, reject) => {
1307
+ timer = setTimeout(
1308
+ () => reject(new Error(`exec exceeded ${timeoutMs}ms`)),
1309
+ timeoutMs
1310
+ );
1311
+ }),
1312
+ new Promise((_, reject) => {
1313
+ abortReject = reject;
1314
+ })
1315
+ ]);
1316
+ } finally {
1317
+ if (timer) clearTimeout(timer);
1318
+ ctx.signal?.removeEventListener?.("abort", onAbort);
1319
+ }
1320
+ } catch (err) {
1321
+ threw = err;
1322
+ } finally {
1323
+ if (typeof console !== "undefined") {
1324
+ for (const m of consoleMethods) {
1325
+ if (originals[m]) {
1326
+ console[m] = originals[m];
1327
+ }
1328
+ }
1329
+ }
1330
+ }
1331
+ if (threw !== void 0) {
1332
+ const e = threw instanceof Error ? threw : new Error(String(threw));
1333
+ const stackTruncated = typeof e.stack === "string" ? e.stack.split("\n").slice(0, 20).join("\n") : void 0;
1334
+ const detail = {
1335
+ name: e.name,
1336
+ message: e.message,
1337
+ stack: stackTruncated,
1338
+ logs,
1339
+ durationMs: Date.now() - start
1340
+ };
1341
+ const wrapped = new Error(JSON.stringify(detail));
1342
+ wrapped.detail = detail;
1343
+ throw wrapped;
1344
+ }
1345
+ return {
1346
+ ok: true,
1347
+ value: execSafeClone(value),
1348
+ logs,
1349
+ durationMs: Date.now() - start
1350
+ };
1351
+ };
1352
+ }
1353
+ function clamp(n, lo, hi) {
1354
+ if (!Number.isFinite(n)) return lo;
1355
+ return Math.min(Math.max(n, lo), hi);
1356
+ }
1357
+ function execSafeClone(v) {
1358
+ try {
1359
+ return JSON.parse(JSON.stringify(v, (_k, val) => {
1360
+ if (typeof val === "function") {
1361
+ return `[Function: ${val.name || "anonymous"}]`;
1362
+ }
1363
+ if (val instanceof Error) {
1364
+ return { name: val.name, message: val.message, stack: val.stack };
1365
+ }
1366
+ if (typeof val === "bigint") return val.toString() + "n";
1367
+ if (typeof val === "undefined") return null;
1368
+ if (val && typeof val === "object" && val.nodeType === 1) {
1369
+ const el = val;
1370
+ return `[${el.tagName ?? "Element"}${el.id ? "#" + el.id : ""}]`;
1371
+ }
1372
+ return val;
1373
+ }));
1374
+ } catch {
1375
+ return { __unserializable: typeof v };
1376
+ }
1377
+ }
1378
+ function safeStringify(v) {
1379
+ try {
1380
+ const seen = /* @__PURE__ */ new WeakSet();
1381
+ return JSON.stringify(v, function replacer(_k, val) {
1382
+ if (typeof val === "bigint") return val.toString() + "n";
1383
+ if (typeof val === "function") return `[Function: ${val.name ?? "anonymous"}]`;
1384
+ if (typeof val === "object" && val !== null) {
1385
+ if (seen.has(val)) return "[Circular]";
1386
+ seen.add(val);
1387
+ }
1388
+ return val;
1389
+ }) ?? "";
1390
+ } catch (err) {
1391
+ return JSON.stringify({ __unserializable: err instanceof Error ? err.message : "unknown" });
1392
+ }
1393
+ }
1394
+
1395
+ // src/dedup.ts
1396
+ var DEFAULT_WINDOW_MS = 1e3;
1397
+ var DEFAULT_MAX_SIGNATURES = 500;
1398
+ var DEDUPED_TYPES = ["console", "network", "error"];
1399
+ var EventDeduper = class {
1400
+ constructor(onSummary, opts = {}) {
1401
+ this.entries = /* @__PURE__ */ new Map();
1402
+ this.timer = null;
1403
+ this.enabled = opts.enabled !== false;
1404
+ this.windowMs = opts.windowMs ?? DEFAULT_WINDOW_MS;
1405
+ this.maxSig = opts.maxSignatures ?? DEFAULT_MAX_SIGNATURES;
1406
+ this.onSummary = onSummary;
1407
+ if (this.enabled) {
1408
+ this.timer = setInterval(() => this.sweep(Date.now()), Math.max(250, Math.floor(this.windowMs / 2)));
1409
+ }
1410
+ }
1411
+ /**
1412
+ * Returns true if the SDK should emit `ev` to the buffer; false if it's a
1413
+ * duplicate within the current window and should be suppressed.
1414
+ */
1415
+ shouldEmit(ev) {
1416
+ if (!this.enabled) return true;
1417
+ if (!DEDUPED_TYPES.includes(ev.type)) return true;
1418
+ const sig = this.signatureOf(ev);
1419
+ const now = ev.ts;
1420
+ const existing = this.entries.get(sig);
1421
+ if (!existing) {
1422
+ this.evictIfFull();
1423
+ this.entries.set(sig, {
1424
+ type: ev.type,
1425
+ level: ev.level,
1426
+ firstTs: now,
1427
+ lastTs: now,
1428
+ count: 1,
1429
+ label: this.labelOf(ev),
1430
+ lastPayload: ev.payload
1431
+ });
1432
+ return true;
1433
+ }
1434
+ if (now - existing.firstTs >= this.windowMs) {
1435
+ if (existing.count > 1) this.emitSummary(sig, existing);
1436
+ this.entries.set(sig, {
1437
+ type: ev.type,
1438
+ level: ev.level,
1439
+ firstTs: now,
1440
+ lastTs: now,
1441
+ count: 1,
1442
+ label: this.labelOf(ev),
1443
+ lastPayload: ev.payload
1444
+ });
1445
+ return true;
1446
+ }
1447
+ existing.lastTs = now;
1448
+ existing.count++;
1449
+ existing.lastPayload = ev.payload;
1450
+ return false;
1451
+ }
1452
+ /**
1453
+ * Force-flush any pending rollups (e.g. before stop). After this returns,
1454
+ * the internal map is empty.
1455
+ */
1456
+ flushAll() {
1457
+ for (const [sig, e] of this.entries) {
1458
+ if (e.count > 1) this.emitSummary(sig, e);
1459
+ }
1460
+ this.entries.clear();
1461
+ }
1462
+ stop() {
1463
+ if (this.timer) {
1464
+ clearInterval(this.timer);
1465
+ this.timer = null;
1466
+ }
1467
+ this.flushAll();
1468
+ }
1469
+ // ─────────────────────────────────────────────────────────────────────────
1470
+ sweep(now) {
1471
+ for (const [sig, e] of this.entries) {
1472
+ if (now - e.firstTs >= this.windowMs) {
1473
+ if (e.count > 1) this.emitSummary(sig, e);
1474
+ this.entries.delete(sig);
1475
+ }
1476
+ }
1477
+ }
1478
+ emitSummary(sig, e) {
1479
+ try {
1480
+ this.onSummary({
1481
+ signature: sig,
1482
+ type: e.type,
1483
+ level: e.level,
1484
+ count: e.count - 1,
1485
+ // first occurrence already emitted as a real event
1486
+ firstTs: e.firstTs,
1487
+ lastTs: e.lastTs,
1488
+ label: e.label,
1489
+ examplePayload: e.lastPayload
1490
+ });
1491
+ } catch {
1492
+ }
1493
+ }
1494
+ evictIfFull() {
1495
+ if (this.entries.size < this.maxSig) return;
1496
+ const firstKey = this.entries.keys().next().value;
1497
+ if (typeof firstKey === "string") {
1498
+ const e = this.entries.get(firstKey);
1499
+ if (e && e.count > 1) this.emitSummary(firstKey, e);
1500
+ this.entries.delete(firstKey);
1501
+ }
1502
+ }
1503
+ // ─── Signature & label generators ───────────────────────────────────────
1504
+ // Signatures are STRUCTURAL: they identify "the same kind of event" rather
1505
+ // than the same bytes. So `console.error("x" + Date.now())` collapses by
1506
+ // method + first-token of args; an HTTP 500 on the same URL collapses
1507
+ // regardless of body content.
1508
+ signatureOf(ev) {
1509
+ if (ev.type === "console") {
1510
+ const p = ev.payload;
1511
+ const args = (p.args || []).map(consoleArgToken).join(" ").slice(0, 240);
1512
+ return `c:${p.method}:${args}`;
1513
+ }
1514
+ if (ev.type === "network") {
1515
+ const p = ev.payload;
1516
+ return `n:${p.phase}:${p.method}:${p.url}:${p.status ?? ""}:${p.errorName ?? ""}`;
1517
+ }
1518
+ if (ev.type === "error") {
1519
+ const p = ev.payload;
1520
+ return `e:${p.source}:${p.name ?? ""}:${(p.message ?? "").slice(0, 240)}`;
1521
+ }
1522
+ return `${ev.type}:${ev.level}`;
1523
+ }
1524
+ labelOf(ev) {
1525
+ if (ev.type === "console") {
1526
+ const p = ev.payload;
1527
+ return `console.${p.method}: ${(p.args || []).map(consoleArgToken).join(" ").slice(0, 160)}`;
1528
+ }
1529
+ if (ev.type === "network") {
1530
+ const p = ev.payload;
1531
+ return `${p.method} ${p.url} (${p.phase}${p.status ? " " + p.status : ""})`;
1532
+ }
1533
+ if (ev.type === "error") {
1534
+ const p = ev.payload;
1535
+ return `${p.name ?? "Error"}: ${(p.message ?? "").slice(0, 160)}`;
1536
+ }
1537
+ return ev.type;
1538
+ }
1539
+ };
1540
+ function consoleArgToken(a) {
1541
+ switch (a.k) {
1542
+ case "primitive":
1543
+ return String(a.v);
1544
+ case "string":
1545
+ case "json":
1546
+ return String(a.v).slice(0, 80);
1547
+ case "error":
1548
+ return `${a.name ?? "Error"}:${a.message ?? ""}`;
1549
+ case "undefined":
1550
+ return "undefined";
1551
+ case "function":
1552
+ return `[fn:${a.name ?? "?"}]`;
1553
+ case "circular":
1554
+ return "[Circular]";
1555
+ case "unserializable":
1556
+ return `[unser:${a.reason}]`;
1557
+ default:
1558
+ return "?";
1559
+ }
1560
+ }
1561
+
1562
+ // src/index.ts
1563
+ var DEFAULT_REDACT_HEADERS = [
1564
+ "authorization",
1565
+ "cookie",
1566
+ "set-cookie",
1567
+ "x-api-key",
1568
+ "x-auth-token",
1569
+ "proxy-authorization"
1570
+ ];
1571
+ var DEFAULT_BODY_PATTERNS = [
1572
+ /(?:bearer\s+)?[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/gi,
1573
+ /[A-Za-z0-9_-]{40,}/g
1574
+ ];
1575
+ var DEFAULT_MAX_BODY_BYTES = 4096;
1576
+ var DEFAULT_FLUSH_INTERVAL_MS = 4e3;
1577
+ var DEFAULT_BUFFER_SIZE = 1e3;
1578
+ var DEFAULT_MAX_BATCH_SIZE = 50;
1579
+ var NOOP_HANDLE = {
1580
+ sid: null,
1581
+ async flush() {
1582
+ },
1583
+ async stop() {
1584
+ },
1585
+ registerCommand() {
1586
+ },
1587
+ unregisterCommand() {
1588
+ return false;
1589
+ }
1590
+ };
1591
+ var defaultBotimConfig = null;
1592
+ function setDefaultBotimConfig(config) {
1593
+ defaultBotimConfig = config;
1594
+ }
1595
+ function assertConsent(config, consent) {
1596
+ if (config.env !== "prod") return;
1597
+ const hasHostToken = typeof consent?.hostToken === "string" && consent.hostToken.length > 0;
1598
+ const hasOptIn = consent?.userOptIn === true;
1599
+ if (hasHostToken || hasOptIn) return;
1600
+ throw new BotimConsentError(
1601
+ "prod debug sessions require consent.hostToken or consent.userOptIn === true"
1602
+ );
1603
+ }
1604
+ function assertConfig(config) {
1605
+ if (!config || typeof config !== "object") {
1606
+ throw new BotimConfigError(
1607
+ 'config is required \u2014 pass options.config, or register a default via "@botim/mp-debug-sdk/auto"',
1608
+ { code: "config-missing" }
1609
+ );
1610
+ }
1611
+ if (typeof config.miniProgramId !== "string" || config.miniProgramId.length === 0) {
1612
+ throw new BotimConfigError("options.config.miniProgramId must be a non-empty string", {
1613
+ code: "config-missing-field"
1614
+ });
1615
+ }
1616
+ if (config.env !== "dev" && config.env !== "uat" && config.env !== "beta" && config.env !== "prod") {
1617
+ throw new BotimConfigError(
1618
+ `options.config.env must be one of dev|uat|beta|prod (got ${JSON.stringify(config.env)})`,
1619
+ { code: "config-invalid-env" }
1620
+ );
1621
+ }
1622
+ }
1623
+ async function enableRemoteDebug(options) {
1624
+ if (options.enabled === false) return NOOP_HANDLE;
1625
+ if (!options.endpoint || typeof options.endpoint !== "string") {
1626
+ throw new BotimConfigError(
1627
+ 'relay server URL is required \u2014 pass options.endpoint (e.g. "https://relay.example.com")',
1628
+ { code: "endpoint-missing" }
1629
+ );
1630
+ }
1631
+ const resolvedConfig = options.config ?? defaultBotimConfig;
1632
+ if (!resolvedConfig) {
1633
+ throw new BotimConfigError(
1634
+ 'no config provided and no default registered \u2014 import { enableRemoteDebug } from "@botim/mp-debug-sdk/auto" (Vite), or pass options.config',
1635
+ { code: "config-missing" }
1636
+ );
1637
+ }
1638
+ assertConfig(resolvedConfig);
1639
+ const consentInput = options.consent ?? {};
1640
+ const hasExplicitConsent = consentInput.userOptIn === true || typeof consentInput.hostToken === "string" && consentInput.hostToken.length > 0;
1641
+ const promptDisabled = consentInput.promptUser === false;
1642
+ const canPrompt = typeof document !== "undefined" && typeof window !== "undefined";
1643
+ const shouldPrompt = !hasExplicitConsent && !promptDisabled && canPrompt;
1644
+ if (shouldPrompt) {
1645
+ const cached = readConsentDecision(resolvedConfig.miniProgramId);
1646
+ let decision = cached;
1647
+ if (decision === null) {
1648
+ decision = await promptForConsent(
1649
+ resolvedConfig.miniProgramId,
1650
+ consentInput.promptCopy
1651
+ );
1652
+ }
1653
+ if (decision === "denied") return NOOP_HANDLE;
1654
+ options = {
1655
+ ...options,
1656
+ consent: { ...consentInput, userOptIn: true }
1657
+ };
1658
+ }
1659
+ assertConsent(resolvedConfig, options.consent);
1660
+ const onError = options.onError ?? (() => {
1661
+ });
1662
+ const app = options.app ?? {
1663
+ name: resolvedConfig.appName ?? resolvedConfig.miniProgramId,
1664
+ version: resolvedConfig.appVersion ?? "0.0.0"
1665
+ };
1666
+ const deviceInfo = detectDeviceInfo(app, options.device);
1667
+ const consent = options.consent ?? {};
1668
+ const session = await attachDevice(options.endpoint, resolvedConfig, deviceInfo, consent, options.token);
1669
+ const buffer = new RingBuffer({
1670
+ capacity: options.bufferSize ?? DEFAULT_BUFFER_SIZE
1671
+ });
1672
+ const transport = new Transport({
1673
+ ingestUrl: session.ingestUrl,
1674
+ deviceToken: session.deviceToken,
1675
+ buffer,
1676
+ flushIntervalMs: options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,
1677
+ maxBatchSize: options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,
1678
+ maxRetries: options.maxRetries,
1679
+ onError
1680
+ });
1681
+ const registry = new CommandRegistry();
1682
+ registerBuiltins(registry, options.builtins);
1683
+ let seq = 0;
1684
+ const baseMeta = {
1685
+ appVersion: app.version,
1686
+ build: app.build,
1687
+ deviceId: deviceInfo.deviceId
1688
+ };
1689
+ const sampleConsole = options.sampling?.console ?? 1;
1690
+ const sampleNetwork = options.sampling?.network ?? 1;
1691
+ const redactHeaderList = options.redact?.headers ?? DEFAULT_REDACT_HEADERS;
1692
+ const bodyPatterns = options.redact?.bodyPatterns ?? DEFAULT_BODY_PATTERNS;
1693
+ const maxBodyBytes = options.redact?.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
1694
+ const redactNetwork = (p) => {
1695
+ if (p.reqHeaders) p.reqHeaders = redactHeaders(p.reqHeaders, redactHeaderList);
1696
+ if (p.resHeaders) p.resHeaders = redactHeaders(p.resHeaders, redactHeaderList);
1697
+ if (p.reqBody !== void 0) {
1698
+ const r = redactBody(p.reqBody, bodyPatterns, maxBodyBytes);
1699
+ p.reqBody = r.body;
1700
+ p.reqBodyTruncated = r.truncated;
1701
+ }
1702
+ if (p.resBody !== void 0) {
1703
+ const r = redactBody(p.resBody, bodyPatterns, maxBodyBytes);
1704
+ p.resBody = r.body;
1705
+ p.resBodyTruncated = r.truncated;
1706
+ }
1707
+ return p;
1708
+ };
1709
+ const redactConsoleArgs = (p) => {
1710
+ for (const arg of p.args) {
1711
+ if (arg.k === "string" || arg.k === "json") {
1712
+ const r = redactBody(arg.v, bodyPatterns, maxBodyBytes);
1713
+ arg.v = r.body ?? "";
1714
+ if (r.truncated) arg.truncated = true;
1715
+ }
1716
+ }
1717
+ return p;
1718
+ };
1719
+ const deduper = options.dedup === false ? null : new EventDeduper((s) => {
1720
+ const rollupPayload = s.type === "console" ? {
1721
+ method: s.examplePayload?.method ?? "info",
1722
+ args: [
1723
+ {
1724
+ k: "string",
1725
+ v: `[dedup] ${s.label} \u2014 repeated ${s.count} more time${s.count === 1 ? "" : "s"} in ${s.lastTs - s.firstTs}ms`
1726
+ }
1727
+ ]
1728
+ } : s.examplePayload;
1729
+ const ev = {
1730
+ v: SCHEMA_VERSION,
1731
+ sid: session.sid,
1732
+ seq: ++seq,
1733
+ ts: s.lastTs,
1734
+ type: s.type,
1735
+ level: s.level,
1736
+ payload: rollupPayload,
1737
+ meta: {
1738
+ ...baseMeta,
1739
+ dedup: {
1740
+ signature: s.signature,
1741
+ count: s.count,
1742
+ firstTs: s.firstTs,
1743
+ lastTs: s.lastTs
1744
+ }
1745
+ }
1746
+ };
1747
+ buffer.push(ev);
1748
+ }, options.dedup || void 0);
1749
+ const pushOrSuppress = (ev) => {
1750
+ if (!deduper || deduper.shouldEmit(ev)) buffer.push(ev);
1751
+ };
1752
+ const emitConsole = (level, payload) => {
1753
+ const ev = {
1754
+ v: SCHEMA_VERSION,
1755
+ sid: session.sid,
1756
+ seq: ++seq,
1757
+ ts: Date.now(),
1758
+ type: "console",
1759
+ level,
1760
+ payload: redactConsoleArgs(payload),
1761
+ meta: baseMeta
1762
+ };
1763
+ pushOrSuppress(ev);
1764
+ };
1765
+ const emitError = (payload) => {
1766
+ const ev = {
1767
+ v: SCHEMA_VERSION,
1768
+ sid: session.sid,
1769
+ seq: ++seq,
1770
+ ts: Date.now(),
1771
+ type: "error",
1772
+ level: "error",
1773
+ payload,
1774
+ meta: baseMeta
1775
+ };
1776
+ pushOrSuppress(ev);
1777
+ };
1778
+ const emitNetwork = (payload) => {
1779
+ const ev = {
1780
+ v: SCHEMA_VERSION,
1781
+ sid: session.sid,
1782
+ seq: ++seq,
1783
+ ts: Date.now(),
1784
+ type: "network",
1785
+ level: payload.phase === "error" ? "error" : "info",
1786
+ payload: redactNetwork(payload),
1787
+ meta: baseMeta
1788
+ };
1789
+ pushOrSuppress(ev);
1790
+ };
1791
+ const emitCommandResult = (req, kind, payload) => {
1792
+ const ev = {
1793
+ v: SCHEMA_VERSION,
1794
+ sid: session.sid,
1795
+ seq: ++seq,
1796
+ ts: Date.now(),
1797
+ type: kind,
1798
+ level: kind === "command-rejected" ? "warn" : "info",
1799
+ payload,
1800
+ meta: { ...baseMeta, commandId: req.id }
1801
+ };
1802
+ buffer.push(ev);
1803
+ };
1804
+ registry.onOverride((name) => {
1805
+ emitCommandResult(
1806
+ { id: "override-" + name + "-" + Date.now()},
1807
+ "command-rejected",
1808
+ { command: name, reason: "overridden" }
1809
+ );
1810
+ });
1811
+ const uninstallConsole = installConsoleInterceptor({
1812
+ emit: emitConsole,
1813
+ sample: sampleConsole
1814
+ });
1815
+ const uninstallErrors = installErrorInterceptor({ emit: emitError });
1816
+ const internalEndpoints = [session.ingestUrl, session.commandPollUrl];
1817
+ const uninstallNetwork = installNetworkInterceptor({
1818
+ emit: emitNetwork,
1819
+ sample: sampleNetwork,
1820
+ shouldSkip: (url) => internalEndpoints.some((ep) => ep && url.startsWith(ep))
1821
+ });
1822
+ transport.start();
1823
+ transport.startCommandLoop({
1824
+ commandPollUrl: session.commandPollUrl,
1825
+ onCommand: async (req) => {
1826
+ const ctl = new AbortController();
1827
+ const result = await registry.dispatch(req, ctl.signal);
1828
+ emitCommandResult(req, result.kind === "ack" ? "command-ack" : "command-rejected", result.payload);
1829
+ }
1830
+ });
1831
+ return {
1832
+ sid: session.sid,
1833
+ async flush() {
1834
+ await transport.flush();
1835
+ },
1836
+ async stop() {
1837
+ uninstallConsole();
1838
+ uninstallErrors();
1839
+ uninstallNetwork();
1840
+ if (deduper) deduper.stop();
1841
+ await transport.stop();
1842
+ },
1843
+ registerCommand(name, handler) {
1844
+ registry.register(name, handler);
1845
+ },
1846
+ unregisterCommand(name) {
1847
+ return registry.unregister(name);
1848
+ }
1849
+ };
1850
+ }
1851
+
1852
+ // src/auto.ts
1853
+ setDefaultBotimConfig(botimConfig);
1854
+
1855
+ export { BotimConfigError, BotimConsentError, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, SCHEMA_VERSION, clearConsentDecision, enableRemoteDebug, getBOT, promptForConsent, readConsentDecision, setDefaultBotimConfig, writeConsentDecision };
1856
+ //# sourceMappingURL=auto.js.map
1857
+ //# sourceMappingURL=auto.js.map