capdebug 0.1.0 → 0.2.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/plugin.mjs CHANGED
@@ -1,842 +1,772 @@
1
1
  import { registerPlugin, WebPlugin } from '@capacitor/core';
2
2
 
3
- const DEFAULT_SENSITIVE_KEYS = new Set([
4
- 'authorization',
5
- 'cookie',
6
- 'set-cookie',
7
- 'password',
8
- 'passwd',
9
- 'pass',
10
- 'token',
11
- 'access_token',
12
- 'accesstoken',
13
- 'refresh_token',
14
- 'refreshtoken',
15
- 'api_key',
16
- 'apikey',
17
- 'secret',
18
- 'client_secret',
19
- 'clientsecret',
20
- 'bearer',
21
- 'private_key',
22
- 'privatekey',
23
- 'session_id',
24
- 'sessionid',
25
- 'jwt',
26
- 'credit_card',
27
- 'creditcard',
28
- 'cvv',
29
- 'ssn',
3
+ const DEFAULT_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
4
+ "authorization",
5
+ "cookie",
6
+ "set-cookie",
7
+ "password",
8
+ "passwd",
9
+ "pass",
10
+ "token",
11
+ "access_token",
12
+ "accesstoken",
13
+ "refresh_token",
14
+ "refreshtoken",
15
+ "api_key",
16
+ "apikey",
17
+ "secret",
18
+ "client_secret",
19
+ "clientsecret",
20
+ "bearer",
21
+ "private_key",
22
+ "privatekey",
23
+ "session_id",
24
+ "sessionid",
25
+ "jwt",
26
+ "credit_card",
27
+ "creditcard",
28
+ "cvv",
29
+ "ssn"
30
30
  ]);
31
- const MASK_VALUE = '********';
31
+ const MASK_VALUE = "********";
32
32
  class DataSanitizer {
33
- maskKeys;
34
- maxDepth;
35
- maxArrayLength;
36
- constructor(options = {}) {
37
- this.maskKeys = new Set([
38
- ...DEFAULT_SENSITIVE_KEYS,
39
- ...(options.customMaskKeys || []).map(k => k.toLowerCase().trim()),
40
- ]);
41
- this.maxDepth = options.maxDepth ?? 10;
42
- this.maxArrayLength = options.maxArrayLength ?? 100;
43
- }
44
- isSensitiveKey(key) {
45
- const normalized = key.toLowerCase().replace(/[-_]/g, '');
46
- if (this.maskKeys.has(key.toLowerCase()) || this.maskKeys.has(normalized)) {
47
- return true;
48
- }
49
- for (const sensitive of this.maskKeys) {
50
- const cleanSensitive = sensitive.replace(/[-_]/g, '');
51
- if (normalized.includes(cleanSensitive) && cleanSensitive.length >= 4) {
52
- return true;
53
- }
54
- }
55
- return false;
33
+ maskKeys;
34
+ maxDepth;
35
+ maxArrayLength;
36
+ constructor(options = {}) {
37
+ this.maskKeys = /* @__PURE__ */ new Set([
38
+ ...DEFAULT_SENSITIVE_KEYS,
39
+ ...(options.customMaskKeys || []).map((k) => k.toLowerCase().trim())
40
+ ]);
41
+ this.maxDepth = options.maxDepth ?? 10;
42
+ this.maxArrayLength = options.maxArrayLength ?? 100;
43
+ }
44
+ isSensitiveKey(key) {
45
+ const normalized = key.toLowerCase().replace(/[-_]/g, "");
46
+ if (this.maskKeys.has(key.toLowerCase()) || this.maskKeys.has(normalized)) {
47
+ return true;
48
+ }
49
+ for (const sensitive of this.maskKeys) {
50
+ const cleanSensitive = sensitive.replace(/[-_]/g, "");
51
+ if (normalized.includes(cleanSensitive) && cleanSensitive.length >= 4) {
52
+ return true;
53
+ }
54
+ }
55
+ return false;
56
+ }
57
+ sanitize(value) {
58
+ const seen = /* @__PURE__ */ new WeakSet();
59
+ return this.sanitizeRecursive(value, 0, seen);
60
+ }
61
+ sanitizeRecursive(value, depth, seen) {
62
+ if (value === null || value === void 0) {
63
+ return value;
56
64
  }
57
- sanitize(value) {
58
- const seen = new WeakSet();
59
- return this.sanitizeRecursive(value, 0, seen);
65
+ if (depth > this.maxDepth) {
66
+ return "[Max Depth Reached]";
60
67
  }
61
- sanitizeRecursive(value, depth, seen) {
62
- if (value === null || value === undefined) {
63
- return value;
64
- }
65
- if (depth > this.maxDepth) {
66
- return '[Max Depth Reached]';
67
- }
68
- const type = typeof value;
69
- if (type === 'string' || type === 'number' || type === 'boolean') {
70
- return value;
71
- }
72
- if (type === 'bigint') {
73
- return value.toString() + 'n';
74
- }
75
- if (type === 'symbol') {
76
- return value.toString();
77
- }
78
- if (type === 'function') {
79
- return `[Function: ${value.name || 'anonymous'}]`;
80
- }
81
- if (typeof value === 'object') {
82
- // Check circular reference
83
- if (seen.has(value)) {
84
- return '[Circular Reference]';
85
- }
86
- seen.add(value);
87
- // Error handling
88
- if (value instanceof Error) {
89
- return {
90
- name: value.name,
91
- message: value.message,
92
- stack: value.stack,
93
- ...value,
94
- };
95
- }
96
- // Date handling
97
- if (value instanceof Date) {
98
- return value.toISOString();
99
- }
100
- // RegExp handling
101
- if (value instanceof RegExp) {
102
- return value.toString();
103
- }
104
- // DOM Node handling
105
- if (typeof Element !== 'undefined' && value instanceof Element) {
106
- const id = value.id ? `#${value.id}` : '';
107
- const className = value.className ? `.${String(value.className).trim().replace(/\s+/g, '.')}` : '';
108
- return `<${value.tagName.toLowerCase()}${id}${className}>`;
109
- }
110
- // Array handling
111
- if (Array.isArray(value)) {
112
- const arr = value.slice(0, this.maxArrayLength).map(item => this.sanitizeRecursive(item, depth + 1, seen));
113
- if (value.length > this.maxArrayLength) {
114
- arr.push(`[... ${value.length - this.maxArrayLength} more items]`);
115
- }
116
- return arr;
117
- }
118
- // Map handling
119
- if (value instanceof Map) {
120
- const entries = {};
121
- for (const [k, v] of value.entries()) {
122
- const keyStr = String(k);
123
- entries[keyStr] = this.isSensitiveKey(keyStr)
124
- ? MASK_VALUE
125
- : this.sanitizeRecursive(v, depth + 1, seen);
126
- }
127
- return entries;
128
- }
129
- // Set handling
130
- if (value instanceof Set) {
131
- return Array.from(value).map(item => this.sanitizeRecursive(item, depth + 1, seen));
132
- }
133
- // Plain Object handling
134
- const result = {};
135
- const obj = value;
136
- for (const [key, val] of Object.entries(obj)) {
137
- if (this.isSensitiveKey(key)) {
138
- result[key] = MASK_VALUE;
139
- }
140
- else {
141
- result[key] = this.sanitizeRecursive(val, depth + 1, seen);
142
- }
143
- }
144
- return result;
145
- }
146
- return String(value);
68
+ const type = typeof value;
69
+ if (type === "string" || type === "number" || type === "boolean") {
70
+ return value;
147
71
  }
72
+ if (type === "bigint") {
73
+ return value.toString() + "n";
74
+ }
75
+ if (type === "symbol") {
76
+ return value.toString();
77
+ }
78
+ if (type === "function") {
79
+ return `[Function: ${value.name || "anonymous"}]`;
80
+ }
81
+ if (typeof value === "object") {
82
+ if (seen.has(value)) {
83
+ return "[Circular Reference]";
84
+ }
85
+ seen.add(value);
86
+ if (value instanceof Error) {
87
+ return {
88
+ name: value.name,
89
+ message: value.message,
90
+ stack: value.stack,
91
+ ...value
92
+ };
93
+ }
94
+ if (value instanceof Date) {
95
+ return value.toISOString();
96
+ }
97
+ if (value instanceof RegExp) {
98
+ return value.toString();
99
+ }
100
+ if (typeof Element !== "undefined" && value instanceof Element) {
101
+ const id = value.id ? `#${value.id}` : "";
102
+ const className = value.className ? `.${String(value.className).trim().replace(/\s+/g, ".")}` : "";
103
+ return `<${value.tagName.toLowerCase()}${id}${className}>`;
104
+ }
105
+ if (Array.isArray(value)) {
106
+ const arr = value.slice(0, this.maxArrayLength).map(
107
+ (item) => this.sanitizeRecursive(item, depth + 1, seen)
108
+ );
109
+ if (value.length > this.maxArrayLength) {
110
+ arr.push(`[... ${value.length - this.maxArrayLength} more items]`);
111
+ }
112
+ return arr;
113
+ }
114
+ if (value instanceof Map) {
115
+ const entries = {};
116
+ for (const [k, v] of value.entries()) {
117
+ const keyStr = String(k);
118
+ entries[keyStr] = this.isSensitiveKey(keyStr) ? MASK_VALUE : this.sanitizeRecursive(v, depth + 1, seen);
119
+ }
120
+ return entries;
121
+ }
122
+ if (value instanceof Set) {
123
+ return Array.from(value).map(
124
+ (item) => this.sanitizeRecursive(item, depth + 1, seen)
125
+ );
126
+ }
127
+ const result = {};
128
+ const obj = value;
129
+ for (const [key, val] of Object.entries(obj)) {
130
+ if (this.isSensitiveKey(key)) {
131
+ result[key] = MASK_VALUE;
132
+ } else {
133
+ result[key] = this.sanitizeRecursive(val, depth + 1, seen);
134
+ }
135
+ }
136
+ return result;
137
+ }
138
+ return String(value);
139
+ }
148
140
  }
149
141
  const defaultSanitizer = new DataSanitizer();
150
142
  function sanitize(value, options) {
151
- if (!options) {
152
- return defaultSanitizer.sanitize(value);
153
- }
154
- return new DataSanitizer(options).sanitize(value);
143
+ if (!options) {
144
+ return defaultSanitizer.sanitize(value);
145
+ }
146
+ return new DataSanitizer(options).sanitize(value);
155
147
  }
156
148
 
157
149
  class CapDebugStore {
158
- buffer = [];
159
- maxEvents;
160
- paused = false;
161
- listeners = new Set();
162
- rafId = null;
163
- customMaskKeys = [];
164
- filterState = {
165
- tab: 'all',
166
- search: '',
167
- level: 'all',
168
- source: 'all',
169
- };
170
- constructor(maxEvents = 500, customMaskKeys = []) {
171
- this.maxEvents = Math.max(1, maxEvents);
172
- this.customMaskKeys = customMaskKeys;
173
- }
174
- setMaxEvents(max) {
175
- this.maxEvents = Math.max(1, max);
176
- if (this.buffer.length > this.maxEvents) {
177
- this.buffer = this.buffer.slice(this.buffer.length - this.maxEvents);
178
- this.notify();
179
- }
180
- }
181
- setCustomMaskKeys(keys) {
182
- this.customMaskKeys = keys;
183
- }
184
- add(event) {
185
- if (this.paused) {
186
- return null;
187
- }
188
- const sanitizedData = event.data !== undefined
189
- ? sanitize(event.data, { customMaskKeys: this.customMaskKeys })
190
- : undefined;
191
- const fullEvent = {
192
- id: event.id || this.generateId(),
193
- timestamp: event.timestamp || Date.now(),
194
- source: event.source || 'web',
195
- type: event.type,
196
- level: event.level || 'info',
197
- message: event.message,
198
- data: sanitizedData,
199
- duration: event.duration,
200
- platform: event.platform,
201
- };
202
- if (this.buffer.length >= this.maxEvents) {
203
- this.buffer.shift(); // Evict oldest FIFO
204
- }
205
- this.buffer.push(fullEvent);
206
- this.notify();
207
- return fullEvent;
208
- }
209
- clear() {
210
- this.buffer = [];
211
- this.notify();
212
- }
213
- pause() {
214
- this.paused = true;
215
- this.notify();
216
- }
217
- resume() {
218
- this.paused = false;
219
- this.notify();
220
- }
221
- togglePause() {
222
- this.paused = !this.paused;
223
- this.notify();
224
- return this.paused;
225
- }
226
- isPaused() {
227
- return this.paused;
228
- }
229
- setFilter(partial) {
230
- this.filterState = { ...this.filterState, ...partial };
231
- this.notify();
232
- }
233
- getFilter() {
234
- return this.filterState;
235
- }
236
- getAll() {
237
- return [...this.buffer];
238
- }
239
- getCounts() {
240
- const counts = {
241
- all: this.buffer.length,
242
- console: 0,
243
- errors: 0,
244
- events: 0,
245
- native: 0,
246
- device: 0,
247
- network: 0,
248
- bridge: 0,
249
- };
250
- for (const ev of this.buffer) {
251
- if (ev.source === 'console')
252
- counts.console++;
253
- if (ev.level === 'error')
254
- counts.errors++;
255
- if (ev.source === 'web' || ev.source === 'system')
256
- counts.events++;
257
- if (ev.source === 'native')
258
- counts.native++;
259
- if (ev.source === 'network')
260
- counts.network++;
261
- if (ev.source === 'bridge')
262
- counts.bridge++;
263
- }
264
- return counts;
265
- }
266
- getFiltered() {
267
- const { tab, search, level, source } = this.filterState;
268
- const query = search.trim().toLowerCase();
269
- return this.buffer.filter(event => {
270
- // 1. Tab filtering
271
- if (tab === 'console' && event.source !== 'console')
272
- return false;
273
- if (tab === 'errors' && event.level !== 'error')
274
- return false;
275
- if (tab === 'events' && (event.source !== 'web' && event.source !== 'system'))
276
- return false;
277
- if (tab === 'native' && event.source !== 'native')
278
- return false;
279
- if (tab === 'network' && event.source !== 'network')
280
- return false;
281
- if (tab === 'bridge' && event.source !== 'bridge')
282
- return false;
283
- // 2. Level filter
284
- if (level !== 'all' && event.level !== level)
285
- return false;
286
- // 3. Source filter
287
- if (source !== 'all' && event.source !== source)
288
- return false;
289
- // 4. Search query
290
- if (query) {
291
- const typeMatch = event.type.toLowerCase().includes(query);
292
- const msgMatch = event.message ? event.message.toLowerCase().includes(query) : false;
293
- const sourceMatch = event.source.toLowerCase().includes(query);
294
- if (typeMatch || msgMatch || sourceMatch)
295
- return true;
296
- if (event.data) {
297
- try {
298
- const dataStr = typeof event.data === 'string'
299
- ? event.data.toLowerCase()
300
- : JSON.stringify(event.data).toLowerCase();
301
- return dataStr.includes(query);
302
- }
303
- catch {
304
- return false;
305
- }
306
- }
307
- return false;
308
- }
309
- return true;
310
- });
311
- }
312
- exportJson() {
313
- return JSON.stringify({
314
- exportedAt: new Date().toISOString(),
315
- totalEvents: this.buffer.length,
316
- events: this.buffer,
317
- }, null, 2);
318
- }
319
- subscribe(listener) {
320
- this.listeners.add(listener);
321
- listener(this.getFiltered(), this);
322
- return () => {
323
- this.listeners.delete(listener);
324
- };
150
+ buffer = [];
151
+ maxEvents;
152
+ paused = false;
153
+ listeners = /* @__PURE__ */ new Set();
154
+ rafId = null;
155
+ customMaskKeys = [];
156
+ filterState = {
157
+ tab: "all",
158
+ search: "",
159
+ level: "all",
160
+ source: "all"
161
+ };
162
+ constructor(maxEvents = 500, customMaskKeys = []) {
163
+ this.maxEvents = Math.max(1, maxEvents);
164
+ this.customMaskKeys = customMaskKeys;
165
+ }
166
+ setMaxEvents(max) {
167
+ this.maxEvents = Math.max(1, max);
168
+ if (this.buffer.length > this.maxEvents) {
169
+ this.buffer = this.buffer.slice(this.buffer.length - this.maxEvents);
170
+ this.notify();
325
171
  }
326
- notify() {
327
- if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
328
- if (this.rafId !== null) {
329
- cancelAnimationFrame(this.rafId);
330
- }
331
- this.rafId = requestAnimationFrame(() => {
332
- this.rafId = null;
333
- this.dispatchToListeners();
334
- });
335
- }
336
- else {
337
- this.dispatchToListeners();
338
- }
172
+ }
173
+ setCustomMaskKeys(keys) {
174
+ this.customMaskKeys = keys;
175
+ }
176
+ add(event) {
177
+ if (this.paused) {
178
+ return null;
179
+ }
180
+ const sanitizedData = event.data !== void 0 ? sanitize(event.data, { customMaskKeys: this.customMaskKeys }) : void 0;
181
+ const fullEvent = {
182
+ id: event.id || this.generateId(),
183
+ timestamp: event.timestamp || Date.now(),
184
+ source: event.source || "web",
185
+ type: event.type,
186
+ level: event.level || "info",
187
+ message: event.message,
188
+ data: sanitizedData,
189
+ duration: event.duration,
190
+ platform: event.platform
191
+ };
192
+ if (this.buffer.length >= this.maxEvents) {
193
+ this.buffer.shift();
339
194
  }
340
- dispatchToListeners() {
341
- const filtered = this.getFiltered();
342
- for (const listener of this.listeners) {
343
- try {
344
- listener(filtered, this);
345
- }
346
- catch (err) {
347
- // Prevent subscriber errors from halting store
348
- }
195
+ this.buffer.push(fullEvent);
196
+ this.notify();
197
+ return fullEvent;
198
+ }
199
+ clear() {
200
+ this.buffer = [];
201
+ this.notify();
202
+ }
203
+ pause() {
204
+ this.paused = true;
205
+ this.notify();
206
+ }
207
+ resume() {
208
+ this.paused = false;
209
+ this.notify();
210
+ }
211
+ togglePause() {
212
+ this.paused = !this.paused;
213
+ this.notify();
214
+ return this.paused;
215
+ }
216
+ isPaused() {
217
+ return this.paused;
218
+ }
219
+ setFilter(partial) {
220
+ this.filterState = { ...this.filterState, ...partial };
221
+ this.notify();
222
+ }
223
+ getFilter() {
224
+ return this.filterState;
225
+ }
226
+ getAll() {
227
+ return [...this.buffer];
228
+ }
229
+ getCounts() {
230
+ const counts = {
231
+ all: this.buffer.length,
232
+ console: 0,
233
+ errors: 0,
234
+ events: 0,
235
+ native: 0,
236
+ device: 0,
237
+ network: 0,
238
+ bridge: 0
239
+ };
240
+ for (const ev of this.buffer) {
241
+ if (ev.source === "console") counts.console++;
242
+ if (ev.level === "error") counts.errors++;
243
+ if (ev.source === "web" || ev.source === "system") counts.events++;
244
+ if (ev.source === "native") counts.native++;
245
+ if (ev.source === "network") counts.network++;
246
+ if (ev.source === "bridge") counts.bridge++;
247
+ }
248
+ return counts;
249
+ }
250
+ getFiltered() {
251
+ const { tab, search, level, source } = this.filterState;
252
+ const query = search.trim().toLowerCase();
253
+ return this.buffer.filter((event) => {
254
+ if (tab === "console" && event.source !== "console") return false;
255
+ if (tab === "errors" && event.level !== "error") return false;
256
+ if (tab === "events" && (event.source !== "web" && event.source !== "system")) return false;
257
+ if (tab === "native" && event.source !== "native") return false;
258
+ if (tab === "network" && event.source !== "network") return false;
259
+ if (tab === "bridge" && event.source !== "bridge") return false;
260
+ if (level !== "all" && event.level !== level) return false;
261
+ if (source !== "all" && event.source !== source) return false;
262
+ if (query) {
263
+ const typeMatch = event.type.toLowerCase().includes(query);
264
+ const msgMatch = event.message ? event.message.toLowerCase().includes(query) : false;
265
+ const sourceMatch = event.source.toLowerCase().includes(query);
266
+ if (typeMatch || msgMatch || sourceMatch) return true;
267
+ if (event.data) {
268
+ try {
269
+ const dataStr = typeof event.data === "string" ? event.data.toLowerCase() : JSON.stringify(event.data).toLowerCase();
270
+ return dataStr.includes(query);
271
+ } catch {
272
+ return false;
273
+ }
349
274
  }
275
+ return false;
276
+ }
277
+ return true;
278
+ });
279
+ }
280
+ exportJson() {
281
+ return JSON.stringify(
282
+ {
283
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
284
+ totalEvents: this.buffer.length,
285
+ events: this.buffer
286
+ },
287
+ null,
288
+ 2
289
+ );
290
+ }
291
+ subscribe(listener) {
292
+ this.listeners.add(listener);
293
+ listener(this.getFiltered(), this);
294
+ return () => {
295
+ this.listeners.delete(listener);
296
+ };
297
+ }
298
+ notify() {
299
+ if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
300
+ if (this.rafId !== null) {
301
+ cancelAnimationFrame(this.rafId);
302
+ }
303
+ this.rafId = requestAnimationFrame(() => {
304
+ this.rafId = null;
305
+ this.dispatchToListeners();
306
+ });
307
+ } else {
308
+ this.dispatchToListeners();
350
309
  }
351
- generateId() {
352
- return 'ev_' + Math.random().toString(36).substring(2, 9) + '_' + Date.now().toString(36);
310
+ }
311
+ dispatchToListeners() {
312
+ const filtered = this.getFiltered();
313
+ for (const listener of this.listeners) {
314
+ try {
315
+ listener(filtered, this);
316
+ } catch (err) {
317
+ }
353
318
  }
319
+ }
320
+ generateId() {
321
+ return "ev_" + Math.random().toString(36).substring(2, 9) + "_" + Date.now().toString(36);
322
+ }
354
323
  }
355
324
 
356
325
  class ConsoleCollector {
357
- id = 'console';
358
- running = false;
359
- originalMethods = {};
360
- start() {
361
- if (this.running || typeof console === 'undefined')
362
- return;
363
- const methods = [
364
- { method: 'log', level: 'info' },
365
- { method: 'info', level: 'info' },
366
- { method: 'warn', level: 'warn' },
367
- { method: 'error', level: 'error' },
368
- { method: 'debug', level: 'debug' },
369
- ];
370
- for (const { method, level } of methods) {
371
- const original = console[method]?.bind(console);
372
- this.originalMethods[method] = original;
373
- console[method] = (...args) => {
374
- // 1. Call original console method
375
- if (original) {
376
- try {
377
- original(...args);
378
- }
379
- catch {
380
- // ignore
381
- }
382
- }
383
- // 2. Dispatch CapDebug event safely
384
- try {
385
- this.emitConsoleEvent(method, level, args);
386
- }
387
- catch {
388
- // ignore to prevent crashing host application
389
- }
390
- };
391
- }
392
- this.running = true;
393
- }
394
- stop() {
395
- if (!this.running || typeof console === 'undefined')
396
- return;
397
- for (const method of Object.keys(this.originalMethods)) {
398
- const original = this.originalMethods[method];
399
- if (original) {
400
- console[method] = original;
401
- }
402
- }
403
- this.originalMethods = {};
404
- this.running = false;
405
- }
406
- emitConsoleEvent(method, level, args) {
407
- if (typeof window === 'undefined')
408
- return;
409
- let message = '';
410
- let data = undefined;
411
- if (args.length === 1) {
412
- const arg = args[0];
413
- if (typeof arg === 'string' || typeof arg === 'number' || typeof arg === 'boolean') {
414
- message = String(arg);
415
- }
416
- else if (arg instanceof Error) {
417
- message = `${arg.name}: ${arg.message}`;
418
- data = { name: arg.name, message: arg.message, stack: arg.stack };
419
- }
420
- else {
421
- message = `[${typeof arg}]`;
422
- data = arg;
423
- }
326
+ id = "console";
327
+ running = false;
328
+ originalMethods = {};
329
+ start() {
330
+ if (this.running || typeof console === "undefined") return;
331
+ const methods = [
332
+ { method: "log", level: "info" },
333
+ { method: "info", level: "info" },
334
+ { method: "warn", level: "warn" },
335
+ { method: "error", level: "error" },
336
+ { method: "debug", level: "debug" }
337
+ ];
338
+ for (const { method, level } of methods) {
339
+ const original = console[method]?.bind(console);
340
+ this.originalMethods[method] = original;
341
+ console[method] = (...args) => {
342
+ if (original) {
343
+ try {
344
+ original(...args);
345
+ } catch {
346
+ }
424
347
  }
425
- else if (args.length > 1) {
426
- // Multiple arguments
427
- const first = args[0];
428
- if (typeof first === 'string') {
429
- message = first;
430
- data = args.slice(1);
431
- }
432
- else {
433
- message = args.map(a => (typeof a === 'object' && a !== null ? '[Object]' : String(a))).join(' ');
434
- data = args;
435
- }
348
+ try {
349
+ this.emitConsoleEvent(method, level, args);
350
+ } catch {
436
351
  }
437
- window.dispatchEvent(new CustomEvent('capdebug', {
438
- detail: {
439
- source: 'console',
440
- type: `console.${method}`,
441
- level,
442
- message,
443
- data,
444
- timestamp: Date.now(),
445
- },
446
- }));
352
+ };
447
353
  }
354
+ this.running = true;
355
+ }
356
+ stop() {
357
+ if (!this.running || typeof console === "undefined") return;
358
+ for (const method of Object.keys(this.originalMethods)) {
359
+ const original = this.originalMethods[method];
360
+ if (original) {
361
+ console[method] = original;
362
+ }
363
+ }
364
+ this.originalMethods = {};
365
+ this.running = false;
366
+ }
367
+ emitConsoleEvent(method, level, args) {
368
+ if (typeof window === "undefined") return;
369
+ let message = "";
370
+ let data = void 0;
371
+ if (args.length === 1) {
372
+ const arg = args[0];
373
+ if (typeof arg === "string" || typeof arg === "number" || typeof arg === "boolean") {
374
+ message = String(arg);
375
+ } else if (arg instanceof Error) {
376
+ message = `${arg.name}: ${arg.message}`;
377
+ data = { name: arg.name, message: arg.message, stack: arg.stack };
378
+ } else {
379
+ message = `[${typeof arg}]`;
380
+ data = arg;
381
+ }
382
+ } else if (args.length > 1) {
383
+ const first = args[0];
384
+ if (typeof first === "string") {
385
+ message = first;
386
+ data = args.slice(1);
387
+ } else {
388
+ message = args.map((a) => typeof a === "object" && a !== null ? "[Object]" : String(a)).join(" ");
389
+ data = args;
390
+ }
391
+ }
392
+ window.dispatchEvent(
393
+ new CustomEvent("capdebug", {
394
+ detail: {
395
+ source: "console",
396
+ type: `console.${method}`,
397
+ level,
398
+ message,
399
+ data,
400
+ timestamp: Date.now()
401
+ }
402
+ })
403
+ );
404
+ }
448
405
  }
449
406
 
450
407
  class ErrorCollector {
451
- id = 'error';
452
- running = false;
453
- errorHandler = null;
454
- rejectionHandler = null;
455
- start() {
456
- if (this.running || typeof window === 'undefined')
457
- return;
458
- this.errorHandler = (event) => {
459
- try {
460
- const error = event.error;
461
- const message = event.message || (error && error.message) || 'Uncaught Error';
462
- const stack = error && error.stack ? error.stack : undefined;
463
- window.dispatchEvent(new CustomEvent('capdebug', {
464
- detail: {
465
- source: 'web',
466
- type: 'window.error',
467
- level: 'error',
468
- message,
469
- data: {
470
- filename: event.filename,
471
- lineno: event.lineno,
472
- colno: event.colno,
473
- stack,
474
- errorName: error?.name,
475
- },
476
- timestamp: Date.now(),
477
- },
478
- }));
479
- }
480
- catch {
481
- // ignore
482
- }
483
- };
484
- this.rejectionHandler = (event) => {
485
- try {
486
- const reason = event.reason;
487
- let message = 'Unhandled Promise Rejection';
488
- let stack = undefined;
489
- let data = reason;
490
- if (reason instanceof Error) {
491
- message = `Unhandled Rejection: ${reason.message}`;
492
- stack = reason.stack;
493
- data = {
494
- name: reason.name,
495
- message: reason.message,
496
- stack,
497
- };
498
- }
499
- else if (typeof reason === 'string') {
500
- message = `Unhandled Rejection: ${reason}`;
501
- }
502
- window.dispatchEvent(new CustomEvent('capdebug', {
503
- detail: {
504
- source: 'web',
505
- type: 'unhandledrejection',
506
- level: 'error',
507
- message,
508
- data: {
509
- reason: data,
510
- stack,
511
- },
512
- timestamp: Date.now(),
513
- },
514
- }));
408
+ id = "error";
409
+ running = false;
410
+ errorHandler = null;
411
+ rejectionHandler = null;
412
+ start() {
413
+ if (this.running || typeof window === "undefined") return;
414
+ this.errorHandler = (event) => {
415
+ try {
416
+ const error = event.error;
417
+ const message = event.message || error && error.message || "Uncaught Error";
418
+ const stack = error && error.stack ? error.stack : void 0;
419
+ window.dispatchEvent(
420
+ new CustomEvent("capdebug", {
421
+ detail: {
422
+ source: "web",
423
+ type: "window.error",
424
+ level: "error",
425
+ message,
426
+ data: {
427
+ filename: event.filename,
428
+ lineno: event.lineno,
429
+ colno: event.colno,
430
+ stack,
431
+ errorName: error?.name
432
+ },
433
+ timestamp: Date.now()
515
434
  }
516
- catch {
517
- // ignore
435
+ })
436
+ );
437
+ } catch {
438
+ }
439
+ };
440
+ this.rejectionHandler = (event) => {
441
+ try {
442
+ const reason = event.reason;
443
+ let message = "Unhandled Promise Rejection";
444
+ let stack = void 0;
445
+ let data = reason;
446
+ if (reason instanceof Error) {
447
+ message = `Unhandled Rejection: ${reason.message}`;
448
+ stack = reason.stack;
449
+ data = {
450
+ name: reason.name,
451
+ message: reason.message,
452
+ stack
453
+ };
454
+ } else if (typeof reason === "string") {
455
+ message = `Unhandled Rejection: ${reason}`;
456
+ }
457
+ window.dispatchEvent(
458
+ new CustomEvent("capdebug", {
459
+ detail: {
460
+ source: "web",
461
+ type: "unhandledrejection",
462
+ level: "error",
463
+ message,
464
+ data: {
465
+ reason: data,
466
+ stack
467
+ },
468
+ timestamp: Date.now()
518
469
  }
519
- };
520
- window.addEventListener('error', this.errorHandler);
521
- window.addEventListener('unhandledrejection', this.rejectionHandler);
522
- this.running = true;
523
- }
524
- stop() {
525
- if (!this.running || typeof window === 'undefined')
526
- return;
527
- if (this.errorHandler) {
528
- window.removeEventListener('error', this.errorHandler);
529
- this.errorHandler = null;
530
- }
531
- if (this.rejectionHandler) {
532
- window.removeEventListener('unhandledrejection', this.rejectionHandler);
533
- this.rejectionHandler = null;
534
- }
535
- this.running = false;
470
+ })
471
+ );
472
+ } catch {
473
+ }
474
+ };
475
+ window.addEventListener("error", this.errorHandler);
476
+ window.addEventListener("unhandledrejection", this.rejectionHandler);
477
+ this.running = true;
478
+ }
479
+ stop() {
480
+ if (!this.running || typeof window === "undefined") return;
481
+ if (this.errorHandler) {
482
+ window.removeEventListener("error", this.errorHandler);
483
+ this.errorHandler = null;
484
+ }
485
+ if (this.rejectionHandler) {
486
+ window.removeEventListener("unhandledrejection", this.rejectionHandler);
487
+ this.rejectionHandler = null;
536
488
  }
489
+ this.running = false;
490
+ }
537
491
  }
538
492
 
539
493
  class NetworkCollector {
540
- id = 'network';
541
- running = false;
542
- originalFetch = null;
543
- options;
544
- constructor(options = {}) {
545
- this.options = {
546
- body: false,
547
- ...options,
548
- };
549
- }
550
- setOptions(options) {
551
- this.options = { ...this.options, ...options };
552
- }
553
- start() {
554
- if (this.running || typeof window === 'undefined' || !window.fetch)
555
- return;
556
- this.originalFetch = window.fetch;
557
- const self = this;
558
- window.fetch = async function (input, init) {
559
- const startTime = performance.now();
560
- const timestamp = Date.now();
561
- let url = '';
562
- let method = 'GET';
563
- if (typeof input === 'string') {
564
- url = input;
565
- }
566
- else if (input instanceof URL) {
567
- url = input.toString();
568
- }
569
- else if (typeof Request !== 'undefined' && input instanceof Request) {
570
- url = input.url;
571
- method = input.method;
572
- }
573
- if (init && init.method) {
574
- method = init.method.toUpperCase();
575
- }
576
- let requestHeaders = {};
577
- if (init && init.headers) {
578
- if (typeof Headers !== 'undefined' && init.headers instanceof Headers) {
579
- init.headers.forEach((value, key) => {
580
- requestHeaders[key] = value;
581
- });
582
- }
583
- else if (Array.isArray(init.headers)) {
584
- for (const [k, v] of init.headers) {
585
- requestHeaders[k] = v;
586
- }
587
- }
588
- else if (typeof init.headers === 'object') {
589
- requestHeaders = { ...init.headers };
590
- }
591
- }
592
- let requestBody = undefined;
593
- if (self.options.body && init && init.body) {
594
- try {
595
- if (typeof init.body === 'string') {
596
- try {
597
- requestBody = JSON.parse(init.body);
598
- }
599
- catch {
600
- requestBody = init.body;
601
- }
602
- }
603
- else {
604
- requestBody = '[Binary/Stream Body]';
605
- }
606
- }
607
- catch {
608
- requestBody = '[Unreadable Body]';
609
- }
610
- }
494
+ id = "network";
495
+ running = false;
496
+ originalFetch = null;
497
+ options;
498
+ constructor(options = {}) {
499
+ this.options = {
500
+ body: false,
501
+ ...options
502
+ };
503
+ }
504
+ setOptions(options) {
505
+ this.options = { ...this.options, ...options };
506
+ }
507
+ start() {
508
+ if (this.running || typeof window === "undefined" || !window.fetch) return;
509
+ this.originalFetch = window.fetch;
510
+ const self = this;
511
+ window.fetch = async function(input, init) {
512
+ const startTime = performance.now();
513
+ const timestamp = Date.now();
514
+ let url = "";
515
+ let method = "GET";
516
+ if (typeof input === "string") {
517
+ url = input;
518
+ } else if (input instanceof URL) {
519
+ url = input.toString();
520
+ } else if (typeof Request !== "undefined" && input instanceof Request) {
521
+ url = input.url;
522
+ method = input.method;
523
+ }
524
+ if (init && init.method) {
525
+ method = init.method.toUpperCase();
526
+ }
527
+ let requestHeaders = {};
528
+ if (init && init.headers) {
529
+ if (typeof Headers !== "undefined" && init.headers instanceof Headers) {
530
+ init.headers.forEach((value, key) => {
531
+ requestHeaders[key] = value;
532
+ });
533
+ } else if (Array.isArray(init.headers)) {
534
+ for (const [k, v] of init.headers) {
535
+ requestHeaders[k] = v;
536
+ }
537
+ } else if (typeof init.headers === "object") {
538
+ requestHeaders = { ...init.headers };
539
+ }
540
+ }
541
+ let requestBody = void 0;
542
+ if (self.options.body && init && init.body) {
543
+ try {
544
+ if (typeof init.body === "string") {
611
545
  try {
612
- const response = await self.originalFetch.call(this, input, init);
613
- const duration = Math.round(performance.now() - startTime);
614
- let level = 'info';
615
- if (response.status >= 500) {
616
- level = 'error';
617
- }
618
- else if (response.status >= 400) {
619
- level = 'warn';
620
- }
621
- let responseBody = undefined;
622
- if (self.options.body) {
623
- try {
624
- const clone = response.clone();
625
- const contentType = clone.headers.get('content-type') || '';
626
- if (contentType.includes('application/json')) {
627
- responseBody = await clone.json();
628
- }
629
- else if (contentType.includes('text/')) {
630
- responseBody = await clone.text();
631
- }
632
- }
633
- catch {
634
- responseBody = '[Could not clone response body]';
635
- }
636
- }
637
- const responseHeaders = {};
638
- response.headers.forEach((v, k) => {
639
- responseHeaders[k] = v;
640
- });
641
- self.emitNetworkEvent({
642
- type: `fetch ${method}`,
643
- level,
644
- message: `${method} ${url} → ${response.status} ${response.statusText} (${duration}ms)`,
645
- duration,
646
- timestamp,
647
- data: {
648
- method,
649
- url,
650
- status: response.status,
651
- statusText: response.statusText,
652
- ok: response.ok,
653
- duration,
654
- requestHeaders,
655
- responseHeaders,
656
- requestBody,
657
- responseBody,
658
- },
659
- });
660
- return response;
546
+ requestBody = JSON.parse(init.body);
547
+ } catch {
548
+ requestBody = init.body;
661
549
  }
662
- catch (err) {
663
- const duration = Math.round(performance.now() - startTime);
664
- self.emitNetworkEvent({
665
- type: `fetch ${method}`,
666
- level: 'error',
667
- message: `${method} ${url} → Failed: ${err?.message || 'Network Error'} (${duration}ms)`,
668
- duration,
669
- timestamp,
670
- data: {
671
- method,
672
- url,
673
- error: err?.message || String(err),
674
- duration,
675
- requestHeaders,
676
- requestBody,
677
- },
678
- });
679
- throw err;
550
+ } else {
551
+ requestBody = "[Binary/Stream Body]";
552
+ }
553
+ } catch {
554
+ requestBody = "[Unreadable Body]";
555
+ }
556
+ }
557
+ try {
558
+ const response = await self.originalFetch.call(this, input, init);
559
+ const duration = Math.round(performance.now() - startTime);
560
+ let level = "info";
561
+ if (response.status >= 500) {
562
+ level = "error";
563
+ } else if (response.status >= 400) {
564
+ level = "warn";
565
+ }
566
+ let responseBody = void 0;
567
+ if (self.options.body) {
568
+ try {
569
+ const clone = response.clone();
570
+ const contentType = clone.headers.get("content-type") || "";
571
+ if (contentType.includes("application/json")) {
572
+ responseBody = await clone.json();
573
+ } else if (contentType.includes("text/")) {
574
+ responseBody = await clone.text();
680
575
  }
681
- };
682
- this.running = true;
683
- }
684
- stop() {
685
- if (!this.running || typeof window === 'undefined')
686
- return;
687
- if (this.originalFetch) {
688
- window.fetch = this.originalFetch;
689
- this.originalFetch = null;
576
+ } catch {
577
+ responseBody = "[Could not clone response body]";
578
+ }
690
579
  }
691
- this.running = false;
580
+ const responseHeaders = {};
581
+ response.headers.forEach((v, k) => {
582
+ responseHeaders[k] = v;
583
+ });
584
+ self.emitNetworkEvent({
585
+ type: `fetch ${method}`,
586
+ level,
587
+ message: `${method} ${url} \u2192 ${response.status} ${response.statusText} (${duration}ms)`,
588
+ duration,
589
+ timestamp,
590
+ data: {
591
+ method,
592
+ url,
593
+ status: response.status,
594
+ statusText: response.statusText,
595
+ ok: response.ok,
596
+ duration,
597
+ requestHeaders,
598
+ responseHeaders,
599
+ requestBody,
600
+ responseBody
601
+ }
602
+ });
603
+ return response;
604
+ } catch (err) {
605
+ const duration = Math.round(performance.now() - startTime);
606
+ self.emitNetworkEvent({
607
+ type: `fetch ${method}`,
608
+ level: "error",
609
+ message: `${method} ${url} \u2192 Failed: ${err?.message || "Network Error"} (${duration}ms)`,
610
+ duration,
611
+ timestamp,
612
+ data: {
613
+ method,
614
+ url,
615
+ error: err?.message || String(err),
616
+ duration,
617
+ requestHeaders,
618
+ requestBody
619
+ }
620
+ });
621
+ throw err;
622
+ }
623
+ };
624
+ this.running = true;
625
+ }
626
+ stop() {
627
+ if (!this.running || typeof window === "undefined") return;
628
+ if (this.originalFetch) {
629
+ window.fetch = this.originalFetch;
630
+ this.originalFetch = null;
692
631
  }
693
- emitNetworkEvent(detail) {
694
- if (typeof window === 'undefined')
695
- return;
696
- try {
697
- window.dispatchEvent(new CustomEvent('capdebug', {
698
- detail: {
699
- source: 'network',
700
- ...detail,
701
- },
702
- }));
703
- }
704
- catch {
705
- // ignore
706
- }
632
+ this.running = false;
633
+ }
634
+ emitNetworkEvent(detail) {
635
+ if (typeof window === "undefined") return;
636
+ try {
637
+ window.dispatchEvent(
638
+ new CustomEvent("capdebug", {
639
+ detail: {
640
+ source: "network",
641
+ ...detail
642
+ }
643
+ })
644
+ );
645
+ } catch {
707
646
  }
647
+ }
708
648
  }
709
649
 
710
650
  class CustomEventCollector {
711
- id = 'custom-event';
712
- store;
713
- running = false;
714
- handler = null;
715
- constructor(store) {
716
- this.store = store;
717
- }
718
- start() {
719
- if (this.running || typeof window === 'undefined')
720
- return;
721
- this.handler = (event) => {
722
- const customEvent = event;
723
- if (customEvent && customEvent.detail) {
724
- const detail = customEvent.detail;
725
- this.store.add({
726
- id: detail.id,
727
- timestamp: detail.timestamp || Date.now(),
728
- source: detail.source || 'web',
729
- type: detail.type || 'custom-event',
730
- level: detail.level || 'info',
731
- message: detail.message,
732
- data: detail.data,
733
- duration: detail.duration,
734
- platform: detail.platform,
735
- });
736
- }
737
- };
738
- window.addEventListener('capdebug', this.handler);
739
- this.running = true;
740
- }
741
- stop() {
742
- if (!this.running || typeof window === 'undefined')
743
- return;
744
- if (this.handler) {
745
- window.removeEventListener('capdebug', this.handler);
746
- this.handler = null;
747
- }
748
- this.running = false;
651
+ id = "custom-event";
652
+ store;
653
+ running = false;
654
+ handler = null;
655
+ constructor(store) {
656
+ this.store = store;
657
+ }
658
+ start() {
659
+ if (this.running || typeof window === "undefined") return;
660
+ this.handler = (event) => {
661
+ const customEvent = event;
662
+ if (customEvent && customEvent.detail) {
663
+ const detail = customEvent.detail;
664
+ this.store.add({
665
+ id: detail.id,
666
+ timestamp: detail.timestamp || Date.now(),
667
+ source: detail.source || "web",
668
+ type: detail.type || "custom-event",
669
+ level: detail.level || "info",
670
+ message: detail.message,
671
+ data: detail.data,
672
+ duration: detail.duration,
673
+ platform: detail.platform
674
+ });
675
+ }
676
+ };
677
+ window.addEventListener("capdebug", this.handler);
678
+ this.running = true;
679
+ }
680
+ stop() {
681
+ if (!this.running || typeof window === "undefined") return;
682
+ if (this.handler) {
683
+ window.removeEventListener("capdebug", this.handler);
684
+ this.handler = null;
749
685
  }
686
+ this.running = false;
687
+ }
750
688
  }
751
689
 
752
690
  class BrowserEventCollector {
753
- id = 'browser-event';
754
- running = false;
755
- observedEvents = new Map();
756
- start() {
757
- this.running = true;
758
- }
759
- observe(eventName) {
760
- if (!this.running || typeof window === 'undefined' || this.observedEvents.has(eventName))
761
- return;
762
- const listener = (event) => {
763
- let data = {
764
- type: event.type,
765
- timeStamp: event.timeStamp,
766
- };
767
- if (event.type === 'visibilitychange' && typeof document !== 'undefined') {
768
- data.visibilityState = document.visibilityState;
769
- data.hidden = document.hidden;
770
- }
771
- else if (event.type === 'online' || event.type === 'offline') {
772
- data.onLine = typeof navigator !== 'undefined' ? navigator.onLine : undefined;
773
- }
774
- window.dispatchEvent(new CustomEvent('capdebug', {
775
- detail: {
776
- source: 'system',
777
- type: `event.${eventName}`,
778
- level: 'info',
779
- message: `Browser event fired: ${eventName}`,
780
- data,
781
- timestamp: Date.now(),
782
- },
783
- }));
784
- };
785
- const target = eventName === 'visibilitychange' && typeof document !== 'undefined'
786
- ? document
787
- : window;
788
- target.addEventListener(eventName, listener);
789
- this.observedEvents.set(eventName, listener);
790
- }
791
- unobserve(eventName) {
792
- const listener = this.observedEvents.get(eventName);
793
- if (!listener)
794
- return;
795
- const target = eventName === 'visibilitychange' && typeof document !== 'undefined'
796
- ? document
797
- : window;
798
- target.removeEventListener(eventName, listener);
799
- this.observedEvents.delete(eventName);
800
- }
801
- stop() {
802
- for (const [eventName, listener] of this.observedEvents.entries()) {
803
- const target = eventName === 'visibilitychange' && typeof document !== 'undefined'
804
- ? document
805
- : window;
806
- target.removeEventListener(eventName, listener);
807
- }
808
- this.observedEvents.clear();
809
- this.running = false;
691
+ id = "browser-event";
692
+ running = false;
693
+ observedEvents = /* @__PURE__ */ new Map();
694
+ start() {
695
+ this.running = true;
696
+ }
697
+ observe(eventName) {
698
+ if (!this.running || typeof window === "undefined" || this.observedEvents.has(eventName)) return;
699
+ const listener = (event) => {
700
+ let data = {
701
+ type: event.type,
702
+ timeStamp: event.timeStamp
703
+ };
704
+ if (event.type === "visibilitychange" && typeof document !== "undefined") {
705
+ data.visibilityState = document.visibilityState;
706
+ data.hidden = document.hidden;
707
+ } else if (event.type === "online" || event.type === "offline") {
708
+ data.onLine = typeof navigator !== "undefined" ? navigator.onLine : void 0;
709
+ }
710
+ window.dispatchEvent(
711
+ new CustomEvent("capdebug", {
712
+ detail: {
713
+ source: "system",
714
+ type: `event.${eventName}`,
715
+ level: "info",
716
+ message: `Browser event fired: ${eventName}`,
717
+ data,
718
+ timestamp: Date.now()
719
+ }
720
+ })
721
+ );
722
+ };
723
+ const target = eventName === "visibilitychange" && typeof document !== "undefined" ? document : window;
724
+ target.addEventListener(eventName, listener);
725
+ this.observedEvents.set(eventName, listener);
726
+ }
727
+ unobserve(eventName) {
728
+ const listener = this.observedEvents.get(eventName);
729
+ if (!listener) return;
730
+ const target = eventName === "visibilitychange" && typeof document !== "undefined" ? document : window;
731
+ target.removeEventListener(eventName, listener);
732
+ this.observedEvents.delete(eventName);
733
+ }
734
+ stop() {
735
+ for (const [eventName, listener] of this.observedEvents.entries()) {
736
+ const target = eventName === "visibilitychange" && typeof document !== "undefined" ? document : window;
737
+ target.removeEventListener(eventName, listener);
810
738
  }
739
+ this.observedEvents.clear();
740
+ this.running = false;
741
+ }
811
742
  }
812
743
 
813
744
  class BridgeCollector {
814
- id = 'bridge';
815
- running = false;
816
- start() {
817
- if (this.running)
818
- return;
819
- // Reserved for non-intrusive Capacitor bridge telemetry
820
- this.running = true;
821
- }
822
- stop() {
823
- this.running = false;
824
- }
825
- emitBridgeCall(pluginName, methodName, options, duration) {
826
- if (!this.running || typeof window === 'undefined')
827
- return;
828
- window.dispatchEvent(new CustomEvent('capdebug', {
829
- detail: {
830
- source: 'bridge',
831
- type: `${pluginName}.${methodName}`,
832
- level: 'debug',
833
- message: `Bridge call: ${pluginName}.${methodName}`,
834
- data: options,
835
- duration,
836
- timestamp: Date.now(),
837
- },
838
- }));
839
- }
745
+ id = "bridge";
746
+ running = false;
747
+ start() {
748
+ if (this.running) return;
749
+ this.running = true;
750
+ }
751
+ stop() {
752
+ this.running = false;
753
+ }
754
+ emitBridgeCall(pluginName, methodName, options, duration) {
755
+ if (!this.running || typeof window === "undefined") return;
756
+ window.dispatchEvent(
757
+ new CustomEvent("capdebug", {
758
+ detail: {
759
+ source: "bridge",
760
+ type: `${pluginName}.${methodName}`,
761
+ level: "debug",
762
+ message: `Bridge call: ${pluginName}.${methodName}`,
763
+ data: options,
764
+ duration,
765
+ timestamp: Date.now()
766
+ }
767
+ })
768
+ );
769
+ }
840
770
  }
841
771
 
842
772
  const VIEWER_STYLES = `
@@ -1563,1203 +1493,1134 @@ const VIEWER_STYLES = `
1563
1493
  }
1564
1494
  `;
1565
1495
 
1566
- const STORAGE_KEY = 'capdebug_btn_pos';
1496
+ const STORAGE_KEY = "capdebug_btn_pos";
1567
1497
  class FloatingButton {
1568
- element;
1569
- badgeElement;
1570
- options;
1571
- isDragging = false;
1572
- hasMoved = false;
1573
- startX = 0;
1574
- startY = 0;
1575
- initialLeft = 0;
1576
- initialTop = 0;
1577
- constructor(options = {}) {
1578
- this.options = {
1579
- position: 'right',
1580
- persistPosition: true,
1581
- ...options,
1582
- };
1583
- this.element = document.createElement('div');
1584
- this.element.className = 'cd-floating-btn';
1585
- this.element.setAttribute('role', 'button');
1586
- this.element.setAttribute('aria-label', 'Open CapDebug');
1587
- const logo = document.createElement('span');
1588
- logo.className = 'cd-btn-logo';
1589
- logo.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px"><path d="m8 2 1.88 1.88"/><path d="M14.12 3.88 16 2"/><path d="M9 7.13v-1a3.003 3.003 0 1 1 6 0v1"/><path d="M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6"/><path d="M12 20v-9"/><path d="M6.53 9C4.6 8.8 3 7.1 3 5"/><path d="M6 13H2"/><path d="M3 21c0-2.1 1.7-3.9 3.8-4"/><path d="M20.97 5c0 2.1-1.6 3.8-3.5 4"/><path d="M22 13h-4"/><path d="M17.2 17c2.1.1 3.8 1.9 3.8 4"/></svg>CAP`;
1590
- this.badgeElement = document.createElement('span');
1591
- this.badgeElement.className = 'cd-btn-badge';
1592
- this.badgeElement.textContent = '0';
1593
- this.element.appendChild(logo);
1594
- this.element.appendChild(this.badgeElement);
1595
- this.initPosition();
1596
- this.bindEvents();
1597
- }
1598
- getElement() {
1599
- return this.element;
1600
- }
1601
- setErrorCount(count) {
1602
- if (count > 0) {
1603
- this.badgeElement.textContent = count > 99 ? '99+' : String(count);
1604
- this.badgeElement.classList.add('visible');
1605
- }
1606
- else {
1607
- this.badgeElement.classList.remove('visible');
1608
- }
1609
- }
1610
- show() {
1611
- this.element.style.display = 'flex';
1612
- }
1613
- hide() {
1614
- this.element.style.display = 'none';
1498
+ element;
1499
+ badgeElement;
1500
+ options;
1501
+ isDragging = false;
1502
+ hasMoved = false;
1503
+ startX = 0;
1504
+ startY = 0;
1505
+ initialLeft = 0;
1506
+ initialTop = 0;
1507
+ constructor(options = {}) {
1508
+ this.options = {
1509
+ position: "right",
1510
+ persistPosition: true,
1511
+ ...options
1512
+ };
1513
+ this.element = document.createElement("div");
1514
+ this.element.className = "cd-floating-btn";
1515
+ this.element.setAttribute("role", "button");
1516
+ this.element.setAttribute("aria-label", "Open CapDebug");
1517
+ const logo = document.createElement("span");
1518
+ logo.className = "cd-btn-logo";
1519
+ logo.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px"><path d="m8 2 1.88 1.88"/><path d="M14.12 3.88 16 2"/><path d="M9 7.13v-1a3.003 3.003 0 1 1 6 0v1"/><path d="M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6"/><path d="M12 20v-9"/><path d="M6.53 9C4.6 8.8 3 7.1 3 5"/><path d="M6 13H2"/><path d="M3 21c0-2.1 1.7-3.9 3.8-4"/><path d="M20.97 5c0 2.1-1.6 3.8-3.5 4"/><path d="M22 13h-4"/><path d="M17.2 17c2.1.1 3.8 1.9 3.8 4"/></svg>CAP`;
1520
+ this.badgeElement = document.createElement("span");
1521
+ this.badgeElement.className = "cd-btn-badge";
1522
+ this.badgeElement.textContent = "0";
1523
+ this.element.appendChild(logo);
1524
+ this.element.appendChild(this.badgeElement);
1525
+ this.initPosition();
1526
+ this.bindEvents();
1527
+ }
1528
+ getElement() {
1529
+ return this.element;
1530
+ }
1531
+ setErrorCount(count) {
1532
+ if (count > 0) {
1533
+ this.badgeElement.textContent = count > 99 ? "99+" : String(count);
1534
+ this.badgeElement.classList.add("visible");
1535
+ } else {
1536
+ this.badgeElement.classList.remove("visible");
1615
1537
  }
1616
- initPosition() {
1617
- let savedPos = null;
1618
- if (this.options.persistPosition && typeof localStorage !== 'undefined') {
1619
- try {
1620
- const stored = localStorage.getItem(STORAGE_KEY);
1621
- if (stored) {
1622
- savedPos = JSON.parse(stored);
1623
- }
1624
- }
1625
- catch {
1626
- // ignore
1627
- }
1628
- }
1629
- const windowWidth = typeof window !== 'undefined' ? window.innerWidth : 400;
1630
- const windowHeight = typeof window !== 'undefined' ? window.innerHeight : 800;
1631
- let x = windowWidth - 76;
1632
- let y = Math.max(80, windowHeight * 0.4);
1633
- if (savedPos && typeof savedPos.x === 'number' && typeof savedPos.y === 'number') {
1634
- x = Math.min(Math.max(10, savedPos.x), windowWidth - 76);
1635
- y = Math.min(Math.max(60, savedPos.y), windowHeight - 60);
1636
- }
1637
- else if (typeof this.options.position === 'object') {
1638
- x = this.options.position.x;
1639
- y = this.options.position.y;
1640
- }
1641
- else if (this.options.position === 'left') {
1642
- x = 12;
1643
- }
1644
- this.setPosition(x, y);
1645
- }
1646
- setPosition(x, y) {
1647
- this.element.style.left = `${x}px`;
1648
- this.element.style.top = `${y}px`;
1649
- this.element.style.right = 'auto';
1650
- this.element.style.bottom = 'auto';
1651
- }
1652
- bindEvents() {
1653
- const onPointerDown = (e) => {
1654
- this.isDragging = true;
1655
- this.hasMoved = false;
1656
- this.startX = e.clientX;
1657
- this.startY = e.clientY;
1658
- const rect = this.element.getBoundingClientRect();
1659
- this.initialLeft = rect.left;
1660
- this.initialTop = rect.top;
1661
- this.element.classList.add('dragging');
1662
- this.element.setPointerCapture(e.pointerId);
1663
- };
1664
- const onPointerMove = (e) => {
1665
- if (!this.isDragging)
1666
- return;
1667
- const deltaX = e.clientX - this.startX;
1668
- const deltaY = e.clientY - this.startY;
1669
- if (Math.abs(deltaX) > 4 || Math.abs(deltaY) > 4) {
1670
- this.hasMoved = true;
1671
- }
1672
- let newX = this.initialLeft + deltaX;
1673
- let newY = this.initialTop + deltaY;
1674
- const maxX = window.innerWidth - this.element.offsetWidth - 8;
1675
- const maxY = window.innerHeight - this.element.offsetHeight - 8;
1676
- newX = Math.max(8, Math.min(newX, maxX));
1677
- newY = Math.max(40, Math.min(newY, maxY));
1678
- this.setPosition(newX, newY);
1679
- };
1680
- const onPointerUp = (e) => {
1681
- if (!this.isDragging)
1682
- return;
1683
- this.isDragging = false;
1684
- this.element.classList.remove('dragging');
1685
- try {
1686
- this.element.releasePointerCapture(e.pointerId);
1687
- }
1688
- catch {
1689
- // ignore
1690
- }
1691
- if (!this.hasMoved) {
1692
- // Tap / Click action
1693
- if (this.options.onClick) {
1694
- this.options.onClick();
1695
- }
1696
- }
1697
- else {
1698
- // Snap to nearest edge (left or right)
1699
- this.snapToEdge();
1700
- }
1701
- };
1702
- this.element.addEventListener('pointerdown', onPointerDown);
1703
- this.element.addEventListener('pointermove', onPointerMove);
1704
- this.element.addEventListener('pointerup', onPointerUp);
1705
- this.element.addEventListener('pointercancel', onPointerUp);
1706
- }
1707
- snapToEdge() {
1708
- const rect = this.element.getBoundingClientRect();
1709
- const windowWidth = window.innerWidth;
1710
- const windowHeight = window.innerHeight;
1711
- const snapLeft = 12;
1712
- const snapRight = windowWidth - rect.width - 12;
1713
- const targetX = rect.left + rect.width / 2 < windowWidth / 2 ? snapLeft : snapRight;
1714
- const targetY = Math.max(50, Math.min(rect.top, windowHeight - rect.height - 50));
1715
- this.element.style.transition = 'left 0.2s cubic-bezier(0.16, 1, 0.3, 1), top 0.2s cubic-bezier(0.16, 1, 0.3, 1)';
1716
- this.setPosition(targetX, targetY);
1717
- setTimeout(() => {
1718
- this.element.style.transition = '';
1719
- }, 200);
1720
- if (this.options.persistPosition && typeof localStorage !== 'undefined') {
1721
- try {
1722
- localStorage.setItem(STORAGE_KEY, JSON.stringify({ x: targetX, y: targetY }));
1723
- }
1724
- catch {
1725
- // ignore
1726
- }
1727
- }
1538
+ }
1539
+ show() {
1540
+ this.element.style.display = "flex";
1541
+ }
1542
+ hide() {
1543
+ this.element.style.display = "none";
1544
+ }
1545
+ initPosition() {
1546
+ let savedPos = null;
1547
+ if (this.options.persistPosition && typeof localStorage !== "undefined") {
1548
+ try {
1549
+ const stored = localStorage.getItem(STORAGE_KEY);
1550
+ if (stored) {
1551
+ savedPos = JSON.parse(stored);
1552
+ }
1553
+ } catch {
1554
+ }
1555
+ }
1556
+ const windowWidth = typeof window !== "undefined" ? window.innerWidth : 400;
1557
+ const windowHeight = typeof window !== "undefined" ? window.innerHeight : 800;
1558
+ let x = windowWidth - 76;
1559
+ let y = Math.max(80, windowHeight * 0.4);
1560
+ if (savedPos && typeof savedPos.x === "number" && typeof savedPos.y === "number") {
1561
+ x = Math.min(Math.max(10, savedPos.x), windowWidth - 76);
1562
+ y = Math.min(Math.max(60, savedPos.y), windowHeight - 60);
1563
+ } else if (typeof this.options.position === "object") {
1564
+ x = this.options.position.x;
1565
+ y = this.options.position.y;
1566
+ } else if (this.options.position === "left") {
1567
+ x = 12;
1568
+ }
1569
+ this.setPosition(x, y);
1570
+ }
1571
+ setPosition(x, y) {
1572
+ this.element.style.left = `${x}px`;
1573
+ this.element.style.top = `${y}px`;
1574
+ this.element.style.right = "auto";
1575
+ this.element.style.bottom = "auto";
1576
+ }
1577
+ bindEvents() {
1578
+ const onPointerDown = (e) => {
1579
+ this.isDragging = true;
1580
+ this.hasMoved = false;
1581
+ this.startX = e.clientX;
1582
+ this.startY = e.clientY;
1583
+ const rect = this.element.getBoundingClientRect();
1584
+ this.initialLeft = rect.left;
1585
+ this.initialTop = rect.top;
1586
+ this.element.classList.add("dragging");
1587
+ this.element.setPointerCapture(e.pointerId);
1588
+ };
1589
+ const onPointerMove = (e) => {
1590
+ if (!this.isDragging) return;
1591
+ const deltaX = e.clientX - this.startX;
1592
+ const deltaY = e.clientY - this.startY;
1593
+ if (Math.abs(deltaX) > 4 || Math.abs(deltaY) > 4) {
1594
+ this.hasMoved = true;
1595
+ }
1596
+ let newX = this.initialLeft + deltaX;
1597
+ let newY = this.initialTop + deltaY;
1598
+ const maxX = window.innerWidth - this.element.offsetWidth - 8;
1599
+ const maxY = window.innerHeight - this.element.offsetHeight - 8;
1600
+ newX = Math.max(8, Math.min(newX, maxX));
1601
+ newY = Math.max(40, Math.min(newY, maxY));
1602
+ this.setPosition(newX, newY);
1603
+ };
1604
+ const onPointerUp = (e) => {
1605
+ if (!this.isDragging) return;
1606
+ this.isDragging = false;
1607
+ this.element.classList.remove("dragging");
1608
+ try {
1609
+ this.element.releasePointerCapture(e.pointerId);
1610
+ } catch {
1611
+ }
1612
+ if (!this.hasMoved) {
1613
+ if (this.options.onClick) {
1614
+ this.options.onClick();
1615
+ }
1616
+ } else {
1617
+ this.snapToEdge();
1618
+ }
1619
+ };
1620
+ this.element.addEventListener("pointerdown", onPointerDown);
1621
+ this.element.addEventListener("pointermove", onPointerMove);
1622
+ this.element.addEventListener("pointerup", onPointerUp);
1623
+ this.element.addEventListener("pointercancel", onPointerUp);
1624
+ }
1625
+ snapToEdge() {
1626
+ const rect = this.element.getBoundingClientRect();
1627
+ const windowWidth = window.innerWidth;
1628
+ const windowHeight = window.innerHeight;
1629
+ const snapLeft = 12;
1630
+ const snapRight = windowWidth - rect.width - 12;
1631
+ const targetX = rect.left + rect.width / 2 < windowWidth / 2 ? snapLeft : snapRight;
1632
+ const targetY = Math.max(50, Math.min(rect.top, windowHeight - rect.height - 50));
1633
+ this.element.style.transition = "left 0.2s cubic-bezier(0.16, 1, 0.3, 1), top 0.2s cubic-bezier(0.16, 1, 0.3, 1)";
1634
+ this.setPosition(targetX, targetY);
1635
+ setTimeout(() => {
1636
+ this.element.style.transition = "";
1637
+ }, 200);
1638
+ if (this.options.persistPosition && typeof localStorage !== "undefined") {
1639
+ try {
1640
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({ x: targetX, y: targetY }));
1641
+ } catch {
1642
+ }
1728
1643
  }
1644
+ }
1729
1645
  }
1730
1646
 
1731
1647
  class JsonViewer {
1732
- static render(data, initialExpandedDepth = 2) {
1733
- const container = document.createElement('div');
1734
- container.className = 'cd-json-tree';
1735
- if (data === undefined) {
1736
- const undefinedSpan = document.createElement('span');
1737
- undefinedSpan.className = 'cd-json-null';
1738
- undefinedSpan.textContent = 'undefined';
1739
- container.appendChild(undefinedSpan);
1740
- return container;
1741
- }
1742
- const rootNode = JsonViewer.renderNode('', data, 0, initialExpandedDepth);
1743
- container.appendChild(rootNode);
1744
- return container;
1745
- }
1746
- static renderNode(key, value, depth, maxExpandedDepth) {
1747
- const node = document.createElement('div');
1748
- node.className = 'cd-json-node';
1749
- const row = document.createElement('div');
1750
- row.className = 'cd-json-row';
1751
- const isObject = typeof value === 'object' && value !== null;
1752
- const isArray = Array.isArray(value);
1753
- // Toggle button for objects/arrays
1754
- if (isObject) {
1755
- const toggle = document.createElement('span');
1756
- toggle.className = 'cd-json-toggle';
1757
- toggle.innerHTML = '▼';
1758
- row.appendChild(toggle);
1759
- }
1760
- else {
1761
- const spacer = document.createElement('span');
1762
- spacer.style.width = '14px';
1763
- spacer.style.display = 'inline-block';
1764
- row.appendChild(spacer);
1765
- }
1766
- // Key (if present)
1767
- if (key !== '') {
1768
- const keySpan = document.createElement('span');
1769
- keySpan.className = 'cd-json-key';
1770
- keySpan.textContent = typeof key === 'number' ? `[${key}]` : `"${key}"`;
1771
- row.appendChild(keySpan);
1772
- const colonSpan = document.createElement('span');
1773
- colonSpan.className = 'cd-json-colon';
1774
- colonSpan.textContent = ': ';
1775
- row.appendChild(colonSpan);
1776
- }
1777
- // Value preview or primitive
1778
- if (isObject) {
1779
- const entries = isArray
1780
- ? value.map((v, i) => [i, v])
1781
- : Object.entries(value);
1782
- const bracketOpen = isArray ? '[' : '{';
1783
- const bracketClose = isArray ? ']' : '}';
1784
- const previewSpan = document.createElement('span');
1785
- previewSpan.className = 'cd-json-colon';
1786
- previewSpan.textContent = `${bracketOpen} ${entries.length} ${isArray ? 'items' : 'keys'} ${bracketClose}`;
1787
- row.appendChild(previewSpan);
1788
- // Copy object JSON button
1789
- const copyBtn = document.createElement('button');
1790
- copyBtn.className = 'cd-json-copy-btn';
1791
- copyBtn.textContent = 'copy';
1792
- copyBtn.title = 'Copy branch JSON';
1793
- copyBtn.addEventListener('click', e => {
1794
- e.stopPropagation();
1795
- JsonViewer.copyToClipboard(JSON.stringify(value, null, 2), copyBtn);
1796
- });
1797
- row.appendChild(copyBtn);
1798
- node.appendChild(row);
1799
- // Children container
1800
- const childrenContainer = document.createElement('div');
1801
- childrenContainer.className = 'cd-json-children';
1802
- const isInitiallyExpanded = depth < maxExpandedDepth;
1803
- if (!isInitiallyExpanded) {
1804
- childrenContainer.classList.add('hidden');
1805
- const toggle = row.querySelector('.cd-json-toggle');
1806
- if (toggle)
1807
- toggle.classList.add('collapsed');
1808
- }
1809
- for (const [childKey, childVal] of entries) {
1810
- childrenContainer.appendChild(JsonViewer.renderNode(childKey, childVal, depth + 1, maxExpandedDepth));
1811
- }
1812
- node.appendChild(childrenContainer);
1813
- // Toggle collapse on row or toggle click
1814
- row.addEventListener('click', e => {
1815
- if (e.target.tagName.toLowerCase() === 'button')
1816
- return;
1817
- const toggle = row.querySelector('.cd-json-toggle');
1818
- const isHidden = childrenContainer.classList.toggle('hidden');
1819
- if (toggle) {
1820
- toggle.classList.toggle('collapsed', isHidden);
1821
- }
1822
- });
1823
- }
1824
- else {
1825
- // Primitive value
1826
- const valSpan = document.createElement('span');
1827
- const valType = typeof value;
1828
- if (value === null) {
1829
- valSpan.className = 'cd-json-null';
1830
- valSpan.textContent = 'null';
1831
- }
1832
- else if (valType === 'string') {
1833
- valSpan.className = 'cd-json-string';
1834
- valSpan.textContent = `"${value}"`;
1835
- }
1836
- else if (valType === 'number') {
1837
- valSpan.className = 'cd-json-number';
1838
- valSpan.textContent = String(value);
1839
- }
1840
- else if (valType === 'boolean') {
1841
- valSpan.className = 'cd-json-boolean';
1842
- valSpan.textContent = String(value);
1843
- }
1844
- else {
1845
- valSpan.className = 'cd-json-null';
1846
- valSpan.textContent = String(value);
1847
- }
1848
- row.appendChild(valSpan);
1849
- // Copy primitive value button
1850
- const copyBtn = document.createElement('button');
1851
- copyBtn.className = 'cd-json-copy-btn';
1852
- copyBtn.textContent = 'copy';
1853
- copyBtn.title = 'Copy value';
1854
- copyBtn.addEventListener('click', e => {
1855
- e.stopPropagation();
1856
- JsonViewer.copyToClipboard(String(value), copyBtn);
1857
- });
1858
- row.appendChild(copyBtn);
1859
- node.appendChild(row);
1860
- }
1861
- return node;
1862
- }
1863
- static async copyToClipboard(text, triggerBtn) {
1864
- try {
1865
- if (navigator.clipboard && window.isSecureContext) {
1866
- await navigator.clipboard.writeText(text);
1867
- }
1868
- else {
1869
- const textarea = document.createElement('textarea');
1870
- textarea.value = text;
1871
- textarea.style.position = 'fixed';
1872
- textarea.style.left = '-9999px';
1873
- textarea.style.top = '-9999px';
1874
- document.body.appendChild(textarea);
1875
- textarea.focus();
1876
- textarea.select();
1877
- document.execCommand('copy');
1878
- document.body.removeChild(textarea);
1879
- }
1880
- if (triggerBtn) {
1881
- const originalText = triggerBtn.textContent;
1882
- triggerBtn.textContent = 'copied!';
1883
- triggerBtn.style.color = '#34d399';
1884
- setTimeout(() => {
1885
- triggerBtn.textContent = originalText;
1886
- triggerBtn.style.color = '';
1887
- }, 1500);
1888
- }
1889
- return true;
1890
- }
1891
- catch {
1892
- return false;
1893
- }
1648
+ static render(data, initialExpandedDepth = 2) {
1649
+ const container = document.createElement("div");
1650
+ container.className = "cd-json-tree";
1651
+ if (data === void 0) {
1652
+ const undefinedSpan = document.createElement("span");
1653
+ undefinedSpan.className = "cd-json-null";
1654
+ undefinedSpan.textContent = "undefined";
1655
+ container.appendChild(undefinedSpan);
1656
+ return container;
1657
+ }
1658
+ const rootNode = JsonViewer.renderNode("", data, 0, initialExpandedDepth);
1659
+ container.appendChild(rootNode);
1660
+ return container;
1661
+ }
1662
+ static renderNode(key, value, depth, maxExpandedDepth) {
1663
+ const node = document.createElement("div");
1664
+ node.className = "cd-json-node";
1665
+ const row = document.createElement("div");
1666
+ row.className = "cd-json-row";
1667
+ const isObject = typeof value === "object" && value !== null;
1668
+ const isArray = Array.isArray(value);
1669
+ if (isObject) {
1670
+ const toggle = document.createElement("span");
1671
+ toggle.className = "cd-json-toggle";
1672
+ toggle.innerHTML = "\u25BC";
1673
+ row.appendChild(toggle);
1674
+ } else {
1675
+ const spacer = document.createElement("span");
1676
+ spacer.style.width = "14px";
1677
+ spacer.style.display = "inline-block";
1678
+ row.appendChild(spacer);
1679
+ }
1680
+ if (key !== "") {
1681
+ const keySpan = document.createElement("span");
1682
+ keySpan.className = "cd-json-key";
1683
+ keySpan.textContent = typeof key === "number" ? `[${key}]` : `"${key}"`;
1684
+ row.appendChild(keySpan);
1685
+ const colonSpan = document.createElement("span");
1686
+ colonSpan.className = "cd-json-colon";
1687
+ colonSpan.textContent = ": ";
1688
+ row.appendChild(colonSpan);
1689
+ }
1690
+ if (isObject) {
1691
+ const entries = isArray ? value.map((v, i) => [i, v]) : Object.entries(value);
1692
+ const bracketOpen = isArray ? "[" : "{";
1693
+ const bracketClose = isArray ? "]" : "}";
1694
+ const previewSpan = document.createElement("span");
1695
+ previewSpan.className = "cd-json-colon";
1696
+ previewSpan.textContent = `${bracketOpen} ${entries.length} ${isArray ? "items" : "keys"} ${bracketClose}`;
1697
+ row.appendChild(previewSpan);
1698
+ const copyBtn = document.createElement("button");
1699
+ copyBtn.className = "cd-json-copy-btn";
1700
+ copyBtn.textContent = "copy";
1701
+ copyBtn.title = "Copy branch JSON";
1702
+ copyBtn.addEventListener("click", (e) => {
1703
+ e.stopPropagation();
1704
+ JsonViewer.copyToClipboard(JSON.stringify(value, null, 2), copyBtn);
1705
+ });
1706
+ row.appendChild(copyBtn);
1707
+ node.appendChild(row);
1708
+ const childrenContainer = document.createElement("div");
1709
+ childrenContainer.className = "cd-json-children";
1710
+ const isInitiallyExpanded = depth < maxExpandedDepth;
1711
+ if (!isInitiallyExpanded) {
1712
+ childrenContainer.classList.add("hidden");
1713
+ const toggle = row.querySelector(".cd-json-toggle");
1714
+ if (toggle) toggle.classList.add("collapsed");
1715
+ }
1716
+ for (const [childKey, childVal] of entries) {
1717
+ childrenContainer.appendChild(
1718
+ JsonViewer.renderNode(childKey, childVal, depth + 1, maxExpandedDepth)
1719
+ );
1720
+ }
1721
+ node.appendChild(childrenContainer);
1722
+ row.addEventListener("click", (e) => {
1723
+ if (e.target.tagName.toLowerCase() === "button") return;
1724
+ const toggle = row.querySelector(".cd-json-toggle");
1725
+ const isHidden = childrenContainer.classList.toggle("hidden");
1726
+ if (toggle) {
1727
+ toggle.classList.toggle("collapsed", isHidden);
1728
+ }
1729
+ });
1730
+ } else {
1731
+ const valSpan = document.createElement("span");
1732
+ const valType = typeof value;
1733
+ if (value === null) {
1734
+ valSpan.className = "cd-json-null";
1735
+ valSpan.textContent = "null";
1736
+ } else if (valType === "string") {
1737
+ valSpan.className = "cd-json-string";
1738
+ valSpan.textContent = `"${value}"`;
1739
+ } else if (valType === "number") {
1740
+ valSpan.className = "cd-json-number";
1741
+ valSpan.textContent = String(value);
1742
+ } else if (valType === "boolean") {
1743
+ valSpan.className = "cd-json-boolean";
1744
+ valSpan.textContent = String(value);
1745
+ } else {
1746
+ valSpan.className = "cd-json-null";
1747
+ valSpan.textContent = String(value);
1748
+ }
1749
+ row.appendChild(valSpan);
1750
+ const copyBtn = document.createElement("button");
1751
+ copyBtn.className = "cd-json-copy-btn";
1752
+ copyBtn.textContent = "copy";
1753
+ copyBtn.title = "Copy value";
1754
+ copyBtn.addEventListener("click", (e) => {
1755
+ e.stopPropagation();
1756
+ JsonViewer.copyToClipboard(String(value), copyBtn);
1757
+ });
1758
+ row.appendChild(copyBtn);
1759
+ node.appendChild(row);
1760
+ }
1761
+ return node;
1762
+ }
1763
+ static async copyToClipboard(text, triggerBtn) {
1764
+ try {
1765
+ if (navigator.clipboard && window.isSecureContext) {
1766
+ await navigator.clipboard.writeText(text);
1767
+ } else {
1768
+ const textarea = document.createElement("textarea");
1769
+ textarea.value = text;
1770
+ textarea.style.position = "fixed";
1771
+ textarea.style.left = "-9999px";
1772
+ textarea.style.top = "-9999px";
1773
+ document.body.appendChild(textarea);
1774
+ textarea.focus();
1775
+ textarea.select();
1776
+ document.execCommand("copy");
1777
+ document.body.removeChild(textarea);
1778
+ }
1779
+ if (triggerBtn) {
1780
+ const originalText = triggerBtn.textContent;
1781
+ triggerBtn.textContent = "copied!";
1782
+ triggerBtn.style.color = "#34d399";
1783
+ setTimeout(() => {
1784
+ triggerBtn.textContent = originalText;
1785
+ triggerBtn.style.color = "";
1786
+ }, 1500);
1787
+ }
1788
+ return true;
1789
+ } catch {
1790
+ return false;
1894
1791
  }
1792
+ }
1895
1793
  }
1896
1794
 
1897
- const CapDebugNative = registerPlugin('CapDebug', {
1898
- web: () => Promise.resolve().then(function () { return web; }).then(m => new m.CapDebugWeb()),
1795
+ const CapDebugNative = registerPlugin("CapDebug", {
1796
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.CapDebugWeb())
1899
1797
  });
1900
1798
 
1901
1799
  class DebugPanel {
1902
- element;
1903
- backdrop;
1904
- store;
1905
- options;
1906
- isOpen = false;
1907
- // Header UI
1908
- statusIndicator;
1909
- titleCountSpan;
1910
- pauseBtn;
1911
- // Tab elements
1912
- tabElements = new Map();
1913
- tabBadgeElements = new Map();
1914
- // Search & Filter UI
1915
- searchInput;
1916
- levelPills = new Map();
1917
- // Content
1918
- contentContainer;
1919
- eventListContainer;
1920
- deviceViewContainer;
1921
- // Detail Overlay
1922
- detailOverlay;
1923
- selectedEvent = null;
1924
- // Device tab state
1925
- isDeviceLoading = false;
1926
- deviceData = null;
1927
- constructor(store, options = {}) {
1928
- this.store = store;
1929
- this.options = {
1930
- defaultTab: 'all',
1931
- ...options,
1932
- };
1933
- this.backdrop = document.createElement('div');
1934
- this.backdrop.className = 'cd-backdrop';
1935
- this.element = document.createElement('div');
1936
- this.element.className = 'cd-panel';
1937
- this.buildUI();
1938
- this.bindStore();
1939
- if (this.options.defaultTab) {
1940
- this.store.setFilter({ tab: this.options.defaultTab });
1941
- }
1942
- }
1943
- getElement() {
1944
- return this.element;
1945
- }
1946
- getBackdrop() {
1947
- return this.backdrop;
1800
+ element;
1801
+ backdrop;
1802
+ store;
1803
+ options;
1804
+ isOpen = false;
1805
+ // Header UI
1806
+ statusIndicator;
1807
+ titleCountSpan;
1808
+ pauseBtn;
1809
+ // Tab elements
1810
+ tabElements = /* @__PURE__ */ new Map();
1811
+ tabBadgeElements = /* @__PURE__ */ new Map();
1812
+ // Search & Filter UI
1813
+ searchInput;
1814
+ levelPills = /* @__PURE__ */ new Map();
1815
+ // Content
1816
+ contentContainer;
1817
+ eventListContainer;
1818
+ deviceViewContainer;
1819
+ // Detail Overlay
1820
+ detailOverlay;
1821
+ selectedEvent = null;
1822
+ // Device tab state
1823
+ isDeviceLoading = false;
1824
+ deviceData = null;
1825
+ constructor(store, options = {}) {
1826
+ this.store = store;
1827
+ this.options = {
1828
+ defaultTab: "all",
1829
+ ...options
1830
+ };
1831
+ this.backdrop = document.createElement("div");
1832
+ this.backdrop.className = "cd-backdrop";
1833
+ this.element = document.createElement("div");
1834
+ this.element.className = "cd-panel";
1835
+ this.buildUI();
1836
+ this.bindStore();
1837
+ if (this.options.defaultTab) {
1838
+ this.store.setFilter({ tab: this.options.defaultTab });
1948
1839
  }
1949
- open() {
1950
- this.isOpen = true;
1951
- this.element.classList.add('open');
1952
- this.backdrop.classList.add('open');
1953
- // If device tab is active, trigger lazy load
1954
- if (this.store.getFilter().tab === 'device') {
1955
- this.loadDeviceDiagnostics();
1956
- }
1840
+ }
1841
+ getElement() {
1842
+ return this.element;
1843
+ }
1844
+ getBackdrop() {
1845
+ return this.backdrop;
1846
+ }
1847
+ open() {
1848
+ this.isOpen = true;
1849
+ this.element.classList.add("open");
1850
+ this.backdrop.classList.add("open");
1851
+ if (this.store.getFilter().tab === "device") {
1852
+ this.loadDeviceDiagnostics();
1957
1853
  }
1958
- close() {
1959
- this.isOpen = false;
1960
- this.element.classList.remove('open');
1961
- this.backdrop.classList.remove('open');
1962
- this.closeDetail();
1963
- if (this.options.onClose) {
1964
- this.options.onClose();
1965
- }
1854
+ }
1855
+ close() {
1856
+ this.isOpen = false;
1857
+ this.element.classList.remove("open");
1858
+ this.backdrop.classList.remove("open");
1859
+ this.closeDetail();
1860
+ if (this.options.onClose) {
1861
+ this.options.onClose();
1966
1862
  }
1967
- toggle() {
1968
- if (this.isOpen) {
1969
- this.close();
1970
- return false;
1971
- }
1972
- else {
1973
- this.open();
1974
- return true;
1975
- }
1863
+ }
1864
+ toggle() {
1865
+ if (this.isOpen) {
1866
+ this.close();
1867
+ return false;
1868
+ } else {
1869
+ this.open();
1870
+ return true;
1976
1871
  }
1977
- isPanelOpen() {
1978
- return this.isOpen;
1979
- }
1980
- buildUI() {
1981
- this.backdrop.addEventListener('click', () => this.close());
1982
- // 1. Header
1983
- const header = document.createElement('div');
1984
- header.className = 'cd-header';
1985
- const headerLeft = document.createElement('div');
1986
- headerLeft.className = 'cd-header-left';
1987
- this.statusIndicator = document.createElement('div');
1988
- this.statusIndicator.className = 'cd-status-indicator';
1989
- this.statusIndicator.title = 'CapDebug Active';
1990
- const headerTitle = document.createElement('div');
1991
- headerTitle.className = 'cd-header-title';
1992
- headerTitle.innerHTML = `<span>CapDebug</span>`;
1993
- this.titleCountSpan = document.createElement('span');
1994
- this.titleCountSpan.style.color = 'var(--cd-text-muted)';
1995
- this.titleCountSpan.style.fontSize = '11px';
1996
- this.titleCountSpan.textContent = '(0)';
1997
- headerTitle.appendChild(this.titleCountSpan);
1998
- headerLeft.appendChild(this.statusIndicator);
1999
- headerLeft.appendChild(headerTitle);
2000
- const headerActions = document.createElement('div');
2001
- headerActions.className = 'cd-header-actions';
2002
- // Pause/Resume Button
2003
- this.pauseBtn = document.createElement('button');
2004
- this.pauseBtn.className = 'cd-btn-icon';
2005
- this.pauseBtn.title = 'Pause/Resume capture';
2006
- this.pauseBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>`;
2007
- this.pauseBtn.addEventListener('click', () => {
2008
- const isPaused = this.store.togglePause();
2009
- this.updatePauseState(isPaused);
2010
- });
2011
- // Clear Button
2012
- const clearBtn = document.createElement('button');
2013
- clearBtn.className = 'cd-btn-icon';
2014
- clearBtn.title = 'Clear events';
2015
- clearBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>`;
2016
- clearBtn.addEventListener('click', () => {
2017
- this.store.clear();
2018
- this.closeDetail();
2019
- });
2020
- // Export Button
2021
- const exportBtn = document.createElement('button');
2022
- exportBtn.className = 'cd-btn-icon';
2023
- exportBtn.title = 'Export JSON to clipboard';
2024
- exportBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>`;
2025
- exportBtn.addEventListener('click', async () => {
2026
- const json = this.store.exportJson();
2027
- await JsonViewer.copyToClipboard(json, exportBtn);
2028
- });
2029
- // Close Button
2030
- const closeBtn = document.createElement('button');
2031
- closeBtn.className = 'cd-btn-icon';
2032
- closeBtn.title = 'Close viewer';
2033
- closeBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`;
2034
- closeBtn.addEventListener('click', () => this.close());
2035
- headerActions.appendChild(this.pauseBtn);
2036
- headerActions.appendChild(clearBtn);
2037
- headerActions.appendChild(exportBtn);
2038
- headerActions.appendChild(closeBtn);
2039
- header.appendChild(headerLeft);
2040
- header.appendChild(headerActions);
2041
- this.element.appendChild(header);
2042
- // 2. Tabs Bar
2043
- const tabsBar = document.createElement('div');
2044
- tabsBar.className = 'cd-tabs-bar';
2045
- const tabs = [
2046
- { id: 'all', label: 'All' },
2047
- { id: 'console', label: 'Console' },
2048
- { id: 'errors', label: 'Errors' },
2049
- { id: 'events', label: 'Events' },
2050
- { id: 'native', label: 'Native' },
2051
- { id: 'device', label: 'Device' },
2052
- { id: 'network', label: 'Network' },
2053
- { id: 'bridge', label: 'Bridge' },
2054
- ];
2055
- for (const { id, label } of tabs) {
2056
- const tabBtn = document.createElement('button');
2057
- tabBtn.className = 'cd-tab';
2058
- if (id === this.options.defaultTab) {
2059
- tabBtn.classList.add('active');
2060
- }
2061
- const labelSpan = document.createElement('span');
2062
- labelSpan.textContent = label;
2063
- const badgeSpan = document.createElement('span');
2064
- badgeSpan.className = 'cd-tab-badge';
2065
- badgeSpan.textContent = '0';
2066
- tabBtn.appendChild(labelSpan);
2067
- tabBtn.appendChild(badgeSpan);
2068
- tabBtn.addEventListener('click', () => {
2069
- this.selectTab(id);
2070
- });
2071
- this.tabElements.set(id, tabBtn);
2072
- this.tabBadgeElements.set(id, badgeSpan);
2073
- tabsBar.appendChild(tabBtn);
2074
- }
2075
- this.element.appendChild(tabsBar);
2076
- // 3. Toolbar (Search & Level Filters)
2077
- const toolbar = document.createElement('div');
2078
- toolbar.className = 'cd-toolbar';
2079
- this.searchInput = document.createElement('input');
2080
- this.searchInput.className = 'cd-search-input';
2081
- this.searchInput.type = 'text';
2082
- this.searchInput.placeholder = 'Filter logs, types, payloads...';
2083
- this.searchInput.addEventListener('input', () => {
2084
- this.store.setFilter({ search: this.searchInput.value });
2085
- });
2086
- const levelFilters = document.createElement('div');
2087
- levelFilters.className = 'cd-level-filters';
2088
- const levels = ['all', 'error', 'warn', 'info', 'debug'];
2089
- for (const lvl of levels) {
2090
- const pill = document.createElement('button');
2091
- pill.className = 'cd-level-pill';
2092
- pill.setAttribute('data-level', lvl);
2093
- pill.textContent = lvl === 'all' ? 'All' : lvl.toUpperCase();
2094
- if (lvl === 'all')
2095
- pill.classList.add('active');
2096
- pill.addEventListener('click', () => {
2097
- this.selectLevel(lvl);
2098
- });
2099
- this.levelPills.set(lvl, pill);
2100
- levelFilters.appendChild(pill);
2101
- }
2102
- toolbar.appendChild(this.searchInput);
2103
- toolbar.appendChild(levelFilters);
2104
- this.element.appendChild(toolbar);
2105
- // 4. Content Area
2106
- this.contentContainer = document.createElement('div');
2107
- this.contentContainer.className = 'cd-content';
2108
- this.eventListContainer = document.createElement('div');
2109
- this.eventListContainer.className = 'cd-event-list';
2110
- this.deviceViewContainer = document.createElement('div');
2111
- this.deviceViewContainer.className = 'cd-device-view';
2112
- this.deviceViewContainer.style.display = 'none';
2113
- this.contentContainer.appendChild(this.eventListContainer);
2114
- this.contentContainer.appendChild(this.deviceViewContainer);
2115
- this.element.appendChild(this.contentContainer);
2116
- // 5. Detail Overlay
2117
- this.buildDetailOverlay();
2118
- }
2119
- selectTab(tab) {
2120
- for (const [id, el] of this.tabElements.entries()) {
2121
- el.classList.toggle('active', id === tab);
2122
- }
2123
- this.store.setFilter({ tab });
2124
- this.closeDetail();
2125
- if (tab === 'device') {
2126
- this.eventListContainer.style.display = 'none';
2127
- this.deviceViewContainer.style.display = 'flex';
2128
- this.loadDeviceDiagnostics();
2129
- }
2130
- else {
2131
- this.eventListContainer.style.display = 'flex';
2132
- this.deviceViewContainer.style.display = 'none';
2133
- }
1872
+ }
1873
+ isPanelOpen() {
1874
+ return this.isOpen;
1875
+ }
1876
+ buildUI() {
1877
+ this.backdrop.addEventListener("click", () => this.close());
1878
+ const header = document.createElement("div");
1879
+ header.className = "cd-header";
1880
+ const headerLeft = document.createElement("div");
1881
+ headerLeft.className = "cd-header-left";
1882
+ this.statusIndicator = document.createElement("div");
1883
+ this.statusIndicator.className = "cd-status-indicator";
1884
+ this.statusIndicator.title = "CapDebug Active";
1885
+ const headerTitle = document.createElement("div");
1886
+ headerTitle.className = "cd-header-title";
1887
+ headerTitle.innerHTML = `<span>CapDebug</span>`;
1888
+ this.titleCountSpan = document.createElement("span");
1889
+ this.titleCountSpan.style.color = "var(--cd-text-muted)";
1890
+ this.titleCountSpan.style.fontSize = "11px";
1891
+ this.titleCountSpan.textContent = "(0)";
1892
+ headerTitle.appendChild(this.titleCountSpan);
1893
+ headerLeft.appendChild(this.statusIndicator);
1894
+ headerLeft.appendChild(headerTitle);
1895
+ const headerActions = document.createElement("div");
1896
+ headerActions.className = "cd-header-actions";
1897
+ this.pauseBtn = document.createElement("button");
1898
+ this.pauseBtn.className = "cd-btn-icon";
1899
+ this.pauseBtn.title = "Pause/Resume capture";
1900
+ this.pauseBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>`;
1901
+ this.pauseBtn.addEventListener("click", () => {
1902
+ const isPaused = this.store.togglePause();
1903
+ this.updatePauseState(isPaused);
1904
+ });
1905
+ const clearBtn = document.createElement("button");
1906
+ clearBtn.className = "cd-btn-icon";
1907
+ clearBtn.title = "Clear events";
1908
+ clearBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>`;
1909
+ clearBtn.addEventListener("click", () => {
1910
+ this.store.clear();
1911
+ this.closeDetail();
1912
+ });
1913
+ const exportBtn = document.createElement("button");
1914
+ exportBtn.className = "cd-btn-icon";
1915
+ exportBtn.title = "Export JSON to clipboard";
1916
+ exportBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>`;
1917
+ exportBtn.addEventListener("click", async () => {
1918
+ const json = this.store.exportJson();
1919
+ await JsonViewer.copyToClipboard(json, exportBtn);
1920
+ });
1921
+ const closeBtn = document.createElement("button");
1922
+ closeBtn.className = "cd-btn-icon";
1923
+ closeBtn.title = "Close viewer";
1924
+ closeBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`;
1925
+ closeBtn.addEventListener("click", () => this.close());
1926
+ headerActions.appendChild(this.pauseBtn);
1927
+ headerActions.appendChild(clearBtn);
1928
+ headerActions.appendChild(exportBtn);
1929
+ headerActions.appendChild(closeBtn);
1930
+ header.appendChild(headerLeft);
1931
+ header.appendChild(headerActions);
1932
+ this.element.appendChild(header);
1933
+ const tabsBar = document.createElement("div");
1934
+ tabsBar.className = "cd-tabs-bar";
1935
+ const tabs = [
1936
+ { id: "all", label: "All" },
1937
+ { id: "console", label: "Console" },
1938
+ { id: "errors", label: "Errors" },
1939
+ { id: "events", label: "Events" },
1940
+ { id: "native", label: "Native" },
1941
+ { id: "device", label: "Device" },
1942
+ { id: "network", label: "Network" },
1943
+ { id: "bridge", label: "Bridge" }
1944
+ ];
1945
+ for (const { id, label } of tabs) {
1946
+ const tabBtn = document.createElement("button");
1947
+ tabBtn.className = "cd-tab";
1948
+ if (id === this.options.defaultTab) {
1949
+ tabBtn.classList.add("active");
1950
+ }
1951
+ const labelSpan = document.createElement("span");
1952
+ labelSpan.textContent = label;
1953
+ const badgeSpan = document.createElement("span");
1954
+ badgeSpan.className = "cd-tab-badge";
1955
+ badgeSpan.textContent = "0";
1956
+ tabBtn.appendChild(labelSpan);
1957
+ tabBtn.appendChild(badgeSpan);
1958
+ tabBtn.addEventListener("click", () => {
1959
+ this.selectTab(id);
1960
+ });
1961
+ this.tabElements.set(id, tabBtn);
1962
+ this.tabBadgeElements.set(id, badgeSpan);
1963
+ tabsBar.appendChild(tabBtn);
1964
+ }
1965
+ this.element.appendChild(tabsBar);
1966
+ const toolbar = document.createElement("div");
1967
+ toolbar.className = "cd-toolbar";
1968
+ this.searchInput = document.createElement("input");
1969
+ this.searchInput.className = "cd-search-input";
1970
+ this.searchInput.type = "text";
1971
+ this.searchInput.placeholder = "Filter logs, types, payloads...";
1972
+ this.searchInput.addEventListener("input", () => {
1973
+ this.store.setFilter({ search: this.searchInput.value });
1974
+ });
1975
+ const levelFilters = document.createElement("div");
1976
+ levelFilters.className = "cd-level-filters";
1977
+ const levels = ["all", "error", "warn", "info", "debug"];
1978
+ for (const lvl of levels) {
1979
+ const pill = document.createElement("button");
1980
+ pill.className = "cd-level-pill";
1981
+ pill.setAttribute("data-level", lvl);
1982
+ pill.textContent = lvl === "all" ? "All" : lvl.toUpperCase();
1983
+ if (lvl === "all") pill.classList.add("active");
1984
+ pill.addEventListener("click", () => {
1985
+ this.selectLevel(lvl);
1986
+ });
1987
+ this.levelPills.set(lvl, pill);
1988
+ levelFilters.appendChild(pill);
1989
+ }
1990
+ toolbar.appendChild(this.searchInput);
1991
+ toolbar.appendChild(levelFilters);
1992
+ this.element.appendChild(toolbar);
1993
+ this.contentContainer = document.createElement("div");
1994
+ this.contentContainer.className = "cd-content";
1995
+ this.eventListContainer = document.createElement("div");
1996
+ this.eventListContainer.className = "cd-event-list";
1997
+ this.deviceViewContainer = document.createElement("div");
1998
+ this.deviceViewContainer.className = "cd-device-view";
1999
+ this.deviceViewContainer.style.display = "none";
2000
+ this.contentContainer.appendChild(this.eventListContainer);
2001
+ this.contentContainer.appendChild(this.deviceViewContainer);
2002
+ this.element.appendChild(this.contentContainer);
2003
+ this.buildDetailOverlay();
2004
+ }
2005
+ selectTab(tab) {
2006
+ for (const [id, el] of this.tabElements.entries()) {
2007
+ el.classList.toggle("active", id === tab);
2008
+ }
2009
+ this.store.setFilter({ tab });
2010
+ this.closeDetail();
2011
+ if (tab === "device") {
2012
+ this.eventListContainer.style.display = "none";
2013
+ this.deviceViewContainer.style.display = "flex";
2014
+ this.loadDeviceDiagnostics();
2015
+ } else {
2016
+ this.eventListContainer.style.display = "flex";
2017
+ this.deviceViewContainer.style.display = "none";
2134
2018
  }
2135
- selectLevel(level) {
2136
- for (const [lvl, pill] of this.levelPills.entries()) {
2137
- pill.classList.toggle('active', lvl === level);
2138
- }
2139
- this.store.setFilter({ level });
2140
- }
2141
- updatePauseState(isPaused) {
2142
- this.statusIndicator.classList.toggle('paused', isPaused);
2143
- this.pauseBtn.classList.toggle('active', isPaused);
2144
- this.pauseBtn.title = isPaused ? 'Resume capture' : 'Pause capture';
2145
- }
2146
- bindStore() {
2147
- this.store.subscribe((events, store) => {
2148
- this.renderEventList(events);
2149
- this.renderCounts(store.getCounts());
2150
- this.updatePauseState(store.isPaused());
2151
- });
2019
+ }
2020
+ selectLevel(level) {
2021
+ for (const [lvl, pill] of this.levelPills.entries()) {
2022
+ pill.classList.toggle("active", lvl === level);
2152
2023
  }
2153
- renderCounts(counts) {
2154
- this.titleCountSpan.textContent = `(${counts.all})`;
2155
- for (const [tab, badge] of this.tabBadgeElements.entries()) {
2156
- if (tab === 'device') {
2157
- badge.style.display = 'none';
2158
- }
2159
- else {
2160
- const count = counts[tab] || 0;
2161
- badge.textContent = count > 999 ? '999+' : String(count);
2162
- }
2163
- }
2024
+ this.store.setFilter({ level });
2025
+ }
2026
+ updatePauseState(isPaused) {
2027
+ this.statusIndicator.classList.toggle("paused", isPaused);
2028
+ this.pauseBtn.classList.toggle("active", isPaused);
2029
+ this.pauseBtn.title = isPaused ? "Resume capture" : "Pause capture";
2030
+ }
2031
+ bindStore() {
2032
+ this.store.subscribe((events, store) => {
2033
+ this.renderEventList(events);
2034
+ this.renderCounts(store.getCounts());
2035
+ this.updatePauseState(store.isPaused());
2036
+ });
2037
+ }
2038
+ renderCounts(counts) {
2039
+ this.titleCountSpan.textContent = `(${counts.all})`;
2040
+ for (const [tab, badge] of this.tabBadgeElements.entries()) {
2041
+ if (tab === "device") {
2042
+ badge.style.display = "none";
2043
+ } else {
2044
+ const count = counts[tab] || 0;
2045
+ badge.textContent = count > 999 ? "999+" : String(count);
2046
+ }
2164
2047
  }
2165
- renderEventList(events) {
2166
- if (this.store.getFilter().tab === 'device') {
2167
- return;
2168
- }
2169
- this.eventListContainer.innerHTML = '';
2170
- if (events.length === 0) {
2171
- const empty = document.createElement('div');
2172
- empty.className = 'cd-empty-state';
2173
- empty.innerHTML = `
2048
+ }
2049
+ renderEventList(events) {
2050
+ if (this.store.getFilter().tab === "device") {
2051
+ return;
2052
+ }
2053
+ this.eventListContainer.innerHTML = "";
2054
+ if (events.length === 0) {
2055
+ const empty = document.createElement("div");
2056
+ empty.className = "cd-empty-state";
2057
+ empty.innerHTML = `
2174
2058
  <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
2175
2059
  <span>No events match current filter</span>
2176
2060
  `;
2177
- this.eventListContainer.appendChild(empty);
2178
- return;
2179
- }
2180
- // Render newest items at the top
2181
- for (let i = events.length - 1; i >= 0; i--) {
2182
- const event = events[i];
2183
- const row = this.createEventRow(event);
2184
- this.eventListContainer.appendChild(row);
2185
- }
2061
+ this.eventListContainer.appendChild(empty);
2062
+ return;
2186
2063
  }
2187
- createEventRow(event) {
2188
- const row = document.createElement('div');
2189
- row.className = 'cd-event-row';
2190
- row.setAttribute('data-level', event.level || 'info');
2191
- const dot = document.createElement('div');
2192
- dot.className = 'cd-event-level-dot';
2193
- const time = document.createElement('div');
2194
- time.className = 'cd-event-time';
2195
- time.textContent = this.formatTime(event.timestamp);
2196
- const source = document.createElement('div');
2197
- source.className = 'cd-event-source';
2198
- source.textContent = event.source;
2199
- const main = document.createElement('div');
2200
- main.className = 'cd-event-main';
2201
- const type = document.createElement('div');
2202
- type.className = 'cd-event-type';
2203
- type.textContent = event.type;
2204
- main.appendChild(type);
2205
- if (event.message) {
2206
- const msg = document.createElement('div');
2207
- msg.className = 'cd-event-msg';
2208
- msg.textContent = event.message;
2209
- main.appendChild(msg);
2210
- }
2211
- row.appendChild(dot);
2212
- row.appendChild(time);
2213
- row.appendChild(source);
2214
- row.appendChild(main);
2215
- if (event.duration !== undefined) {
2216
- const duration = document.createElement('div');
2217
- duration.className = 'cd-event-duration';
2218
- duration.textContent = `${event.duration}ms`;
2219
- row.appendChild(duration);
2220
- }
2221
- row.addEventListener('click', () => {
2222
- this.showDetail(event);
2223
- });
2224
- return row;
2225
- }
2226
- buildDetailOverlay() {
2227
- this.detailOverlay = document.createElement('div');
2228
- this.detailOverlay.className = 'cd-detail-overlay';
2229
- const header = document.createElement('div');
2230
- header.className = 'cd-detail-header';
2231
- const backBtn = document.createElement('button');
2232
- backBtn.className = 'cd-detail-back';
2233
- backBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg> Back`;
2234
- backBtn.addEventListener('click', () => this.closeDetail());
2235
- const copyAllBtn = document.createElement('button');
2236
- copyAllBtn.className = 'cd-copy-btn-sm';
2237
- copyAllBtn.textContent = 'Copy JSON';
2238
- copyAllBtn.addEventListener('click', async () => {
2239
- if (this.selectedEvent) {
2240
- await JsonViewer.copyToClipboard(JSON.stringify(this.selectedEvent, null, 2), copyAllBtn);
2241
- }
2242
- });
2243
- header.appendChild(backBtn);
2244
- header.appendChild(copyAllBtn);
2245
- this.detailOverlay.appendChild(header);
2246
- const body = document.createElement('div');
2247
- body.className = 'cd-detail-body';
2248
- this.detailOverlay.appendChild(body);
2249
- this.contentContainer.appendChild(this.detailOverlay);
2250
- }
2251
- showDetail(event) {
2252
- this.selectedEvent = event;
2253
- const body = this.detailOverlay.querySelector('.cd-detail-body');
2254
- body.innerHTML = '';
2255
- // 1. Meta Grid
2256
- const metaGrid = document.createElement('div');
2257
- metaGrid.className = 'cd-meta-grid';
2258
- const addMeta = (label, value) => {
2259
- if (value === undefined)
2260
- return;
2261
- const item = document.createElement('div');
2262
- item.className = 'cd-meta-item';
2263
- item.innerHTML = `
2064
+ for (let i = events.length - 1; i >= 0; i--) {
2065
+ const event = events[i];
2066
+ const row = this.createEventRow(event);
2067
+ this.eventListContainer.appendChild(row);
2068
+ }
2069
+ }
2070
+ createEventRow(event) {
2071
+ const row = document.createElement("div");
2072
+ row.className = "cd-event-row";
2073
+ row.setAttribute("data-level", event.level || "info");
2074
+ const dot = document.createElement("div");
2075
+ dot.className = "cd-event-level-dot";
2076
+ const time = document.createElement("div");
2077
+ time.className = "cd-event-time";
2078
+ time.textContent = this.formatTime(event.timestamp);
2079
+ const source = document.createElement("div");
2080
+ source.className = "cd-event-source";
2081
+ source.textContent = event.source;
2082
+ const main = document.createElement("div");
2083
+ main.className = "cd-event-main";
2084
+ const type = document.createElement("div");
2085
+ type.className = "cd-event-type";
2086
+ type.textContent = event.type;
2087
+ main.appendChild(type);
2088
+ if (event.message) {
2089
+ const msg = document.createElement("div");
2090
+ msg.className = "cd-event-msg";
2091
+ msg.textContent = event.message;
2092
+ main.appendChild(msg);
2093
+ }
2094
+ row.appendChild(dot);
2095
+ row.appendChild(time);
2096
+ row.appendChild(source);
2097
+ row.appendChild(main);
2098
+ if (event.duration !== void 0) {
2099
+ const duration = document.createElement("div");
2100
+ duration.className = "cd-event-duration";
2101
+ duration.textContent = `${event.duration}ms`;
2102
+ row.appendChild(duration);
2103
+ }
2104
+ row.addEventListener("click", () => {
2105
+ this.showDetail(event);
2106
+ });
2107
+ return row;
2108
+ }
2109
+ buildDetailOverlay() {
2110
+ this.detailOverlay = document.createElement("div");
2111
+ this.detailOverlay.className = "cd-detail-overlay";
2112
+ const header = document.createElement("div");
2113
+ header.className = "cd-detail-header";
2114
+ const backBtn = document.createElement("button");
2115
+ backBtn.className = "cd-detail-back";
2116
+ backBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg> Back`;
2117
+ backBtn.addEventListener("click", () => this.closeDetail());
2118
+ const copyAllBtn = document.createElement("button");
2119
+ copyAllBtn.className = "cd-copy-btn-sm";
2120
+ copyAllBtn.textContent = "Copy JSON";
2121
+ copyAllBtn.addEventListener("click", async () => {
2122
+ if (this.selectedEvent) {
2123
+ await JsonViewer.copyToClipboard(
2124
+ JSON.stringify(this.selectedEvent, null, 2),
2125
+ copyAllBtn
2126
+ );
2127
+ }
2128
+ });
2129
+ header.appendChild(backBtn);
2130
+ header.appendChild(copyAllBtn);
2131
+ this.detailOverlay.appendChild(header);
2132
+ const body = document.createElement("div");
2133
+ body.className = "cd-detail-body";
2134
+ this.detailOverlay.appendChild(body);
2135
+ this.contentContainer.appendChild(this.detailOverlay);
2136
+ }
2137
+ showDetail(event) {
2138
+ this.selectedEvent = event;
2139
+ const body = this.detailOverlay.querySelector(".cd-detail-body");
2140
+ body.innerHTML = "";
2141
+ const metaGrid = document.createElement("div");
2142
+ metaGrid.className = "cd-meta-grid";
2143
+ const addMeta = (label, value) => {
2144
+ if (value === void 0) return;
2145
+ const item = document.createElement("div");
2146
+ item.className = "cd-meta-item";
2147
+ item.innerHTML = `
2264
2148
  <span class="cd-meta-label">${label}</span>
2265
2149
  <span class="cd-meta-val">${value}</span>
2266
2150
  `;
2267
- metaGrid.appendChild(item);
2268
- };
2269
- addMeta('Type', event.type);
2270
- addMeta('Source', event.source);
2271
- addMeta('Level', event.level);
2272
- addMeta('Time', `${this.formatTime(event.timestamp)} (${new Date(event.timestamp).toLocaleTimeString()})`);
2273
- addMeta('ID', event.id);
2274
- if (event.duration !== undefined)
2275
- addMeta('Duration', `${event.duration}ms`);
2276
- if (event.platform)
2277
- addMeta('Platform', event.platform);
2278
- body.appendChild(metaGrid);
2279
- // 2. Message
2280
- if (event.message) {
2281
- const msgSection = document.createElement('div');
2282
- msgSection.innerHTML = `<div class="cd-section-title">Message</div>`;
2283
- const msgBlock = document.createElement('div');
2284
- msgBlock.className = 'cd-msg-block';
2285
- msgBlock.textContent = event.message;
2286
- msgSection.appendChild(msgBlock);
2287
- body.appendChild(msgSection);
2288
- }
2289
- // 3. Data Payload / JSON Viewer
2290
- if (event.data !== undefined) {
2291
- const dataSection = document.createElement('div');
2292
- dataSection.innerHTML = `<div class="cd-section-title">Data Payload</div>`;
2293
- const jsonTree = JsonViewer.render(event.data, 3);
2294
- dataSection.appendChild(jsonTree);
2295
- body.appendChild(dataSection);
2296
- }
2297
- this.detailOverlay.classList.add('open');
2298
- }
2299
- closeDetail() {
2300
- this.selectedEvent = null;
2301
- this.detailOverlay.classList.remove('open');
2302
- }
2303
- async loadDeviceDiagnostics() {
2304
- if (this.isDeviceLoading)
2305
- return;
2306
- this.isDeviceLoading = true;
2307
- this.deviceViewContainer.innerHTML = `
2151
+ metaGrid.appendChild(item);
2152
+ };
2153
+ addMeta("Type", event.type);
2154
+ addMeta("Source", event.source);
2155
+ addMeta("Level", event.level);
2156
+ addMeta("Time", `${this.formatTime(event.timestamp)} (${new Date(event.timestamp).toLocaleTimeString()})`);
2157
+ addMeta("ID", event.id);
2158
+ if (event.duration !== void 0) addMeta("Duration", `${event.duration}ms`);
2159
+ if (event.platform) addMeta("Platform", event.platform);
2160
+ body.appendChild(metaGrid);
2161
+ if (event.message) {
2162
+ const msgSection = document.createElement("div");
2163
+ msgSection.innerHTML = `<div class="cd-section-title">Message</div>`;
2164
+ const msgBlock = document.createElement("div");
2165
+ msgBlock.className = "cd-msg-block";
2166
+ msgBlock.textContent = event.message;
2167
+ msgSection.appendChild(msgBlock);
2168
+ body.appendChild(msgSection);
2169
+ }
2170
+ if (event.data !== void 0) {
2171
+ const dataSection = document.createElement("div");
2172
+ dataSection.innerHTML = `<div class="cd-section-title">Data Payload</div>`;
2173
+ const jsonTree = JsonViewer.render(event.data, 3);
2174
+ dataSection.appendChild(jsonTree);
2175
+ body.appendChild(dataSection);
2176
+ }
2177
+ this.detailOverlay.classList.add("open");
2178
+ }
2179
+ closeDetail() {
2180
+ this.selectedEvent = null;
2181
+ this.detailOverlay.classList.remove("open");
2182
+ }
2183
+ async loadDeviceDiagnostics() {
2184
+ if (this.isDeviceLoading) return;
2185
+ this.isDeviceLoading = true;
2186
+ this.deviceViewContainer.innerHTML = `
2308
2187
  <div class="cd-empty-state">
2309
2188
  <span>Querying native device diagnostics...</span>
2310
2189
  </div>
2311
2190
  `;
2312
- try {
2313
- const [device, memory, webView, app, network] = await Promise.allSettled([
2314
- CapDebugNative.getDeviceInfo(),
2315
- CapDebugNative.getMemoryInfo(),
2316
- CapDebugNative.getWebViewInfo(),
2317
- CapDebugNative.getAppInfo(),
2318
- CapDebugNative.getNetworkInfo(),
2319
- ]);
2320
- this.deviceData = {
2321
- device: device.status === 'fulfilled' ? device.value : { error: String(device.reason) },
2322
- memory: memory.status === 'fulfilled' ? memory.value : { error: String(memory.reason) },
2323
- webView: webView.status === 'fulfilled' ? webView.value : { error: String(webView.reason) },
2324
- app: app.status === 'fulfilled' ? app.value : { error: String(app.reason) },
2325
- network: network.status === 'fulfilled' ? network.value : { error: String(network.reason) },
2326
- };
2327
- // Emit native diagnostics event into the store as well
2328
- this.store.add({
2329
- source: 'native',
2330
- type: 'device.diagnostics',
2331
- level: 'info',
2332
- message: 'Device diagnostics loaded',
2333
- data: this.deviceData,
2334
- });
2335
- this.renderDeviceView();
2336
- }
2337
- catch (err) {
2338
- this.deviceViewContainer.innerHTML = `
2191
+ try {
2192
+ const [device, memory, webView, app, network] = await Promise.allSettled([
2193
+ CapDebugNative.getDeviceInfo(),
2194
+ CapDebugNative.getMemoryInfo(),
2195
+ CapDebugNative.getWebViewInfo(),
2196
+ CapDebugNative.getAppInfo(),
2197
+ CapDebugNative.getNetworkInfo()
2198
+ ]);
2199
+ this.deviceData = {
2200
+ device: device.status === "fulfilled" ? device.value : { error: String(device.reason) },
2201
+ memory: memory.status === "fulfilled" ? memory.value : { error: String(memory.reason) },
2202
+ webView: webView.status === "fulfilled" ? webView.value : { error: String(webView.reason) },
2203
+ app: app.status === "fulfilled" ? app.value : { error: String(app.reason) },
2204
+ network: network.status === "fulfilled" ? network.value : { error: String(network.reason) }
2205
+ };
2206
+ this.store.add({
2207
+ source: "native",
2208
+ type: "device.diagnostics",
2209
+ level: "info",
2210
+ message: "Device diagnostics loaded",
2211
+ data: this.deviceData
2212
+ });
2213
+ this.renderDeviceView();
2214
+ } catch (err) {
2215
+ this.deviceViewContainer.innerHTML = `
2339
2216
  <div class="cd-empty-state">
2340
2217
  <span style="color: var(--cd-color-error)">Failed to load diagnostics: ${err?.message || String(err)}</span>
2341
2218
  </div>
2342
2219
  `;
2343
- }
2344
- finally {
2345
- this.isDeviceLoading = false;
2346
- }
2220
+ } finally {
2221
+ this.isDeviceLoading = false;
2347
2222
  }
2348
- renderDeviceView() {
2349
- if (!this.deviceData)
2350
- return;
2351
- this.deviceViewContainer.innerHTML = '';
2352
- // Refresh action bar
2353
- const topBar = document.createElement('div');
2354
- topBar.style.display = 'flex';
2355
- topBar.style.justifyContent = 'space-between';
2356
- topBar.style.alignItems = 'center';
2357
- const infoLabel = document.createElement('span');
2358
- infoLabel.style.fontSize = '11px';
2359
- infoLabel.style.color = 'var(--cd-text-muted)';
2360
- infoLabel.textContent = `Diagnostic snapshot: ${new Date().toLocaleTimeString()}`;
2361
- const refreshBtn = document.createElement('button');
2362
- refreshBtn.className = 'cd-copy-btn-sm';
2363
- refreshBtn.textContent = '↻ Refresh Live Data';
2364
- refreshBtn.addEventListener('click', () => this.loadDeviceDiagnostics());
2365
- topBar.appendChild(infoLabel);
2366
- topBar.appendChild(refreshBtn);
2367
- this.deviceViewContainer.appendChild(topBar);
2368
- const sections = [
2369
- { title: 'Device Information', data: this.deviceData.device },
2370
- { title: 'Memory & Heap', data: this.deviceData.memory },
2371
- { title: 'Application Info', data: this.deviceData.app },
2372
- { title: 'WebView & User Agent', data: this.deviceData.webView },
2373
- { title: 'Network State', data: this.deviceData.network },
2374
- ];
2375
- for (const { title, data } of sections) {
2376
- const card = document.createElement('div');
2377
- card.className = 'cd-device-card';
2378
- const cardHeader = document.createElement('div');
2379
- cardHeader.className = 'cd-device-card-header';
2380
- cardHeader.textContent = title;
2381
- card.appendChild(cardHeader);
2382
- if (typeof data === 'object' && data !== null) {
2383
- const grid = document.createElement('div');
2384
- grid.className = 'cd-device-grid';
2385
- for (const [k, v] of Object.entries(data)) {
2386
- if (typeof v === 'object' && v !== null)
2387
- continue;
2388
- const item = document.createElement('div');
2389
- item.className = 'cd-meta-item';
2390
- item.innerHTML = `
2223
+ }
2224
+ renderDeviceView() {
2225
+ if (!this.deviceData) return;
2226
+ this.deviceViewContainer.innerHTML = "";
2227
+ const topBar = document.createElement("div");
2228
+ topBar.style.display = "flex";
2229
+ topBar.style.justifyContent = "space-between";
2230
+ topBar.style.alignItems = "center";
2231
+ const infoLabel = document.createElement("span");
2232
+ infoLabel.style.fontSize = "11px";
2233
+ infoLabel.style.color = "var(--cd-text-muted)";
2234
+ infoLabel.textContent = `Diagnostic snapshot: ${(/* @__PURE__ */ new Date()).toLocaleTimeString()}`;
2235
+ const refreshBtn = document.createElement("button");
2236
+ refreshBtn.className = "cd-copy-btn-sm";
2237
+ refreshBtn.textContent = "\u21BB Refresh Live Data";
2238
+ refreshBtn.addEventListener("click", () => this.loadDeviceDiagnostics());
2239
+ topBar.appendChild(infoLabel);
2240
+ topBar.appendChild(refreshBtn);
2241
+ this.deviceViewContainer.appendChild(topBar);
2242
+ const sections = [
2243
+ { title: "Device Information", data: this.deviceData.device },
2244
+ { title: "Memory & Heap", data: this.deviceData.memory },
2245
+ { title: "Application Info", data: this.deviceData.app },
2246
+ { title: "WebView & User Agent", data: this.deviceData.webView },
2247
+ { title: "Network State", data: this.deviceData.network }
2248
+ ];
2249
+ for (const { title, data } of sections) {
2250
+ const card = document.createElement("div");
2251
+ card.className = "cd-device-card";
2252
+ const cardHeader = document.createElement("div");
2253
+ cardHeader.className = "cd-device-card-header";
2254
+ cardHeader.textContent = title;
2255
+ card.appendChild(cardHeader);
2256
+ if (typeof data === "object" && data !== null) {
2257
+ const grid = document.createElement("div");
2258
+ grid.className = "cd-device-grid";
2259
+ for (const [k, v] of Object.entries(data)) {
2260
+ if (typeof v === "object" && v !== null) continue;
2261
+ const item = document.createElement("div");
2262
+ item.className = "cd-meta-item";
2263
+ item.innerHTML = `
2391
2264
  <span class="cd-meta-label">${k}</span>
2392
- <span class="cd-meta-val">${v !== undefined && v !== null ? String(v) : '—'}</span>
2265
+ <span class="cd-meta-val">${v !== void 0 && v !== null ? String(v) : "\u2014"}</span>
2393
2266
  `;
2394
- grid.appendChild(item);
2395
- }
2396
- card.appendChild(grid);
2397
- // Render nested objects if present
2398
- for (const [k, v] of Object.entries(data)) {
2399
- if (typeof v === 'object' && v !== null) {
2400
- const nestedSection = document.createElement('div');
2401
- nestedSection.innerHTML = `<div style="font-size: 10px; color: var(--cd-text-muted); font-weight: 600; margin-top: 6px;">${k.toUpperCase()}</div>`;
2402
- nestedSection.appendChild(JsonViewer.render(v, 2));
2403
- card.appendChild(nestedSection);
2404
- }
2405
- }
2406
- }
2407
- else {
2408
- card.textContent = String(data);
2409
- }
2410
- this.deviceViewContainer.appendChild(card);
2411
- }
2412
- }
2413
- formatTime(timestamp) {
2414
- const d = new Date(timestamp);
2415
- const h = String(d.getHours()).padStart(2, '0');
2416
- const m = String(d.getMinutes()).padStart(2, '0');
2417
- const s = String(d.getSeconds()).padStart(2, '0');
2418
- const ms = String(d.getMilliseconds()).padStart(3, '0');
2419
- return `${h}:${m}:${s}.${ms}`;
2267
+ grid.appendChild(item);
2268
+ }
2269
+ card.appendChild(grid);
2270
+ for (const [k, v] of Object.entries(data)) {
2271
+ if (typeof v === "object" && v !== null) {
2272
+ const nestedSection = document.createElement("div");
2273
+ nestedSection.innerHTML = `<div style="font-size: 10px; color: var(--cd-text-muted); font-weight: 600; margin-top: 6px;">${k.toUpperCase()}</div>`;
2274
+ nestedSection.appendChild(JsonViewer.render(v, 2));
2275
+ card.appendChild(nestedSection);
2276
+ }
2277
+ }
2278
+ } else {
2279
+ card.textContent = String(data);
2280
+ }
2281
+ this.deviceViewContainer.appendChild(card);
2420
2282
  }
2283
+ }
2284
+ formatTime(timestamp) {
2285
+ const d = new Date(timestamp);
2286
+ const h = String(d.getHours()).padStart(2, "0");
2287
+ const m = String(d.getMinutes()).padStart(2, "0");
2288
+ const s = String(d.getSeconds()).padStart(2, "0");
2289
+ const ms = String(d.getMilliseconds()).padStart(3, "0");
2290
+ return `${h}:${m}:${s}.${ms}`;
2291
+ }
2421
2292
  }
2422
2293
 
2423
2294
  class CapDebugViewerElement extends HTMLElement {
2424
- static TAG = 'cap-debug-viewer';
2425
- shadowRootNode;
2426
- floatingButton = null;
2427
- debugPanel = null;
2428
- store = null;
2429
- unsubscribeStore = null;
2430
- constructor() {
2431
- super();
2432
- this.shadowRootNode = this.attachShadow({ mode: 'open' });
2433
- }
2434
- getStore() {
2435
- return this.store;
2436
- }
2437
- init(store, options = {}) {
2438
- this.store = store;
2439
- // Inject isolated styles
2440
- const styleEl = document.createElement('style');
2441
- styleEl.textContent = VIEWER_STYLES;
2442
- this.shadowRootNode.appendChild(styleEl);
2443
- // Create Panel
2444
- this.debugPanel = new DebugPanel(store, {
2445
- defaultTab: options.defaultTab || 'all',
2446
- onClose: () => {
2447
- // Can handle panel close events if needed
2448
- },
2449
- });
2450
- this.shadowRootNode.appendChild(this.debugPanel.getBackdrop());
2451
- this.shadowRootNode.appendChild(this.debugPanel.getElement());
2452
- // Create Floating Button (if enabled)
2453
- if (options.button !== false) {
2454
- this.floatingButton = new FloatingButton({
2455
- position: options.position,
2456
- persistPosition: options.persistPosition !== false,
2457
- onClick: () => {
2458
- this.toggle();
2459
- },
2460
- });
2461
- this.shadowRootNode.appendChild(this.floatingButton.getElement());
2462
- }
2463
- // Subscribe to error counts for the button badge
2464
- this.unsubscribeStore = store.subscribe((_events, s) => {
2465
- const counts = s.getCounts();
2466
- if (this.floatingButton) {
2467
- this.floatingButton.setErrorCount(counts.errors);
2468
- }
2469
- });
2470
- }
2471
- show() {
2472
- if (this.debugPanel) {
2473
- this.debugPanel.open();
2474
- }
2295
+ static TAG = "cap-debug-viewer";
2296
+ shadowRootNode;
2297
+ floatingButton = null;
2298
+ debugPanel = null;
2299
+ store = null;
2300
+ unsubscribeStore = null;
2301
+ constructor() {
2302
+ super();
2303
+ this.shadowRootNode = this.attachShadow({ mode: "open" });
2304
+ }
2305
+ getStore() {
2306
+ return this.store;
2307
+ }
2308
+ init(store, options = {}) {
2309
+ this.store = store;
2310
+ const styleEl = document.createElement("style");
2311
+ styleEl.textContent = VIEWER_STYLES;
2312
+ this.shadowRootNode.appendChild(styleEl);
2313
+ this.debugPanel = new DebugPanel(store, {
2314
+ defaultTab: options.defaultTab || "all",
2315
+ onClose: () => {
2316
+ }
2317
+ });
2318
+ this.shadowRootNode.appendChild(this.debugPanel.getBackdrop());
2319
+ this.shadowRootNode.appendChild(this.debugPanel.getElement());
2320
+ if (options.button !== false) {
2321
+ this.floatingButton = new FloatingButton({
2322
+ position: options.position,
2323
+ persistPosition: options.persistPosition !== false,
2324
+ onClick: () => {
2325
+ this.toggle();
2326
+ }
2327
+ });
2328
+ this.shadowRootNode.appendChild(this.floatingButton.getElement());
2329
+ }
2330
+ this.unsubscribeStore = store.subscribe((_events, s) => {
2331
+ const counts = s.getCounts();
2332
+ if (this.floatingButton) {
2333
+ this.floatingButton.setErrorCount(counts.errors);
2334
+ }
2335
+ });
2336
+ }
2337
+ show() {
2338
+ if (this.debugPanel) {
2339
+ this.debugPanel.open();
2475
2340
  }
2476
- hide() {
2477
- if (this.debugPanel) {
2478
- this.debugPanel.close();
2479
- }
2341
+ }
2342
+ hide() {
2343
+ if (this.debugPanel) {
2344
+ this.debugPanel.close();
2480
2345
  }
2481
- toggle() {
2482
- if (this.debugPanel) {
2483
- return this.debugPanel.toggle();
2484
- }
2485
- return false;
2346
+ }
2347
+ toggle() {
2348
+ if (this.debugPanel) {
2349
+ return this.debugPanel.toggle();
2486
2350
  }
2487
- showButton() {
2488
- if (this.floatingButton) {
2489
- this.floatingButton.show();
2490
- }
2351
+ return false;
2352
+ }
2353
+ showButton() {
2354
+ if (this.floatingButton) {
2355
+ this.floatingButton.show();
2491
2356
  }
2492
- hideButton() {
2493
- if (this.floatingButton) {
2494
- this.floatingButton.hide();
2495
- }
2357
+ }
2358
+ hideButton() {
2359
+ if (this.floatingButton) {
2360
+ this.floatingButton.hide();
2496
2361
  }
2497
- disconnectedCallback() {
2498
- if (this.unsubscribeStore) {
2499
- this.unsubscribeStore();
2500
- this.unsubscribeStore = null;
2501
- }
2362
+ }
2363
+ disconnectedCallback() {
2364
+ if (this.unsubscribeStore) {
2365
+ this.unsubscribeStore();
2366
+ this.unsubscribeStore = null;
2502
2367
  }
2368
+ }
2503
2369
  }
2504
2370
  function registerCustomElement() {
2505
- if (typeof customElements !== 'undefined' && !customElements.get(CapDebugViewerElement.TAG)) {
2506
- customElements.define(CapDebugViewerElement.TAG, CapDebugViewerElement);
2507
- }
2371
+ if (typeof customElements !== "undefined" && !customElements.get(CapDebugViewerElement.TAG)) {
2372
+ customElements.define(CapDebugViewerElement.TAG, CapDebugViewerElement);
2373
+ }
2508
2374
  }
2509
2375
 
2510
2376
  class CapDebugManager {
2511
- store;
2512
- viewerElement = null;
2513
- isMounted = false;
2514
- customEventCollector;
2515
- consoleCollector;
2516
- errorCollector;
2517
- networkCollector;
2518
- browserEventCollector;
2519
- bridgeCollector;
2520
- constructor() {
2521
- this.store = new CapDebugStore();
2522
- this.customEventCollector = new CustomEventCollector(this.store);
2523
- this.consoleCollector = new ConsoleCollector();
2524
- this.errorCollector = new ErrorCollector();
2525
- this.networkCollector = new NetworkCollector();
2526
- this.browserEventCollector = new BrowserEventCollector();
2527
- this.bridgeCollector = new BridgeCollector();
2528
- }
2529
- get native() {
2530
- return CapDebugNative;
2531
- }
2532
- get currentStore() {
2533
- return this.store;
2534
- }
2535
- /**
2536
- * Mounts the CapDebug UI into the DOM and activates collectors.
2537
- */
2538
- mount(options = {}) {
2539
- if (typeof window === 'undefined' || typeof document === 'undefined') {
2540
- return;
2541
- }
2542
- if (this.isMounted) {
2543
- this.unmount();
2544
- }
2545
- // Configure store
2546
- if (options.maxEvents) {
2547
- this.store.setMaxEvents(options.maxEvents);
2548
- }
2549
- if (options.customMaskKeys) {
2550
- this.store.setCustomMaskKeys(options.customMaskKeys);
2551
- }
2552
- // 1. Start core CustomEvent bus listener
2553
- this.customEventCollector.start();
2554
- // 2. Start optional collectors
2555
- if (options.console !== false) {
2556
- this.consoleCollector.start();
2557
- }
2558
- if (options.errors !== false) {
2559
- this.errorCollector.start();
2560
- }
2561
- if (options.network) {
2562
- const netOpts = typeof options.network === 'object' ? options.network : {};
2563
- this.networkCollector.setOptions(netOpts);
2564
- this.networkCollector.start();
2565
- }
2566
- if (options.bridge) {
2567
- this.bridgeCollector.start();
2568
- }
2569
- this.browserEventCollector.start();
2570
- // 3. Register and create Web Component
2571
- registerCustomElement();
2572
- this.viewerElement = document.createElement(CapDebugViewerElement.TAG);
2573
- this.viewerElement.init(this.store, options);
2574
- document.body.appendChild(this.viewerElement);
2575
- this.isMounted = true;
2576
- }
2577
- /**
2578
- * Unmounts CapDebug UI and stops all collectors.
2579
- */
2580
- unmount() {
2581
- if (!this.isMounted)
2582
- return;
2583
- if (this.viewerElement && this.viewerElement.parentNode) {
2584
- this.viewerElement.parentNode.removeChild(this.viewerElement);
2585
- this.viewerElement = null;
2586
- }
2587
- this.customEventCollector.stop();
2588
- this.consoleCollector.stop();
2589
- this.errorCollector.stop();
2590
- this.networkCollector.stop();
2591
- this.browserEventCollector.stop();
2592
- this.bridgeCollector.stop();
2593
- this.isMounted = false;
2594
- }
2595
- /**
2596
- * Opens the debug viewer drawer.
2597
- */
2598
- show() {
2599
- if (this.viewerElement) {
2600
- this.viewerElement.show();
2601
- }
2602
- }
2603
- /**
2604
- * Closes the debug viewer drawer.
2605
- */
2606
- hide() {
2607
- if (this.viewerElement) {
2608
- this.viewerElement.hide();
2609
- }
2610
- }
2611
- /**
2612
- * Toggles the debug viewer drawer open/closed.
2613
- */
2614
- toggle() {
2615
- if (this.viewerElement) {
2616
- return this.viewerElement.toggle();
2617
- }
2618
- return false;
2619
- }
2620
- /**
2621
- * Dispatches a debug event through the CustomEvent bus.
2622
- */
2623
- emit(event) {
2624
- if (typeof window === 'undefined')
2625
- return;
2626
- window.dispatchEvent(new CustomEvent('capdebug', {
2627
- detail: {
2628
- id: event.id,
2629
- timestamp: event.timestamp || Date.now(),
2630
- source: event.source || 'web',
2631
- type: event.type,
2632
- level: event.level || 'info',
2633
- message: event.message,
2634
- data: event.data,
2635
- duration: event.duration,
2636
- platform: event.platform,
2637
- },
2638
- }));
2639
- }
2640
- /**
2641
- * Clears all recorded events from the store.
2642
- */
2643
- clear() {
2644
- this.store.clear();
2645
- }
2646
- /**
2647
- * Pauses capturing new events.
2648
- */
2649
- pause() {
2650
- this.store.pause();
2651
- }
2652
- /**
2653
- * Resumes capturing events.
2654
- */
2655
- resume() {
2656
- this.store.resume();
2657
- }
2658
- captureConsole() {
2659
- this.consoleCollector.start();
2660
- }
2661
- releaseConsole() {
2662
- this.consoleCollector.stop();
2663
- }
2664
- captureErrors() {
2665
- this.errorCollector.start();
2666
- }
2667
- releaseErrors() {
2668
- this.errorCollector.stop();
2669
- }
2670
- captureFetch(options) {
2671
- if (options) {
2672
- this.networkCollector.setOptions(options);
2673
- }
2674
- this.networkCollector.start();
2675
- }
2676
- releaseFetch() {
2677
- this.networkCollector.stop();
2377
+ store;
2378
+ viewerElement = null;
2379
+ isMounted = false;
2380
+ customEventCollector;
2381
+ consoleCollector;
2382
+ errorCollector;
2383
+ networkCollector;
2384
+ browserEventCollector;
2385
+ bridgeCollector;
2386
+ constructor() {
2387
+ this.store = new CapDebugStore();
2388
+ this.customEventCollector = new CustomEventCollector(this.store);
2389
+ this.consoleCollector = new ConsoleCollector();
2390
+ this.errorCollector = new ErrorCollector();
2391
+ this.networkCollector = new NetworkCollector();
2392
+ this.browserEventCollector = new BrowserEventCollector();
2393
+ this.bridgeCollector = new BridgeCollector();
2394
+ }
2395
+ get native() {
2396
+ return CapDebugNative;
2397
+ }
2398
+ get currentStore() {
2399
+ return this.store;
2400
+ }
2401
+ /**
2402
+ * Mounts the CapDebug UI into the DOM and activates collectors.
2403
+ */
2404
+ mount(options = {}) {
2405
+ if (typeof window === "undefined" || typeof document === "undefined") {
2406
+ return;
2407
+ }
2408
+ if (this.isMounted) {
2409
+ this.unmount();
2410
+ }
2411
+ if (options.maxEvents) {
2412
+ this.store.setMaxEvents(options.maxEvents);
2413
+ }
2414
+ if (options.customMaskKeys) {
2415
+ this.store.setCustomMaskKeys(options.customMaskKeys);
2416
+ }
2417
+ this.customEventCollector.start();
2418
+ if (options.console !== false) {
2419
+ this.consoleCollector.start();
2420
+ }
2421
+ if (options.errors !== false) {
2422
+ this.errorCollector.start();
2423
+ }
2424
+ if (options.network) {
2425
+ const netOpts = typeof options.network === "object" ? options.network : {};
2426
+ this.networkCollector.setOptions(netOpts);
2427
+ this.networkCollector.start();
2428
+ }
2429
+ if (options.bridge) {
2430
+ this.bridgeCollector.start();
2431
+ }
2432
+ this.browserEventCollector.start();
2433
+ registerCustomElement();
2434
+ this.viewerElement = document.createElement(CapDebugViewerElement.TAG);
2435
+ this.viewerElement.init(this.store, options);
2436
+ document.body.appendChild(this.viewerElement);
2437
+ this.isMounted = true;
2438
+ }
2439
+ /**
2440
+ * Unmounts CapDebug UI and stops all collectors.
2441
+ */
2442
+ unmount() {
2443
+ if (!this.isMounted) return;
2444
+ if (this.viewerElement && this.viewerElement.parentNode) {
2445
+ this.viewerElement.parentNode.removeChild(this.viewerElement);
2446
+ this.viewerElement = null;
2447
+ }
2448
+ this.customEventCollector.stop();
2449
+ this.consoleCollector.stop();
2450
+ this.errorCollector.stop();
2451
+ this.networkCollector.stop();
2452
+ this.browserEventCollector.stop();
2453
+ this.bridgeCollector.stop();
2454
+ this.isMounted = false;
2455
+ }
2456
+ /**
2457
+ * Opens the debug viewer drawer.
2458
+ */
2459
+ show() {
2460
+ if (this.viewerElement) {
2461
+ this.viewerElement.show();
2678
2462
  }
2679
- observeEvent(eventName) {
2680
- this.browserEventCollector.observe(eventName);
2463
+ }
2464
+ /**
2465
+ * Closes the debug viewer drawer.
2466
+ */
2467
+ hide() {
2468
+ if (this.viewerElement) {
2469
+ this.viewerElement.hide();
2681
2470
  }
2682
- unobserveEvent(eventName) {
2683
- this.browserEventCollector.unobserve(eventName);
2471
+ }
2472
+ /**
2473
+ * Toggles the debug viewer drawer open/closed.
2474
+ */
2475
+ toggle() {
2476
+ if (this.viewerElement) {
2477
+ return this.viewerElement.toggle();
2478
+ }
2479
+ return false;
2480
+ }
2481
+ /**
2482
+ * Dispatches a debug event through the CustomEvent bus.
2483
+ */
2484
+ emit(event) {
2485
+ if (typeof window === "undefined") return;
2486
+ window.dispatchEvent(
2487
+ new CustomEvent("capdebug", {
2488
+ detail: {
2489
+ id: event.id,
2490
+ timestamp: event.timestamp || Date.now(),
2491
+ source: event.source || "web",
2492
+ type: event.type,
2493
+ level: event.level || "info",
2494
+ message: event.message,
2495
+ data: event.data,
2496
+ duration: event.duration,
2497
+ platform: event.platform
2498
+ }
2499
+ })
2500
+ );
2501
+ }
2502
+ /**
2503
+ * Clears all recorded events from the store.
2504
+ */
2505
+ clear() {
2506
+ this.store.clear();
2507
+ }
2508
+ /**
2509
+ * Pauses capturing new events.
2510
+ */
2511
+ pause() {
2512
+ this.store.pause();
2513
+ }
2514
+ /**
2515
+ * Resumes capturing events.
2516
+ */
2517
+ resume() {
2518
+ this.store.resume();
2519
+ }
2520
+ captureConsole() {
2521
+ this.consoleCollector.start();
2522
+ }
2523
+ releaseConsole() {
2524
+ this.consoleCollector.stop();
2525
+ }
2526
+ captureErrors() {
2527
+ this.errorCollector.start();
2528
+ }
2529
+ releaseErrors() {
2530
+ this.errorCollector.stop();
2531
+ }
2532
+ captureFetch(options) {
2533
+ if (options) {
2534
+ this.networkCollector.setOptions(options);
2684
2535
  }
2536
+ this.networkCollector.start();
2537
+ }
2538
+ releaseFetch() {
2539
+ this.networkCollector.stop();
2540
+ }
2541
+ observeEvent(eventName) {
2542
+ this.browserEventCollector.observe(eventName);
2543
+ }
2544
+ unobserveEvent(eventName) {
2545
+ this.browserEventCollector.unobserve(eventName);
2546
+ }
2685
2547
  }
2686
2548
  const CapDebug = new CapDebugManager();
2687
2549
 
2688
2550
  function formatBytes(bytes) {
2689
- if (bytes === 0)
2690
- return '0 B';
2691
- const k = 1024;
2692
- const sizes = ['B', 'KB', 'MB', 'GB'];
2693
- const i = Math.floor(Math.log(bytes) / Math.log(k));
2694
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
2551
+ if (bytes === 0) return "0 B";
2552
+ const k = 1024;
2553
+ const sizes = ["B", "KB", "MB", "GB"];
2554
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
2555
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
2695
2556
  }
2696
2557
  class CapDebugWeb extends WebPlugin {
2697
- async getDeviceInfo() {
2698
- const nav = typeof navigator !== 'undefined' ? navigator : {};
2699
- return {
2700
- manufacturer: 'Web Browser',
2701
- model: nav.userAgentData?.brands?.[0]?.brand || nav.appName || 'Browser',
2702
- device: nav.platform || 'Web',
2703
- systemVersion: nav.appVersion || 'Unknown',
2704
- architecture: nav.userAgentData?.architecture || 'Unknown',
2705
- availableProcessors: nav.hardwareConcurrency || 1,
2706
- isEmulator: false,
2707
- platform: 'web',
2708
- };
2709
- }
2710
- async getMemoryInfo() {
2711
- const perf = typeof window !== 'undefined' ? window.performance : null;
2712
- const memory = perf && perf.memory ? perf.memory : null;
2713
- if (memory) {
2714
- return {
2715
- appMemoryUsage: memory.usedJSHeapSize,
2716
- appMemoryUsageFormatted: formatBytes(memory.usedJSHeapSize),
2717
- totalHeap: memory.totalJSHeapSize,
2718
- totalHeapFormatted: formatBytes(memory.totalJSHeapSize),
2719
- maxHeap: memory.jsHeapSizeLimit,
2720
- maxHeapFormatted: formatBytes(memory.jsHeapSizeLimit),
2721
- freeHeap: memory.totalJSHeapSize - memory.usedJSHeapSize,
2722
- freeHeapFormatted: formatBytes(memory.totalJSHeapSize - memory.usedJSHeapSize),
2723
- lowMemory: false,
2724
- };
2725
- }
2726
- return {
2727
- appMemoryUsageFormatted: 'N/A (Browser non-Chrome)',
2728
- lowMemory: false,
2729
- };
2730
- }
2731
- async getWebViewInfo() {
2732
- const nav = typeof navigator !== 'undefined' ? navigator : {};
2733
- return {
2734
- userAgent: nav.userAgent || 'Unknown',
2735
- version: nav.appVersion || 'Unknown',
2736
- packageName: typeof window !== 'undefined' ? window.location.hostname : 'localhost',
2737
- debugEnabled: true,
2738
- };
2739
- }
2740
- async getAppInfo() {
2741
- return {
2742
- applicationId: typeof window !== 'undefined' ? window.location.host : 'localhost',
2743
- version: '1.0.0',
2744
- versionName: '1.0.0 (Web)',
2745
- versionCode: 1,
2746
- buildType: typeof process !== 'undefined' && process.env?.NODE_ENV ? process.env.NODE_ENV : 'development',
2747
- };
2748
- }
2749
- async getNetworkInfo() {
2750
- const nav = typeof navigator !== 'undefined' ? navigator : {};
2751
- const conn = nav.connection || nav.mozConnection || nav.webkitConnection;
2752
- return {
2753
- connected: nav.onLine ?? true,
2754
- connectionType: conn ? conn.effectiveType || conn.type || 'unknown' : (nav.onLine ? 'online' : 'offline'),
2755
- metered: conn ? conn.saveData ?? false : false,
2756
- };
2757
- }
2558
+ async getDeviceInfo() {
2559
+ const nav = typeof navigator !== "undefined" ? navigator : {};
2560
+ return {
2561
+ manufacturer: "Web Browser",
2562
+ model: nav.userAgentData?.brands?.[0]?.brand || nav.appName || "Browser",
2563
+ device: nav.platform || "Web",
2564
+ systemVersion: nav.appVersion || "Unknown",
2565
+ architecture: nav.userAgentData?.architecture || "Unknown",
2566
+ availableProcessors: nav.hardwareConcurrency || 1,
2567
+ isEmulator: false,
2568
+ platform: "web"
2569
+ };
2570
+ }
2571
+ async getMemoryInfo() {
2572
+ const perf = typeof window !== "undefined" ? window.performance : null;
2573
+ const memory = perf && perf.memory ? perf.memory : null;
2574
+ if (memory) {
2575
+ return {
2576
+ appMemoryUsage: memory.usedJSHeapSize,
2577
+ appMemoryUsageFormatted: formatBytes(memory.usedJSHeapSize),
2578
+ totalHeap: memory.totalJSHeapSize,
2579
+ totalHeapFormatted: formatBytes(memory.totalJSHeapSize),
2580
+ maxHeap: memory.jsHeapSizeLimit,
2581
+ maxHeapFormatted: formatBytes(memory.jsHeapSizeLimit),
2582
+ freeHeap: memory.totalJSHeapSize - memory.usedJSHeapSize,
2583
+ freeHeapFormatted: formatBytes(memory.totalJSHeapSize - memory.usedJSHeapSize),
2584
+ lowMemory: false
2585
+ };
2586
+ }
2587
+ return {
2588
+ appMemoryUsageFormatted: "N/A (Browser non-Chrome)",
2589
+ lowMemory: false
2590
+ };
2591
+ }
2592
+ async getWebViewInfo() {
2593
+ const nav = typeof navigator !== "undefined" ? navigator : {};
2594
+ return {
2595
+ userAgent: nav.userAgent || "Unknown",
2596
+ version: nav.appVersion || "Unknown",
2597
+ packageName: typeof window !== "undefined" ? window.location.hostname : "localhost",
2598
+ debugEnabled: true
2599
+ };
2600
+ }
2601
+ async getAppInfo() {
2602
+ return {
2603
+ applicationId: typeof window !== "undefined" ? window.location.host : "localhost",
2604
+ version: "1.0.0",
2605
+ versionName: "1.0.0 (Web)",
2606
+ versionCode: 1,
2607
+ buildType: typeof process !== "undefined" && process.env?.NODE_ENV ? process.env.NODE_ENV : "development"
2608
+ };
2609
+ }
2610
+ async getNetworkInfo() {
2611
+ const nav = typeof navigator !== "undefined" ? navigator : {};
2612
+ const conn = nav.connection || nav.mozConnection || nav.webkitConnection;
2613
+ return {
2614
+ connected: nav.onLine ?? true,
2615
+ connectionType: conn ? conn.effectiveType || conn.type || "unknown" : nav.onLine ? "online" : "offline",
2616
+ metered: conn ? conn.saveData ?? false : false
2617
+ };
2618
+ }
2758
2619
  }
2759
2620
 
2760
2621
  var web = /*#__PURE__*/Object.freeze({
2761
- __proto__: null,
2762
- CapDebugWeb: CapDebugWeb
2622
+ __proto__: null,
2623
+ CapDebugWeb: CapDebugWeb
2763
2624
  });
2764
2625
 
2765
2626
  export { BridgeCollector, BrowserEventCollector, CapDebug, CapDebugManager, CapDebugNative, CapDebugStore, CapDebugViewerElement, ConsoleCollector, CustomEventCollector, DataSanitizer, ErrorCollector, JsonViewer, NetworkCollector, CapDebug as default, defaultSanitizer, registerCustomElement, sanitize };