@ansight/capacitor 1.3.0-preview.1 → 1.3.0-preview.11
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/AnsightCapacitor.podspec +1 -1
- package/Package.swift +1 -1
- package/README.md +63 -0
- package/android/build.gradle +2 -2
- package/android/src/main/kotlin/ai/ansight/capacitor/AnsightCapacitorPlugin.kt +79 -4
- package/dist/esm/definitions.d.ts +101 -2
- package/dist/esm/definitions.d.ts.map +1 -1
- package/dist/esm/dom.d.ts.map +1 -1
- package/dist/esm/dom.js +91 -8
- package/dist/esm/dom.js.map +1 -1
- package/dist/esm/index.d.ts +20 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +131 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/network.d.ts +6 -0
- package/dist/esm/network.d.ts.map +1 -0
- package/dist/esm/network.js +742 -0
- package/dist/esm/network.js.map +1 -0
- package/dist/esm/options.d.ts +14 -2
- package/dist/esm/options.d.ts.map +1 -1
- package/dist/esm/options.js +73 -1
- package/dist/esm/options.js.map +1 -1
- package/dist/esm/session-properties.d.ts +16 -0
- package/dist/esm/session-properties.d.ts.map +1 -0
- package/dist/esm/session-properties.js +123 -0
- package/dist/esm/session-properties.js.map +1 -0
- package/dist/plugin.cjs.js +1162 -12
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +1162 -12
- package/dist/plugin.js.map +1 -1
- package/dist/standalone.js +1157 -12
- package/dist/standalone.js.map +1 -1
- package/ios/Sources/AnsightCapacitorPlugin/AnsightCapacitorPlugin.swift +81 -3
- package/package.json +1 -1
package/dist/plugin.cjs.js
CHANGED
|
@@ -4,6 +4,18 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var core = require('@capacitor/core');
|
|
6
6
|
|
|
7
|
+
function createTypeRegistry() {
|
|
8
|
+
return { idsByTypeName: new Map(), types: [] };
|
|
9
|
+
}
|
|
10
|
+
function registerType(registry, typeName) {
|
|
11
|
+
const existingTypeId = registry.idsByTypeName.get(typeName);
|
|
12
|
+
if (existingTypeId !== undefined)
|
|
13
|
+
return existingTypeId;
|
|
14
|
+
const typeId = registry.types.length;
|
|
15
|
+
registry.types.push(typeName);
|
|
16
|
+
registry.idsByTypeName.set(typeName, typeId);
|
|
17
|
+
return typeId;
|
|
18
|
+
}
|
|
7
19
|
const nodeIds = new WeakMap();
|
|
8
20
|
let nextNodeId = 1;
|
|
9
21
|
function nodeId(element) {
|
|
@@ -50,6 +62,55 @@ function automationId(element) {
|
|
|
50
62
|
element.id ??
|
|
51
63
|
undefined)?.trim() || undefined);
|
|
52
64
|
}
|
|
65
|
+
function semanticRole(element) {
|
|
66
|
+
const declared = element.getAttribute("role")?.trim().toLowerCase();
|
|
67
|
+
if (declared)
|
|
68
|
+
return declared;
|
|
69
|
+
if (element instanceof HTMLButtonElement)
|
|
70
|
+
return "button";
|
|
71
|
+
if (element instanceof HTMLInputElement) {
|
|
72
|
+
if (element.type === "checkbox")
|
|
73
|
+
return "checkbox";
|
|
74
|
+
if (element.type === "radio")
|
|
75
|
+
return "radio";
|
|
76
|
+
if (element.type === "range")
|
|
77
|
+
return "slider";
|
|
78
|
+
if (["button", "submit", "reset"].includes(element.type))
|
|
79
|
+
return "button";
|
|
80
|
+
return "textbox";
|
|
81
|
+
}
|
|
82
|
+
if (element instanceof HTMLTextAreaElement)
|
|
83
|
+
return "textbox";
|
|
84
|
+
if (element instanceof HTMLSelectElement)
|
|
85
|
+
return "combobox";
|
|
86
|
+
if (element.tagName === "A")
|
|
87
|
+
return "link";
|
|
88
|
+
if (/^H[1-6]$/.test(element.tagName))
|
|
89
|
+
return "heading";
|
|
90
|
+
return "view";
|
|
91
|
+
}
|
|
92
|
+
function supportedActions(element, allowActions) {
|
|
93
|
+
if (!allowActions)
|
|
94
|
+
return [];
|
|
95
|
+
const actions = [];
|
|
96
|
+
if (["A", "BUTTON", "SUMMARY"].includes(element.tagName) ||
|
|
97
|
+
element instanceof HTMLInputElement) {
|
|
98
|
+
actions.push("tap");
|
|
99
|
+
}
|
|
100
|
+
if (element instanceof HTMLInputElement ||
|
|
101
|
+
element instanceof HTMLTextAreaElement ||
|
|
102
|
+
element instanceof HTMLSelectElement) {
|
|
103
|
+
actions.push("typeText", "focus");
|
|
104
|
+
}
|
|
105
|
+
else if (element.tabIndex >= 0) {
|
|
106
|
+
actions.push("focus");
|
|
107
|
+
}
|
|
108
|
+
if (element.scrollHeight >
|
|
109
|
+
element.clientHeight) {
|
|
110
|
+
actions.push("scroll", "swipe");
|
|
111
|
+
}
|
|
112
|
+
return [...new Set(actions)];
|
|
113
|
+
}
|
|
53
114
|
function computedColorToArgbHex(value) {
|
|
54
115
|
const normalized = value.trim().toLowerCase();
|
|
55
116
|
if (!normalized)
|
|
@@ -115,7 +176,7 @@ function displayedValue(element) {
|
|
|
115
176
|
const normalized = value?.replace(/\s+/g, " ").trim();
|
|
116
177
|
return normalized ? normalized.slice(0, 240) : undefined;
|
|
117
178
|
}
|
|
118
|
-
function captureNode(element, options, depth, limits) {
|
|
179
|
+
function captureNode(element, options, depth, limits, typeRegistry) {
|
|
119
180
|
if (limits.count >= limits.maxNodes || depth > limits.maxDepth)
|
|
120
181
|
return null;
|
|
121
182
|
const style = getComputedStyle(element);
|
|
@@ -125,17 +186,23 @@ function captureNode(element, options, depth, limits) {
|
|
|
125
186
|
return null;
|
|
126
187
|
limits.count += 1;
|
|
127
188
|
const children = Array.from(element.children)
|
|
128
|
-
.map((child) => captureNode(child, options, depth + 1, limits))
|
|
189
|
+
.map((child) => captureNode(child, options, depth + 1, limits, typeRegistry))
|
|
129
190
|
.filter((child) => child !== null);
|
|
130
191
|
const htmlElement = element;
|
|
131
192
|
const disabled = element.hasAttribute("disabled") ||
|
|
132
193
|
element.getAttribute("aria-disabled") === "true";
|
|
133
194
|
const parsedOpacity = Number.parseFloat(style.opacity);
|
|
134
|
-
|
|
195
|
+
const parsedZIndex = Number.parseFloat(style.zIndex);
|
|
196
|
+
const actions = supportedActions(element, options.allowActions);
|
|
197
|
+
const label = accessibleLabel(element, options.includeText);
|
|
198
|
+
const node = {
|
|
135
199
|
id: nodeId(element),
|
|
136
|
-
|
|
200
|
+
typeId: registerType(typeRegistry, element.tagName.toLowerCase()),
|
|
137
201
|
automationId: automationId(element),
|
|
138
|
-
label
|
|
202
|
+
label,
|
|
203
|
+
role: semanticRole(element),
|
|
204
|
+
supportedActions: actions,
|
|
205
|
+
interactable: visible && !disabled && actions.length > 0,
|
|
139
206
|
visible,
|
|
140
207
|
enabled: !disabled,
|
|
141
208
|
focusable: htmlElement.tabIndex >= 0 ||
|
|
@@ -160,12 +227,15 @@ function captureNode(element, options, depth, limits) {
|
|
|
160
227
|
id: element.id || undefined,
|
|
161
228
|
role: element.getAttribute("role") ?? undefined,
|
|
162
229
|
className: element.getAttribute("class") ?? undefined,
|
|
163
|
-
value: options.includeText ? displayedValue(element) : undefined,
|
|
164
230
|
checked: element instanceof HTMLInputElement ? element.checked : undefined,
|
|
165
231
|
attributes: options.includeAttributes ? attributes(element) : undefined,
|
|
166
232
|
},
|
|
167
233
|
children,
|
|
168
234
|
};
|
|
235
|
+
if (Number.isFinite(parsedZIndex) && parsedZIndex !== 0) {
|
|
236
|
+
node.z = parsedZIndex;
|
|
237
|
+
}
|
|
238
|
+
return node;
|
|
169
239
|
}
|
|
170
240
|
function findElement(id) {
|
|
171
241
|
return Array.from(document.querySelectorAll("*")).find((element) => nodeId(element) === id);
|
|
@@ -198,12 +268,15 @@ function installDomTools$1(registerTool, input = {}) {
|
|
|
198
268
|
maxNodes: options.maxNodes,
|
|
199
269
|
count: 0,
|
|
200
270
|
};
|
|
201
|
-
const
|
|
271
|
+
const typeRegistry = createTypeRegistry();
|
|
272
|
+
const tree = captureNode(root, options, 0, limits, typeRegistry);
|
|
202
273
|
return successful({
|
|
274
|
+
format: "ansight.dom.visual-tree.compact.v2",
|
|
203
275
|
platform: "web",
|
|
204
276
|
source: options.source,
|
|
205
277
|
adapter: "@ansight/capacitor",
|
|
206
278
|
capturedAtUtc: new Date().toISOString(),
|
|
279
|
+
types: typeRegistry.types,
|
|
207
280
|
truncated: limits.count >= limits.maxNodes,
|
|
208
281
|
root: tree,
|
|
209
282
|
}, "DOM document captured.");
|
|
@@ -229,7 +302,17 @@ function installDomTools$1(registerTool, input = {}) {
|
|
|
229
302
|
};
|
|
230
303
|
}
|
|
231
304
|
const limits = { maxDepth: 0, maxNodes: 1, count: 0 };
|
|
232
|
-
|
|
305
|
+
const typeRegistry = createTypeRegistry();
|
|
306
|
+
const node = captureNode(element, options, 0, limits, typeRegistry);
|
|
307
|
+
return successful({
|
|
308
|
+
format: "ansight.dom.visual-tree.compact.v2",
|
|
309
|
+
platform: "web",
|
|
310
|
+
source: options.source,
|
|
311
|
+
adapter: "@ansight/capacitor",
|
|
312
|
+
capturedAtUtc: new Date().toISOString(),
|
|
313
|
+
types: typeRegistry.types,
|
|
314
|
+
node,
|
|
315
|
+
}, "DOM node captured.");
|
|
233
316
|
}),
|
|
234
317
|
registerTool({
|
|
235
318
|
id: "dom.query_selector",
|
|
@@ -336,6 +419,8 @@ class AnsightOptionsBuilder {
|
|
|
336
419
|
retentionPeriodSeconds: 120,
|
|
337
420
|
enableFramesPerSecond: true,
|
|
338
421
|
enableBatteryLevel: false,
|
|
422
|
+
enableOpenFileHandleTracking: false,
|
|
423
|
+
enableJniReferenceCountTracking: false,
|
|
339
424
|
sessionJpegCapture: {
|
|
340
425
|
intervalMilliseconds: 2000,
|
|
341
426
|
quality: 60,
|
|
@@ -378,6 +463,22 @@ class AnsightOptionsBuilder {
|
|
|
378
463
|
this.options.enableBatteryLevel = false;
|
|
379
464
|
return this;
|
|
380
465
|
}
|
|
466
|
+
withOpenFileHandleTracking() {
|
|
467
|
+
this.options.enableOpenFileHandleTracking = true;
|
|
468
|
+
return this;
|
|
469
|
+
}
|
|
470
|
+
withoutOpenFileHandleTracking() {
|
|
471
|
+
this.options.enableOpenFileHandleTracking = false;
|
|
472
|
+
return this;
|
|
473
|
+
}
|
|
474
|
+
withJniReferenceCountTracking() {
|
|
475
|
+
this.options.enableJniReferenceCountTracking = true;
|
|
476
|
+
return this;
|
|
477
|
+
}
|
|
478
|
+
withoutJniReferenceCountTracking() {
|
|
479
|
+
this.options.enableJniReferenceCountTracking = false;
|
|
480
|
+
return this;
|
|
481
|
+
}
|
|
381
482
|
withRetentionPeriodSeconds(value) {
|
|
382
483
|
this.options.retentionPeriodSeconds = value;
|
|
383
484
|
return this;
|
|
@@ -416,7 +517,7 @@ class AnsightOptionsBuilder {
|
|
|
416
517
|
this.options.defaultMemoryChannels = current;
|
|
417
518
|
return this;
|
|
418
519
|
}
|
|
419
|
-
withSessionJpegCapture(optionsOrIntervalMilliseconds = {}, quality = 60, maxWidth = 480, captureGpuBackedSurfaces = true, mode = "screenshotOnly") {
|
|
520
|
+
withSessionJpegCapture(optionsOrIntervalMilliseconds = {}, quality = 60, maxWidth = 480, captureGpuBackedSurfaces = true, mode = "screenshotOnly", captureKeyboardPresence = false) {
|
|
420
521
|
this.options.sessionJpegCapture =
|
|
421
522
|
typeof optionsOrIntervalMilliseconds === "number"
|
|
422
523
|
? {
|
|
@@ -425,11 +526,13 @@ class AnsightOptionsBuilder {
|
|
|
425
526
|
maxWidth,
|
|
426
527
|
captureGpuBackedSurfaces,
|
|
427
528
|
mode,
|
|
529
|
+
captureKeyboardPresence,
|
|
428
530
|
}
|
|
429
531
|
: {
|
|
430
532
|
intervalMilliseconds: 2000,
|
|
431
533
|
quality: 60,
|
|
432
534
|
maxWidth: 480,
|
|
535
|
+
captureKeyboardPresence: false,
|
|
433
536
|
mode: "screenshotOnly",
|
|
434
537
|
...optionsOrIntervalMilliseconds,
|
|
435
538
|
};
|
|
@@ -447,6 +550,14 @@ class AnsightOptionsBuilder {
|
|
|
447
550
|
this.options.touchCapture = false;
|
|
448
551
|
return this;
|
|
449
552
|
}
|
|
553
|
+
withCrashCapture(options = {}) {
|
|
554
|
+
this.options.crashCapture = { ...options, enabled: true };
|
|
555
|
+
return this;
|
|
556
|
+
}
|
|
557
|
+
withoutCrashCapture() {
|
|
558
|
+
this.options.crashCapture = false;
|
|
559
|
+
return this;
|
|
560
|
+
}
|
|
450
561
|
withLifecycleCapture(options = {}) {
|
|
451
562
|
this.options.lifecycleCapture = {
|
|
452
563
|
...options,
|
|
@@ -454,6 +565,50 @@ class AnsightOptionsBuilder {
|
|
|
454
565
|
};
|
|
455
566
|
return this;
|
|
456
567
|
}
|
|
568
|
+
withNetworkCapture(options = {}) {
|
|
569
|
+
this.options.networkCapture = { ...options };
|
|
570
|
+
return this;
|
|
571
|
+
}
|
|
572
|
+
withNetworkRequestBodies(maximumBodyBytes) {
|
|
573
|
+
if (typeof this.options.networkCapture !== "object")
|
|
574
|
+
return this;
|
|
575
|
+
const current = this.options.networkCapture;
|
|
576
|
+
this.options.networkCapture = {
|
|
577
|
+
...current,
|
|
578
|
+
captureRequestBody: true,
|
|
579
|
+
...(maximumBodyBytes == null ? {} : { maximumBodyBytes }),
|
|
580
|
+
};
|
|
581
|
+
return this;
|
|
582
|
+
}
|
|
583
|
+
withoutNetworkRequestBodies() {
|
|
584
|
+
if (typeof this.options.networkCapture !== "object")
|
|
585
|
+
return this;
|
|
586
|
+
const current = this.options.networkCapture;
|
|
587
|
+
this.options.networkCapture = { ...current, captureRequestBody: false };
|
|
588
|
+
return this;
|
|
589
|
+
}
|
|
590
|
+
withNetworkResponseBodies(maximumBodyBytes) {
|
|
591
|
+
if (typeof this.options.networkCapture !== "object")
|
|
592
|
+
return this;
|
|
593
|
+
const current = this.options.networkCapture;
|
|
594
|
+
this.options.networkCapture = {
|
|
595
|
+
...current,
|
|
596
|
+
captureResponseBody: true,
|
|
597
|
+
...(maximumBodyBytes == null ? {} : { maximumBodyBytes }),
|
|
598
|
+
};
|
|
599
|
+
return this;
|
|
600
|
+
}
|
|
601
|
+
withoutNetworkResponseBodies() {
|
|
602
|
+
if (typeof this.options.networkCapture !== "object")
|
|
603
|
+
return this;
|
|
604
|
+
const current = this.options.networkCapture;
|
|
605
|
+
this.options.networkCapture = { ...current, captureResponseBody: false };
|
|
606
|
+
return this;
|
|
607
|
+
}
|
|
608
|
+
withoutNetworkCapture() {
|
|
609
|
+
this.options.networkCapture = false;
|
|
610
|
+
return this;
|
|
611
|
+
}
|
|
457
612
|
withToolGuard(toolGuard) {
|
|
458
613
|
this.options.toolGuard = toolGuard;
|
|
459
614
|
return this;
|
|
@@ -621,6 +776,871 @@ function createOptionsBuilder(options = {}) {
|
|
|
621
776
|
return new AnsightOptionsBuilder(options);
|
|
622
777
|
}
|
|
623
778
|
|
|
779
|
+
const networkRequestSchema = "ansight.network-request.v1";
|
|
780
|
+
const redactedNetworkValue = "<redacted>";
|
|
781
|
+
const maximumHeaderCount = 128;
|
|
782
|
+
const maximumHeaderValueLength = 4096;
|
|
783
|
+
const maximumErrorMessageLength = 4096;
|
|
784
|
+
const maximumUrlLength = 16384;
|
|
785
|
+
const defaultMaximumBodyBytes = 64 * 1024;
|
|
786
|
+
const sensitiveHeaderNames = new Set([
|
|
787
|
+
"authorization",
|
|
788
|
+
"cookie",
|
|
789
|
+
"proxy-authorization",
|
|
790
|
+
"set-cookie",
|
|
791
|
+
"x-api-key",
|
|
792
|
+
"x-auth-token",
|
|
793
|
+
]);
|
|
794
|
+
const sensitiveQueryNames = new Set([
|
|
795
|
+
"access_token",
|
|
796
|
+
"accesskey",
|
|
797
|
+
"access_key",
|
|
798
|
+
"api_key",
|
|
799
|
+
"apikey",
|
|
800
|
+
"auth",
|
|
801
|
+
"authorization",
|
|
802
|
+
"client_secret",
|
|
803
|
+
"code",
|
|
804
|
+
"credential",
|
|
805
|
+
"credentials",
|
|
806
|
+
"id_token",
|
|
807
|
+
"jwt",
|
|
808
|
+
"key",
|
|
809
|
+
"password",
|
|
810
|
+
"passwd",
|
|
811
|
+
"refresh_token",
|
|
812
|
+
"sas",
|
|
813
|
+
"sastoken",
|
|
814
|
+
"secret",
|
|
815
|
+
"secret_key",
|
|
816
|
+
"security_token",
|
|
817
|
+
"session_token",
|
|
818
|
+
"sig",
|
|
819
|
+
"signature",
|
|
820
|
+
"token",
|
|
821
|
+
]);
|
|
822
|
+
const azureSasFingerprintNames = new Set([
|
|
823
|
+
"se",
|
|
824
|
+
"skoid",
|
|
825
|
+
"sp",
|
|
826
|
+
"sr",
|
|
827
|
+
"srt",
|
|
828
|
+
"ss",
|
|
829
|
+
"sv",
|
|
830
|
+
]);
|
|
831
|
+
const azureSasQueryNames = new Set([
|
|
832
|
+
"epk",
|
|
833
|
+
"erk",
|
|
834
|
+
"rscc",
|
|
835
|
+
"rscd",
|
|
836
|
+
"rsce",
|
|
837
|
+
"rscl",
|
|
838
|
+
"rsct",
|
|
839
|
+
"saoid",
|
|
840
|
+
"scid",
|
|
841
|
+
"se",
|
|
842
|
+
"sig",
|
|
843
|
+
"si",
|
|
844
|
+
"sip",
|
|
845
|
+
"ske",
|
|
846
|
+
"skoid",
|
|
847
|
+
"sks",
|
|
848
|
+
"skt",
|
|
849
|
+
"sktid",
|
|
850
|
+
"skv",
|
|
851
|
+
"snapshot",
|
|
852
|
+
"sp",
|
|
853
|
+
"spk",
|
|
854
|
+
"spr",
|
|
855
|
+
"sr",
|
|
856
|
+
"srk",
|
|
857
|
+
"srt",
|
|
858
|
+
"ss",
|
|
859
|
+
"st",
|
|
860
|
+
"suoid",
|
|
861
|
+
"tn",
|
|
862
|
+
"versionid",
|
|
863
|
+
"sv",
|
|
864
|
+
]);
|
|
865
|
+
function truncate(value, maximumLength) {
|
|
866
|
+
const text = String(value);
|
|
867
|
+
return text.length <= maximumLength
|
|
868
|
+
? text
|
|
869
|
+
: `${text.slice(0, maximumLength)}…`;
|
|
870
|
+
}
|
|
871
|
+
function normalizeRequired(value, fallback, maximumLength) {
|
|
872
|
+
const normalized = value == null ? "" : String(value).trim();
|
|
873
|
+
return truncate(normalized || fallback, maximumLength);
|
|
874
|
+
}
|
|
875
|
+
function normalizeOptional(value, maximumLength) {
|
|
876
|
+
if (value == null)
|
|
877
|
+
return undefined;
|
|
878
|
+
const normalized = String(value).trim();
|
|
879
|
+
return normalized ? truncate(normalized, maximumLength) : undefined;
|
|
880
|
+
}
|
|
881
|
+
function lowercaseSet(values) {
|
|
882
|
+
return new Set((values ?? []).map((value) => value.toLowerCase()));
|
|
883
|
+
}
|
|
884
|
+
function isSensitiveHeader(name, options) {
|
|
885
|
+
const lowered = name.toLowerCase();
|
|
886
|
+
if (sensitiveHeaderNames.has(lowered) ||
|
|
887
|
+
lowercaseSet(options.additionalSensitiveHeaderNames).has(lowered)) {
|
|
888
|
+
return true;
|
|
889
|
+
}
|
|
890
|
+
const compact = lowered.replaceAll("-", "");
|
|
891
|
+
return (compact.includes("token") ||
|
|
892
|
+
compact.includes("secret") ||
|
|
893
|
+
compact.includes("apikey"));
|
|
894
|
+
}
|
|
895
|
+
function headerEntries(headers) {
|
|
896
|
+
if (!headers)
|
|
897
|
+
return [];
|
|
898
|
+
if (Array.isArray(headers)) {
|
|
899
|
+
return headers.flatMap((header) => {
|
|
900
|
+
if (Array.isArray(header))
|
|
901
|
+
return [[header[0], header[1]]];
|
|
902
|
+
if (header && typeof header === "object") {
|
|
903
|
+
const value = header;
|
|
904
|
+
return [[value.name, value.value]];
|
|
905
|
+
}
|
|
906
|
+
return [];
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
if (typeof headers.forEach === "function") {
|
|
910
|
+
const entries = [];
|
|
911
|
+
headers.forEach((value, name) => entries.push([name, value]));
|
|
912
|
+
return entries;
|
|
913
|
+
}
|
|
914
|
+
return typeof headers === "object" ? Object.entries(headers) : [];
|
|
915
|
+
}
|
|
916
|
+
function sanitizeHeaders(headers, options) {
|
|
917
|
+
return headerEntries(headers)
|
|
918
|
+
.filter(([name]) => name != null && String(name).trim())
|
|
919
|
+
.slice(0, maximumHeaderCount)
|
|
920
|
+
.map(([rawName, rawValue]) => {
|
|
921
|
+
const name = normalizeRequired(rawName, "Header", 256);
|
|
922
|
+
return {
|
|
923
|
+
name,
|
|
924
|
+
value: isSensitiveHeader(name, options)
|
|
925
|
+
? redactedNetworkValue
|
|
926
|
+
: normalizeRequired(rawValue, "", maximumHeaderValueLength),
|
|
927
|
+
};
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
function sanitizeQuery(query, options) {
|
|
931
|
+
const appSensitive = lowercaseSet(options.additionalSensitiveQueryParameterNames);
|
|
932
|
+
const pairs = query.split("&");
|
|
933
|
+
const decodedNames = new Set(pairs.map((pair) => decodeQueryName(pair).toLowerCase()));
|
|
934
|
+
const hasAzureSas = decodedNames.has("sig") &&
|
|
935
|
+
[...azureSasFingerprintNames].some((name) => decodedNames.has(name));
|
|
936
|
+
const hasAwsSignature = decodedNames.has("x-amz-signature");
|
|
937
|
+
const hasGoogleSignature = decodedNames.has("x-goog-signature");
|
|
938
|
+
const hasCloudFrontSignature = decodedNames.has("signature") &&
|
|
939
|
+
["key-pair-id", "policy", "expires"].some((name) => decodedNames.has(name));
|
|
940
|
+
const hasLegacyGoogleSignature = decodedNames.has("signature") && decodedNames.has("googleaccessid");
|
|
941
|
+
const hasAlibabaSignature = (decodedNames.has("signature") && decodedNames.has("ossaccesskeyid")) ||
|
|
942
|
+
decodedNames.has("x-oss-signature");
|
|
943
|
+
return pairs
|
|
944
|
+
.map((pair) => {
|
|
945
|
+
const equalsIndex = pair.indexOf("=");
|
|
946
|
+
const encodedName = equalsIndex < 0 ? pair : pair.slice(0, equalsIndex);
|
|
947
|
+
const decodedName = decodeQueryName(pair);
|
|
948
|
+
const lowered = decodedName.toLowerCase();
|
|
949
|
+
const providerSensitive = (hasAzureSas && azureSasQueryNames.has(lowered)) ||
|
|
950
|
+
(hasAwsSignature && lowered.startsWith("x-amz-")) ||
|
|
951
|
+
(hasGoogleSignature && lowered.startsWith("x-goog-")) ||
|
|
952
|
+
(hasCloudFrontSignature &&
|
|
953
|
+
[
|
|
954
|
+
"signature",
|
|
955
|
+
"key-pair-id",
|
|
956
|
+
"policy",
|
|
957
|
+
"expires",
|
|
958
|
+
"hash-algorithm",
|
|
959
|
+
].includes(lowered)) ||
|
|
960
|
+
(hasLegacyGoogleSignature &&
|
|
961
|
+
["signature", "googleaccessid", "expires"].includes(lowered)) ||
|
|
962
|
+
(hasAlibabaSignature &&
|
|
963
|
+
(lowered.startsWith("x-oss-") ||
|
|
964
|
+
["signature", "ossaccesskeyid", "security-token"].includes(lowered)));
|
|
965
|
+
return providerSensitive ||
|
|
966
|
+
sensitiveQueryNames.has(lowered) ||
|
|
967
|
+
appSensitive.has(lowered)
|
|
968
|
+
? `${encodedName}=${encodeURIComponent(redactedNetworkValue)}`
|
|
969
|
+
: pair;
|
|
970
|
+
})
|
|
971
|
+
.join("&");
|
|
972
|
+
}
|
|
973
|
+
function decodeQueryName(pair) {
|
|
974
|
+
const equalsIndex = pair.indexOf("=");
|
|
975
|
+
const encodedName = equalsIndex < 0 ? pair : pair.slice(0, equalsIndex);
|
|
976
|
+
try {
|
|
977
|
+
return decodeURIComponent(encodedName.replaceAll("+", " "));
|
|
978
|
+
}
|
|
979
|
+
catch {
|
|
980
|
+
return encodedName;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
function sanitizeUrl(value, options) {
|
|
984
|
+
let normalized = normalizeRequired(value, "<unknown>", maximumUrlLength);
|
|
985
|
+
normalized = normalized.replace(/^(https?:\/\/)[^/@]+@/i, `$1${redactedNetworkValue}@`);
|
|
986
|
+
const queryIndex = normalized.indexOf("?");
|
|
987
|
+
if (queryIndex < 0)
|
|
988
|
+
return truncate(normalized, maximumUrlLength);
|
|
989
|
+
const fragmentIndex = normalized.indexOf("#", queryIndex);
|
|
990
|
+
if (options.includeQueryString === false) {
|
|
991
|
+
return truncate(normalized.slice(0, queryIndex) +
|
|
992
|
+
(fragmentIndex < 0 ? "" : normalized.slice(fragmentIndex)), maximumUrlLength);
|
|
993
|
+
}
|
|
994
|
+
const queryEnd = fragmentIndex < 0 ? normalized.length : fragmentIndex;
|
|
995
|
+
return truncate(normalized.slice(0, queryIndex + 1) +
|
|
996
|
+
sanitizeQuery(normalized.slice(queryIndex + 1, queryEnd), options) +
|
|
997
|
+
(fragmentIndex < 0 ? "" : normalized.slice(fragmentIndex)), maximumUrlLength);
|
|
998
|
+
}
|
|
999
|
+
function sanitizeErrorMessage(value, options) {
|
|
1000
|
+
const normalized = normalizeOptional(value, maximumErrorMessageLength);
|
|
1001
|
+
if (!normalized)
|
|
1002
|
+
return undefined;
|
|
1003
|
+
return truncate(normalized
|
|
1004
|
+
.replace(/(access_token|api_key|apikey|auth|authorization|code|key|password|passwd|secret|signature|token)(\s*=\s*)([^&\s,;]+)/gi, `$1$2${redactedNetworkValue}`)
|
|
1005
|
+
.replace(/https?:\/\/[^\s"'<>]+/gi, (url) => sanitizeUrl(url, options)), maximumErrorMessageLength);
|
|
1006
|
+
}
|
|
1007
|
+
function normalizeTimestamp(value, fallback) {
|
|
1008
|
+
const date = new Date(value == null ? fallback : String(value));
|
|
1009
|
+
return Number.isFinite(date.valueOf()) ? date.toISOString() : fallback;
|
|
1010
|
+
}
|
|
1011
|
+
function generateId(globalObject) {
|
|
1012
|
+
if (typeof globalObject.crypto?.randomUUID === "function") {
|
|
1013
|
+
return globalObject.crypto.randomUUID().replaceAll("-", "");
|
|
1014
|
+
}
|
|
1015
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
1016
|
+
}
|
|
1017
|
+
function normalizeSize(value) {
|
|
1018
|
+
const number = Number(value);
|
|
1019
|
+
return Number.isFinite(number) && number >= 0
|
|
1020
|
+
? Math.round(number)
|
|
1021
|
+
: undefined;
|
|
1022
|
+
}
|
|
1023
|
+
function maximumBodyBytes(options) {
|
|
1024
|
+
const configured = Number(options.maximumBodyBytes);
|
|
1025
|
+
const value = Number.isFinite(configured)
|
|
1026
|
+
? Math.round(configured)
|
|
1027
|
+
: defaultMaximumBodyBytes;
|
|
1028
|
+
return Math.max(0, value);
|
|
1029
|
+
}
|
|
1030
|
+
function sanitizeSensitiveText(value, options) {
|
|
1031
|
+
return value
|
|
1032
|
+
.replace(/(access_token|accesskey|access_key|api_key|apikey|auth|authorization|client_secret|code|credential|credentials|id_token|jwt|key|password|passwd|refresh_token|sas|sastoken|secret|secret_key|security_token|session_token|sig|signature|token)(["']?\s*[:=]\s*["']?)([^&\s,;}"']+)/gi, `$1$2${redactedNetworkValue}`)
|
|
1033
|
+
.replace(/https?:\/\/[^\s"'<>]+/gi, (url) => sanitizeUrl(url, options));
|
|
1034
|
+
}
|
|
1035
|
+
function truncateUtf8(bytes, maximum) {
|
|
1036
|
+
let length = Math.min(bytes.length, maximum);
|
|
1037
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1038
|
+
while (length > 0) {
|
|
1039
|
+
try {
|
|
1040
|
+
decoder.decode(bytes.slice(0, length));
|
|
1041
|
+
return bytes.slice(0, length);
|
|
1042
|
+
}
|
|
1043
|
+
catch {
|
|
1044
|
+
length -= 1;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
return new Uint8Array();
|
|
1048
|
+
}
|
|
1049
|
+
function bytesToBase64$1(bytes) {
|
|
1050
|
+
let binary = "";
|
|
1051
|
+
for (const byte of bytes)
|
|
1052
|
+
binary += String.fromCharCode(byte);
|
|
1053
|
+
return btoa(binary);
|
|
1054
|
+
}
|
|
1055
|
+
function base64ToBytes(value) {
|
|
1056
|
+
const binary = atob(value);
|
|
1057
|
+
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
1058
|
+
}
|
|
1059
|
+
function normalizeBody(body, options) {
|
|
1060
|
+
const maximum = maximumBodyBytes(options);
|
|
1061
|
+
if (!body || maximum <= 0)
|
|
1062
|
+
return undefined;
|
|
1063
|
+
const encoding = body.encoding?.toLowerCase();
|
|
1064
|
+
let bytes;
|
|
1065
|
+
try {
|
|
1066
|
+
if (encoding === "utf8") {
|
|
1067
|
+
bytes = new TextEncoder().encode(sanitizeSensitiveText(body.data, options));
|
|
1068
|
+
}
|
|
1069
|
+
else if (encoding === "base64" && options.captureBinaryBodies === true) {
|
|
1070
|
+
bytes = base64ToBytes(body.data);
|
|
1071
|
+
}
|
|
1072
|
+
else {
|
|
1073
|
+
return undefined;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
catch {
|
|
1077
|
+
return undefined;
|
|
1078
|
+
}
|
|
1079
|
+
const originalLength = bytes.length;
|
|
1080
|
+
const captured = encoding === "utf8"
|
|
1081
|
+
? truncateUtf8(bytes, maximum)
|
|
1082
|
+
: bytes.slice(0, maximum);
|
|
1083
|
+
const totalBytes = normalizeSize(body.totalBytes);
|
|
1084
|
+
return {
|
|
1085
|
+
contentType: normalizeOptional(body.contentType, 512),
|
|
1086
|
+
encoding,
|
|
1087
|
+
data: encoding === "base64"
|
|
1088
|
+
? bytesToBase64$1(captured)
|
|
1089
|
+
: new TextDecoder().decode(captured),
|
|
1090
|
+
capturedBytes: captured.length,
|
|
1091
|
+
totalBytes,
|
|
1092
|
+
truncated: body.truncated ||
|
|
1093
|
+
originalLength > captured.length ||
|
|
1094
|
+
(totalBytes != null && totalBytes > captured.length),
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
function normalizeRecord(input, options, globalObject) {
|
|
1098
|
+
const now = new Date().toISOString();
|
|
1099
|
+
const startedAtUtc = normalizeTimestamp(input.startedAtUtc, now);
|
|
1100
|
+
const completedAtUtc = normalizeTimestamp(input.completedAtUtc, startedAtUtc);
|
|
1101
|
+
const duration = Number(input.durationMilliseconds);
|
|
1102
|
+
return {
|
|
1103
|
+
schema: networkRequestSchema,
|
|
1104
|
+
id: normalizeRequired(input.id, generateId(globalObject), 128),
|
|
1105
|
+
source: normalizeRequired(input.source, "unknown", 128),
|
|
1106
|
+
startedAtUtc,
|
|
1107
|
+
completedAtUtc: completedAtUtc < startedAtUtc ? startedAtUtc : completedAtUtc,
|
|
1108
|
+
durationMilliseconds: Number.isFinite(duration) && duration >= 0 ? duration : 0,
|
|
1109
|
+
method: normalizeRequired(input.method, "GET", 32).toUpperCase(),
|
|
1110
|
+
url: sanitizeUrl(input.url, options),
|
|
1111
|
+
protocol: normalizeOptional(input.protocol, 64),
|
|
1112
|
+
requestHeaders: options.includeRequestHeaders === false
|
|
1113
|
+
? []
|
|
1114
|
+
: sanitizeHeaders(input.requestHeaders, options),
|
|
1115
|
+
requestBodySizeBytes: options.includeBodySizes === false
|
|
1116
|
+
? undefined
|
|
1117
|
+
: normalizeSize(input.requestBodySizeBytes),
|
|
1118
|
+
requestBody: options.captureRequestBody !== false
|
|
1119
|
+
? normalizeBody(input.requestBody, options)
|
|
1120
|
+
: undefined,
|
|
1121
|
+
statusCode: Number.isInteger(Number(input.statusCode)) &&
|
|
1122
|
+
Number(input.statusCode) >= 100 &&
|
|
1123
|
+
Number(input.statusCode) <= 999
|
|
1124
|
+
? Number(input.statusCode)
|
|
1125
|
+
: undefined,
|
|
1126
|
+
reasonPhrase: normalizeOptional(input.reasonPhrase, 512),
|
|
1127
|
+
responseHeaders: options.includeResponseHeaders === false
|
|
1128
|
+
? []
|
|
1129
|
+
: sanitizeHeaders(input.responseHeaders, options),
|
|
1130
|
+
responseBodySizeBytes: options.includeBodySizes === false
|
|
1131
|
+
? undefined
|
|
1132
|
+
: normalizeSize(input.responseBodySizeBytes),
|
|
1133
|
+
responseBody: options.captureResponseBody !== false
|
|
1134
|
+
? normalizeBody(input.responseBody, options)
|
|
1135
|
+
: undefined,
|
|
1136
|
+
errorType: normalizeOptional(input.errorType, 512),
|
|
1137
|
+
errorMessage: sanitizeErrorMessage(input.errorMessage, options),
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
function sanitizeNetworkRequest(input, options = {}, globalObject = globalThis) {
|
|
1141
|
+
try {
|
|
1142
|
+
let normalized = normalizeRecord(input, options, globalObject);
|
|
1143
|
+
if (options.urlSanitizer) {
|
|
1144
|
+
normalized = normalizeRecord({ ...normalized, url: options.urlSanitizer(normalized.url) }, options, globalObject);
|
|
1145
|
+
}
|
|
1146
|
+
if (options.requestSanitizer) {
|
|
1147
|
+
const transformed = options.requestSanitizer(normalized);
|
|
1148
|
+
if (transformed == null)
|
|
1149
|
+
return null;
|
|
1150
|
+
normalized = normalizeRecord(transformed, options, globalObject);
|
|
1151
|
+
}
|
|
1152
|
+
return normalized;
|
|
1153
|
+
}
|
|
1154
|
+
catch {
|
|
1155
|
+
return null;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
function parseContentLength(headers) {
|
|
1159
|
+
const entry = headerEntries(headers).find(([name]) => String(name).toLowerCase() === "content-length");
|
|
1160
|
+
return entry ? normalizeSize(entry[1]) : undefined;
|
|
1161
|
+
}
|
|
1162
|
+
function headerValue(headers, wantedName) {
|
|
1163
|
+
const entry = headerEntries(headers).find(([name]) => String(name).toLowerCase() === wantedName);
|
|
1164
|
+
return entry == null ? undefined : String(entry[1]);
|
|
1165
|
+
}
|
|
1166
|
+
function isTextContentType(contentType) {
|
|
1167
|
+
if (!contentType)
|
|
1168
|
+
return true;
|
|
1169
|
+
const mediaType = contentType.split(";", 1)[0].trim().toLowerCase();
|
|
1170
|
+
return (mediaType.startsWith("text/") ||
|
|
1171
|
+
mediaType.endsWith("+json") ||
|
|
1172
|
+
mediaType.endsWith("+xml") ||
|
|
1173
|
+
[
|
|
1174
|
+
"application/json",
|
|
1175
|
+
"application/xml",
|
|
1176
|
+
"application/graphql",
|
|
1177
|
+
"application/javascript",
|
|
1178
|
+
"application/x-www-form-urlencoded",
|
|
1179
|
+
].includes(mediaType));
|
|
1180
|
+
}
|
|
1181
|
+
function bodyFromBytes(bytes, totalBytes, contentType, options) {
|
|
1182
|
+
const binary = !isTextContentType(contentType);
|
|
1183
|
+
if (binary && options.captureBinaryBodies !== true)
|
|
1184
|
+
return undefined;
|
|
1185
|
+
const maximum = maximumBodyBytes(options);
|
|
1186
|
+
if (maximum <= 0)
|
|
1187
|
+
return undefined;
|
|
1188
|
+
const captured = binary
|
|
1189
|
+
? bytes.slice(0, maximum)
|
|
1190
|
+
: truncateUtf8(bytes, maximum);
|
|
1191
|
+
return {
|
|
1192
|
+
contentType,
|
|
1193
|
+
encoding: binary ? "base64" : "utf8",
|
|
1194
|
+
data: binary ? bytesToBase64$1(captured) : new TextDecoder().decode(captured),
|
|
1195
|
+
capturedBytes: captured.length,
|
|
1196
|
+
totalBytes,
|
|
1197
|
+
truncated: bytes.length > captured.length ||
|
|
1198
|
+
(totalBytes != null && totalBytes > captured.length),
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
function bodyFromValue(value, headers, options) {
|
|
1202
|
+
if (value == null || options.captureRequestBody === false)
|
|
1203
|
+
return undefined;
|
|
1204
|
+
const contentType = headerValue(headers, "content-type");
|
|
1205
|
+
if (typeof value === "string" || value instanceof URLSearchParams) {
|
|
1206
|
+
const bytes = new TextEncoder().encode(String(value));
|
|
1207
|
+
return bodyFromBytes(bytes, bytes.length, contentType ||
|
|
1208
|
+
(value instanceof URLSearchParams
|
|
1209
|
+
? "application/x-www-form-urlencoded"
|
|
1210
|
+
: undefined), options);
|
|
1211
|
+
}
|
|
1212
|
+
if (value instanceof ArrayBuffer) {
|
|
1213
|
+
const bytes = new Uint8Array(value);
|
|
1214
|
+
return bodyFromBytes(bytes, bytes.length, contentType, options);
|
|
1215
|
+
}
|
|
1216
|
+
if (ArrayBuffer.isView(value)) {
|
|
1217
|
+
const bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
1218
|
+
return bodyFromBytes(bytes, bytes.length, contentType, options);
|
|
1219
|
+
}
|
|
1220
|
+
return undefined;
|
|
1221
|
+
}
|
|
1222
|
+
async function bodyFromFetchResponse(response, headers, options, shouldContinue = () => true) {
|
|
1223
|
+
if (!shouldContinue() || options.captureResponseBody === false)
|
|
1224
|
+
return undefined;
|
|
1225
|
+
const contentType = headerValue(headers, "content-type");
|
|
1226
|
+
if (!isTextContentType(contentType) && options.captureBinaryBodies !== true) {
|
|
1227
|
+
return undefined;
|
|
1228
|
+
}
|
|
1229
|
+
const totalBytes = parseContentLength(headers);
|
|
1230
|
+
const maximum = maximumBodyBytes(options);
|
|
1231
|
+
if (maximum <= 0)
|
|
1232
|
+
return undefined;
|
|
1233
|
+
const clone = response.clone();
|
|
1234
|
+
if (clone.body) {
|
|
1235
|
+
const reader = clone.body.getReader();
|
|
1236
|
+
const chunks = [];
|
|
1237
|
+
let capturedLength = 0;
|
|
1238
|
+
let observedLength = 0;
|
|
1239
|
+
try {
|
|
1240
|
+
while (capturedLength <= maximum) {
|
|
1241
|
+
if (!shouldContinue()) {
|
|
1242
|
+
await reader.cancel().catch(() => undefined);
|
|
1243
|
+
return undefined;
|
|
1244
|
+
}
|
|
1245
|
+
const result = await reader.read();
|
|
1246
|
+
if (!shouldContinue()) {
|
|
1247
|
+
await reader.cancel().catch(() => undefined);
|
|
1248
|
+
return undefined;
|
|
1249
|
+
}
|
|
1250
|
+
if (result.done)
|
|
1251
|
+
break;
|
|
1252
|
+
const chunk = result.value;
|
|
1253
|
+
observedLength += chunk.length;
|
|
1254
|
+
const remaining = maximum - capturedLength;
|
|
1255
|
+
if (remaining > 0) {
|
|
1256
|
+
const kept = chunk.slice(0, remaining);
|
|
1257
|
+
chunks.push(kept);
|
|
1258
|
+
capturedLength += kept.length;
|
|
1259
|
+
}
|
|
1260
|
+
if (observedLength > maximum) {
|
|
1261
|
+
await reader.cancel().catch(() => undefined);
|
|
1262
|
+
break;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
finally {
|
|
1267
|
+
reader.releaseLock();
|
|
1268
|
+
}
|
|
1269
|
+
const joined = new Uint8Array(capturedLength);
|
|
1270
|
+
let offset = 0;
|
|
1271
|
+
for (const chunk of chunks) {
|
|
1272
|
+
joined.set(chunk, offset);
|
|
1273
|
+
offset += chunk.length;
|
|
1274
|
+
}
|
|
1275
|
+
return bodyFromBytes(joined, totalBytes ?? observedLength, contentType, options);
|
|
1276
|
+
}
|
|
1277
|
+
if (totalBytes == null || totalBytes > maximum)
|
|
1278
|
+
return undefined;
|
|
1279
|
+
const bytes = new Uint8Array(await clone.arrayBuffer());
|
|
1280
|
+
if (!shouldContinue())
|
|
1281
|
+
return undefined;
|
|
1282
|
+
return bodyFromBytes(bytes, totalBytes, contentType, options);
|
|
1283
|
+
}
|
|
1284
|
+
function parseXhrResponseHeaders(value) {
|
|
1285
|
+
return value
|
|
1286
|
+
.trim()
|
|
1287
|
+
.split(/[\r\n]+/)
|
|
1288
|
+
.flatMap((line) => {
|
|
1289
|
+
const separator = line.indexOf(":");
|
|
1290
|
+
return separator < 0
|
|
1291
|
+
? []
|
|
1292
|
+
: [
|
|
1293
|
+
{
|
|
1294
|
+
name: line.slice(0, separator),
|
|
1295
|
+
value: line.slice(separator + 1),
|
|
1296
|
+
},
|
|
1297
|
+
];
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
function monotonicNow(globalObject) {
|
|
1301
|
+
return typeof globalObject.performance?.now === "function"
|
|
1302
|
+
? globalObject.performance.now()
|
|
1303
|
+
: Date.now();
|
|
1304
|
+
}
|
|
1305
|
+
function installBrowserNetworkCapture(capture, options = {}, sourcePrefix = "capacitor", globalObject = globalThis) {
|
|
1306
|
+
const cleanups = [];
|
|
1307
|
+
let active = true;
|
|
1308
|
+
let fetchInvocationDepth = 0;
|
|
1309
|
+
if (options.captureFetch !== false &&
|
|
1310
|
+
typeof globalObject.fetch === "function") {
|
|
1311
|
+
const originalFetch = globalObject.fetch;
|
|
1312
|
+
const wrappedFetch = function (input, init) {
|
|
1313
|
+
const startedAtUtc = new Date().toISOString();
|
|
1314
|
+
const started = monotonicNow(globalObject);
|
|
1315
|
+
const inputRequest = typeof input === "object" && "headers" in input && "method" in input
|
|
1316
|
+
? input
|
|
1317
|
+
: undefined;
|
|
1318
|
+
const requestHeaders = headerEntries(inputRequest?.headers).concat(headerEntries(init?.headers));
|
|
1319
|
+
const requestBody = bodyFromValue(init?.body, requestHeaders, options);
|
|
1320
|
+
const method = init?.method || inputRequest?.method || "GET";
|
|
1321
|
+
const url = typeof input === "string" ? input : inputRequest?.url || String(input);
|
|
1322
|
+
let promise;
|
|
1323
|
+
fetchInvocationDepth += 1;
|
|
1324
|
+
try {
|
|
1325
|
+
promise = originalFetch(input, init);
|
|
1326
|
+
}
|
|
1327
|
+
catch (error) {
|
|
1328
|
+
fetchInvocationDepth -= 1;
|
|
1329
|
+
const request = sanitizeNetworkRequest({
|
|
1330
|
+
source: `${sourcePrefix}.fetch`,
|
|
1331
|
+
startedAtUtc,
|
|
1332
|
+
completedAtUtc: new Date().toISOString(),
|
|
1333
|
+
durationMilliseconds: monotonicNow(globalObject) - started,
|
|
1334
|
+
method,
|
|
1335
|
+
url,
|
|
1336
|
+
requestHeaders: sanitizeHeaders(requestHeaders, {}),
|
|
1337
|
+
requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
|
|
1338
|
+
requestBody,
|
|
1339
|
+
errorType: error instanceof Error ? error.name : "Error",
|
|
1340
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
1341
|
+
}, options, globalObject);
|
|
1342
|
+
if (active && request)
|
|
1343
|
+
Promise.resolve(capture(request)).catch(() => undefined);
|
|
1344
|
+
throw error;
|
|
1345
|
+
}
|
|
1346
|
+
fetchInvocationDepth -= 1;
|
|
1347
|
+
return promise.then((response) => {
|
|
1348
|
+
if (!active)
|
|
1349
|
+
return response;
|
|
1350
|
+
const responseHeaders = headerEntries(response.headers);
|
|
1351
|
+
const responseRecord = {
|
|
1352
|
+
source: `${sourcePrefix}.fetch`,
|
|
1353
|
+
startedAtUtc,
|
|
1354
|
+
completedAtUtc: new Date().toISOString(),
|
|
1355
|
+
durationMilliseconds: monotonicNow(globalObject) - started,
|
|
1356
|
+
method,
|
|
1357
|
+
url: response.url || url,
|
|
1358
|
+
requestHeaders: sanitizeHeaders(requestHeaders, {}),
|
|
1359
|
+
requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
|
|
1360
|
+
requestBody,
|
|
1361
|
+
statusCode: response.status,
|
|
1362
|
+
reasonPhrase: response.statusText,
|
|
1363
|
+
responseHeaders: sanitizeHeaders(responseHeaders, {}),
|
|
1364
|
+
responseBodySizeBytes: parseContentLength(responseHeaders),
|
|
1365
|
+
};
|
|
1366
|
+
return bodyFromFetchResponse(response, responseHeaders, options, () => active).then((responseBody) => {
|
|
1367
|
+
const request = sanitizeNetworkRequest({
|
|
1368
|
+
...responseRecord,
|
|
1369
|
+
responseBody,
|
|
1370
|
+
responseBodySizeBytes: responseRecord.responseBodySizeBytes ??
|
|
1371
|
+
responseBody?.totalBytes,
|
|
1372
|
+
}, options, globalObject);
|
|
1373
|
+
if (active && request)
|
|
1374
|
+
void Promise.resolve(capture(request)).catch(() => undefined);
|
|
1375
|
+
return response;
|
|
1376
|
+
}, () => {
|
|
1377
|
+
const request = sanitizeNetworkRequest(responseRecord, options, globalObject);
|
|
1378
|
+
if (active && request)
|
|
1379
|
+
void Promise.resolve(capture(request)).catch(() => undefined);
|
|
1380
|
+
return response;
|
|
1381
|
+
});
|
|
1382
|
+
}, (error) => {
|
|
1383
|
+
const request = sanitizeNetworkRequest({
|
|
1384
|
+
source: `${sourcePrefix}.fetch`,
|
|
1385
|
+
startedAtUtc,
|
|
1386
|
+
completedAtUtc: new Date().toISOString(),
|
|
1387
|
+
durationMilliseconds: monotonicNow(globalObject) - started,
|
|
1388
|
+
method,
|
|
1389
|
+
url,
|
|
1390
|
+
requestHeaders: sanitizeHeaders(requestHeaders, {}),
|
|
1391
|
+
requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
|
|
1392
|
+
requestBody,
|
|
1393
|
+
errorType: error instanceof Error ? error.name : "Error",
|
|
1394
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
1395
|
+
}, options, globalObject);
|
|
1396
|
+
if (active && request)
|
|
1397
|
+
Promise.resolve(capture(request)).catch(() => undefined);
|
|
1398
|
+
throw error;
|
|
1399
|
+
});
|
|
1400
|
+
};
|
|
1401
|
+
globalObject.fetch = wrappedFetch;
|
|
1402
|
+
cleanups.push(() => {
|
|
1403
|
+
if (globalObject.fetch === wrappedFetch)
|
|
1404
|
+
globalObject.fetch = originalFetch;
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
const Xhr = globalObject.XMLHttpRequest;
|
|
1408
|
+
if (options.captureXmlHttpRequest !== false && Xhr?.prototype) {
|
|
1409
|
+
const states = new WeakMap();
|
|
1410
|
+
const prototype = Xhr.prototype;
|
|
1411
|
+
const originalOpen = prototype.open;
|
|
1412
|
+
const originalSend = prototype.send;
|
|
1413
|
+
const originalSetRequestHeader = prototype.setRequestHeader;
|
|
1414
|
+
const wrappedOpen = function (method, url, ...rest) {
|
|
1415
|
+
states.set(this, {
|
|
1416
|
+
method,
|
|
1417
|
+
url: String(url),
|
|
1418
|
+
requestHeaders: [],
|
|
1419
|
+
suppressed: fetchInvocationDepth > 0,
|
|
1420
|
+
});
|
|
1421
|
+
Reflect.apply(originalOpen, this, [method, url, ...rest]);
|
|
1422
|
+
};
|
|
1423
|
+
const wrappedSetRequestHeader = function (name, value) {
|
|
1424
|
+
states.get(this)?.requestHeaders.push({ name, value });
|
|
1425
|
+
Reflect.apply(originalSetRequestHeader, this, [name, value]);
|
|
1426
|
+
};
|
|
1427
|
+
const wrappedSend = function (body) {
|
|
1428
|
+
const state = states.get(this);
|
|
1429
|
+
if (!state || state.suppressed) {
|
|
1430
|
+
Reflect.apply(originalSend, this, [body]);
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
state.startedAtUtc = new Date().toISOString();
|
|
1434
|
+
state.started = monotonicNow(globalObject);
|
|
1435
|
+
state.requestBody = bodyFromValue(body, state.requestHeaders, options);
|
|
1436
|
+
let failure;
|
|
1437
|
+
const markFailure = (event) => {
|
|
1438
|
+
failure = event.type;
|
|
1439
|
+
};
|
|
1440
|
+
const complete = () => {
|
|
1441
|
+
if (!active)
|
|
1442
|
+
return;
|
|
1443
|
+
let responseHeaders = [];
|
|
1444
|
+
try {
|
|
1445
|
+
responseHeaders = parseXhrResponseHeaders(this.getAllResponseHeaders());
|
|
1446
|
+
}
|
|
1447
|
+
catch {
|
|
1448
|
+
// Some WebViews throw before response headers exist.
|
|
1449
|
+
}
|
|
1450
|
+
let responseBody;
|
|
1451
|
+
try {
|
|
1452
|
+
const responseType = this.responseType || "text";
|
|
1453
|
+
const responseOptions = {
|
|
1454
|
+
...options,
|
|
1455
|
+
captureRequestBody: options.captureResponseBody,
|
|
1456
|
+
};
|
|
1457
|
+
if (responseType === "text") {
|
|
1458
|
+
responseBody = bodyFromValue(this.responseText, responseHeaders, responseOptions);
|
|
1459
|
+
}
|
|
1460
|
+
else if (responseType === "arraybuffer") {
|
|
1461
|
+
responseBody = bodyFromValue(this.response, responseHeaders, responseOptions);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
catch {
|
|
1465
|
+
// Response data is not readable for every XHR response type.
|
|
1466
|
+
}
|
|
1467
|
+
const request = sanitizeNetworkRequest({
|
|
1468
|
+
source: `${sourcePrefix}.xhr`,
|
|
1469
|
+
startedAtUtc: state.startedAtUtc,
|
|
1470
|
+
completedAtUtc: new Date().toISOString(),
|
|
1471
|
+
durationMilliseconds: monotonicNow(globalObject) - (state.started ?? 0),
|
|
1472
|
+
method: state.method,
|
|
1473
|
+
url: this.responseURL || state.url,
|
|
1474
|
+
requestHeaders: state.requestHeaders,
|
|
1475
|
+
requestBodySizeBytes: parseContentLength(state.requestHeaders) ??
|
|
1476
|
+
state.requestBody?.totalBytes,
|
|
1477
|
+
requestBody: state.requestBody,
|
|
1478
|
+
statusCode: this.status || undefined,
|
|
1479
|
+
reasonPhrase: this.statusText,
|
|
1480
|
+
responseHeaders,
|
|
1481
|
+
responseBodySizeBytes: parseContentLength(responseHeaders) ?? responseBody?.totalBytes,
|
|
1482
|
+
responseBody,
|
|
1483
|
+
errorType: failure,
|
|
1484
|
+
errorMessage: failure ? `XMLHttpRequest ${failure}` : undefined,
|
|
1485
|
+
}, options, globalObject);
|
|
1486
|
+
if (request)
|
|
1487
|
+
Promise.resolve(capture(request)).catch(() => undefined);
|
|
1488
|
+
};
|
|
1489
|
+
this.addEventListener("error", markFailure);
|
|
1490
|
+
this.addEventListener("abort", markFailure);
|
|
1491
|
+
this.addEventListener("timeout", markFailure);
|
|
1492
|
+
this.addEventListener("loadend", complete, { once: true });
|
|
1493
|
+
Reflect.apply(originalSend, this, [body]);
|
|
1494
|
+
};
|
|
1495
|
+
prototype.open = wrappedOpen;
|
|
1496
|
+
prototype.setRequestHeader = wrappedSetRequestHeader;
|
|
1497
|
+
prototype.send = wrappedSend;
|
|
1498
|
+
cleanups.push(() => {
|
|
1499
|
+
if (prototype.open === wrappedOpen)
|
|
1500
|
+
prototype.open = originalOpen;
|
|
1501
|
+
if (prototype.send === wrappedSend)
|
|
1502
|
+
prototype.send = originalSend;
|
|
1503
|
+
if (prototype.setRequestHeader === wrappedSetRequestHeader) {
|
|
1504
|
+
prototype.setRequestHeader = originalSetRequestHeader;
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
let removed = false;
|
|
1509
|
+
return {
|
|
1510
|
+
remove() {
|
|
1511
|
+
if (removed)
|
|
1512
|
+
return;
|
|
1513
|
+
removed = true;
|
|
1514
|
+
active = false;
|
|
1515
|
+
for (const cleanup of cleanups.reverse())
|
|
1516
|
+
cleanup();
|
|
1517
|
+
},
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
const ANSIGHT_CAPACITOR_SDK_VERSION = "1.3.0-preview.11";
|
|
1522
|
+
const COMPILED_CAPACITOR_CORE_VERSION = "8.4.2";
|
|
1523
|
+
const CAPACITOR_GROUP = "capacitor";
|
|
1524
|
+
const LOCALIZATION_GROUP = "localization";
|
|
1525
|
+
function normalized(value) {
|
|
1526
|
+
if (value == null)
|
|
1527
|
+
return undefined;
|
|
1528
|
+
const result = String(value).trim();
|
|
1529
|
+
return result || undefined;
|
|
1530
|
+
}
|
|
1531
|
+
function canonicalizeLocale(value) {
|
|
1532
|
+
const locale = normalized(value)?.replace(/_/g, "-");
|
|
1533
|
+
if (!locale)
|
|
1534
|
+
return undefined;
|
|
1535
|
+
try {
|
|
1536
|
+
return Intl.getCanonicalLocales(locale)[0] ?? locale;
|
|
1537
|
+
}
|
|
1538
|
+
catch {
|
|
1539
|
+
return locale;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function parseLocale(locale) {
|
|
1543
|
+
const parts = (locale ?? "").split("-").filter(Boolean);
|
|
1544
|
+
const language = parts[0]?.toLowerCase();
|
|
1545
|
+
const region = parts.find((part, index) => index > 0 && (/^[A-Za-z]{2}$/.test(part) || /^\d{3}$/.test(part)));
|
|
1546
|
+
return { language, region: region?.toUpperCase() };
|
|
1547
|
+
}
|
|
1548
|
+
function webViewDetails(platform, nativePlatform, userAgent) {
|
|
1549
|
+
const agent = userAgent ?? "";
|
|
1550
|
+
if (nativePlatform && platform === "ios") {
|
|
1551
|
+
return {
|
|
1552
|
+
engine: "wkWebView",
|
|
1553
|
+
version: /AppleWebKit\/([^\s]+)/.exec(agent)?.[1],
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
if (nativePlatform && platform === "android") {
|
|
1557
|
+
return {
|
|
1558
|
+
engine: "chromiumWebView",
|
|
1559
|
+
version: /(?:Chrome|Chromium)\/([^\s]+)/.exec(agent)?.[1],
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
if (/Firefox\/([^\s]+)/.test(agent)) {
|
|
1563
|
+
return { engine: "gecko", version: /Firefox\/([^\s]+)/.exec(agent)?.[1] };
|
|
1564
|
+
}
|
|
1565
|
+
if (/(?:Chrome|Chromium)\/([^\s]+)/.test(agent)) {
|
|
1566
|
+
return {
|
|
1567
|
+
engine: "chromium",
|
|
1568
|
+
version: /(?:Chrome|Chromium)\/([^\s]+)/.exec(agent)?.[1],
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
if (/AppleWebKit\/([^\s]+)/.test(agent)) {
|
|
1572
|
+
return {
|
|
1573
|
+
engine: "webkit",
|
|
1574
|
+
version: /AppleWebKit\/([^\s]+)/.exec(agent)?.[1],
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
return { engine: "unknown" };
|
|
1578
|
+
}
|
|
1579
|
+
function currentCapacitorSessionEnvironment(platform, nativePlatform) {
|
|
1580
|
+
let resolved;
|
|
1581
|
+
try {
|
|
1582
|
+
resolved = Intl.DateTimeFormat().resolvedOptions();
|
|
1583
|
+
}
|
|
1584
|
+
catch {
|
|
1585
|
+
resolved = undefined;
|
|
1586
|
+
}
|
|
1587
|
+
return {
|
|
1588
|
+
platform,
|
|
1589
|
+
nativePlatform,
|
|
1590
|
+
userAgent: typeof navigator === "undefined" ? undefined : navigator.userAgent,
|
|
1591
|
+
locale: resolved?.locale ??
|
|
1592
|
+
(typeof navigator === "undefined" ? undefined : navigator.language),
|
|
1593
|
+
timeZone: resolved?.timeZone,
|
|
1594
|
+
utcOffsetMinutes: -new Date().getTimezoneOffset(),
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
function createAutomaticSessionProperties(environment) {
|
|
1598
|
+
const platform = normalized(environment.platform) ?? "unknown";
|
|
1599
|
+
const userAgent = normalized(environment.userAgent);
|
|
1600
|
+
const webView = webViewDetails(platform, environment.nativePlatform, userAgent);
|
|
1601
|
+
const locale = canonicalizeLocale(environment.locale);
|
|
1602
|
+
const parsedLocale = parseLocale(locale);
|
|
1603
|
+
const capacitor = {
|
|
1604
|
+
sdkVersion: ANSIGHT_CAPACITOR_SDK_VERSION,
|
|
1605
|
+
capacitorVersion: "8.x",
|
|
1606
|
+
compiledCapacitorVersion: COMPILED_CAPACITOR_CORE_VERSION,
|
|
1607
|
+
platform,
|
|
1608
|
+
runtimeLanguage: "javascript",
|
|
1609
|
+
executionMode: environment.nativePlatform ? "native" : "web",
|
|
1610
|
+
webViewEngine: webView.engine,
|
|
1611
|
+
};
|
|
1612
|
+
if (webView.version)
|
|
1613
|
+
capacitor.webViewEngineVersion = webView.version;
|
|
1614
|
+
if (userAgent)
|
|
1615
|
+
capacitor.userAgent = userAgent;
|
|
1616
|
+
const localization = {
|
|
1617
|
+
utcOffsetMinutes: String(environment.utcOffsetMinutes ?? -new Date().getTimezoneOffset()),
|
|
1618
|
+
};
|
|
1619
|
+
if (locale)
|
|
1620
|
+
localization.locale = locale;
|
|
1621
|
+
if (parsedLocale.language)
|
|
1622
|
+
localization.language = parsedLocale.language;
|
|
1623
|
+
if (parsedLocale.region)
|
|
1624
|
+
localization.region = parsedLocale.region;
|
|
1625
|
+
if (normalized(environment.timeZone)) {
|
|
1626
|
+
localization.timeZone = String(environment.timeZone).trim();
|
|
1627
|
+
}
|
|
1628
|
+
return {
|
|
1629
|
+
[CAPACITOR_GROUP]: capacitor,
|
|
1630
|
+
[LOCALIZATION_GROUP]: localization,
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
function mergeSessionProperties(automaticProperties, customProperties) {
|
|
1634
|
+
const merged = Object.fromEntries(Object.entries(automaticProperties).map(([group, properties]) => [
|
|
1635
|
+
group,
|
|
1636
|
+
{ ...properties },
|
|
1637
|
+
]));
|
|
1638
|
+
for (const [group, properties] of Object.entries(customProperties ?? {})) {
|
|
1639
|
+
merged[group] = { ...(merged[group] ?? {}), ...properties };
|
|
1640
|
+
}
|
|
1641
|
+
return merged;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
624
1644
|
const AnsightNative = core.registerPlugin("Ansight");
|
|
625
1645
|
const toolHandlers = new Map();
|
|
626
1646
|
const artifactProviders = new Map();
|
|
@@ -631,6 +1651,9 @@ let logListener;
|
|
|
631
1651
|
let lifecycleCleanup;
|
|
632
1652
|
let artifactToolRegistrations = [];
|
|
633
1653
|
let domToolRegistration;
|
|
1654
|
+
let networkCaptureSubscription;
|
|
1655
|
+
let networkCaptureRegistration;
|
|
1656
|
+
let networkConnectionListener;
|
|
634
1657
|
function normalizePairingPayload(payload) {
|
|
635
1658
|
if (payload == null)
|
|
636
1659
|
return payload;
|
|
@@ -638,11 +1661,19 @@ function normalizePairingPayload(payload) {
|
|
|
638
1661
|
}
|
|
639
1662
|
function normalizeOptions(input) {
|
|
640
1663
|
const options = JSON.parse(JSON.stringify(input));
|
|
1664
|
+
options.customProperties = mergeSessionProperties(automaticSessionProperties(), options.customProperties);
|
|
641
1665
|
delete options.domTools;
|
|
642
1666
|
delete options.errorCapture;
|
|
643
1667
|
delete options.lifecycle;
|
|
1668
|
+
delete options.networkCapture;
|
|
644
1669
|
return options;
|
|
645
1670
|
}
|
|
1671
|
+
function automaticSessionProperties() {
|
|
1672
|
+
return createAutomaticSessionProperties(currentCapacitorSessionEnvironment(core.Capacitor.getPlatform(), core.Capacitor.isNativePlatform()));
|
|
1673
|
+
}
|
|
1674
|
+
function automaticSessionPropertyValue(group, key) {
|
|
1675
|
+
return automaticSessionProperties()[group]?.[key];
|
|
1676
|
+
}
|
|
646
1677
|
function normalizeToolResult(value) {
|
|
647
1678
|
if (value && typeof value === "object" && "success" in value) {
|
|
648
1679
|
return value;
|
|
@@ -692,11 +1723,13 @@ async function emitHostConnectionStatus() {
|
|
|
692
1723
|
}
|
|
693
1724
|
async function afterConnectionChange(operation) {
|
|
694
1725
|
const result = await operation();
|
|
1726
|
+
await refreshNetworkCaptureConnection();
|
|
695
1727
|
await emitHostConnectionStatus();
|
|
696
1728
|
return result;
|
|
697
1729
|
}
|
|
698
1730
|
async function initialize(options = {}) {
|
|
699
1731
|
const result = await AnsightNative.initialize(normalizeOptions(options));
|
|
1732
|
+
await configureNetworkCapture(options.networkCapture);
|
|
700
1733
|
if (options.lifecycle !== false)
|
|
701
1734
|
startLifecycleTracking();
|
|
702
1735
|
if (options.errorCapture) {
|
|
@@ -710,6 +1743,7 @@ async function initialize(options = {}) {
|
|
|
710
1743
|
}
|
|
711
1744
|
async function initializeAndActivate(options = {}) {
|
|
712
1745
|
const result = await AnsightNative.initializeAndActivate(normalizeOptions(options));
|
|
1746
|
+
await configureNetworkCapture(options.networkCapture);
|
|
713
1747
|
if (options.lifecycle !== false)
|
|
714
1748
|
startLifecycleTracking();
|
|
715
1749
|
if (options.errorCapture) {
|
|
@@ -734,6 +1768,81 @@ function event(input) {
|
|
|
734
1768
|
return AnsightNative.recordEvent(typeof input === "string" ? { label: input } : input);
|
|
735
1769
|
}
|
|
736
1770
|
const recordEvent = event;
|
|
1771
|
+
async function recordNetworkRequest(input, sanitizationOptions = {}) {
|
|
1772
|
+
const request = sanitizeNetworkRequest(input, sanitizationOptions);
|
|
1773
|
+
if (!request) {
|
|
1774
|
+
return {
|
|
1775
|
+
success: false,
|
|
1776
|
+
message: "Network request capture was suppressed by the sanitizer.",
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
return AnsightNative.recordNetworkRequest(request);
|
|
1780
|
+
}
|
|
1781
|
+
function installNetworkCapture(options = {}) {
|
|
1782
|
+
uninstallNetworkCapture();
|
|
1783
|
+
const registration = { options };
|
|
1784
|
+
networkCaptureRegistration = registration;
|
|
1785
|
+
ensureNetworkConnectionListener();
|
|
1786
|
+
void refreshNetworkCaptureConnection();
|
|
1787
|
+
return {
|
|
1788
|
+
remove() {
|
|
1789
|
+
if (networkCaptureRegistration === registration) {
|
|
1790
|
+
uninstallNetworkCapture();
|
|
1791
|
+
}
|
|
1792
|
+
},
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
function uninstallNetworkCapture() {
|
|
1796
|
+
networkCaptureRegistration = undefined;
|
|
1797
|
+
const listener = networkConnectionListener;
|
|
1798
|
+
networkConnectionListener = undefined;
|
|
1799
|
+
if (listener)
|
|
1800
|
+
void listener.then((value) => value.remove());
|
|
1801
|
+
detachNetworkCapture();
|
|
1802
|
+
}
|
|
1803
|
+
function detachNetworkCapture() {
|
|
1804
|
+
networkCaptureSubscription?.remove();
|
|
1805
|
+
networkCaptureSubscription = undefined;
|
|
1806
|
+
}
|
|
1807
|
+
async function refreshNetworkCaptureConnection() {
|
|
1808
|
+
const registration = networkCaptureRegistration;
|
|
1809
|
+
if (!registration) {
|
|
1810
|
+
detachNetworkCapture();
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
try {
|
|
1814
|
+
const status = await AnsightNative.hostConnectionStatus();
|
|
1815
|
+
if (networkCaptureRegistration !== registration)
|
|
1816
|
+
return;
|
|
1817
|
+
applyNetworkConnectionStatus(status);
|
|
1818
|
+
}
|
|
1819
|
+
catch {
|
|
1820
|
+
if (networkCaptureRegistration === registration)
|
|
1821
|
+
detachNetworkCapture();
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
function ensureNetworkConnectionListener() {
|
|
1825
|
+
networkConnectionListener ??= AnsightNative.addListener("ansightHostConnectionStatus", applyNetworkConnectionStatus);
|
|
1826
|
+
}
|
|
1827
|
+
function applyNetworkConnectionStatus(status) {
|
|
1828
|
+
const registration = networkCaptureRegistration;
|
|
1829
|
+
if (!registration || status.isConnected !== true) {
|
|
1830
|
+
detachNetworkCapture();
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
networkCaptureSubscription ??= installBrowserNetworkCapture((request) => AnsightNative.recordNetworkRequest(request), registration.options);
|
|
1834
|
+
}
|
|
1835
|
+
async function configureNetworkCapture(value) {
|
|
1836
|
+
uninstallNetworkCapture();
|
|
1837
|
+
if (!value)
|
|
1838
|
+
return;
|
|
1839
|
+
networkCaptureRegistration = {
|
|
1840
|
+
options: typeof value === "object" ? value : {},
|
|
1841
|
+
};
|
|
1842
|
+
ensureNetworkConnectionListener();
|
|
1843
|
+
await refreshNetworkCaptureConnection();
|
|
1844
|
+
}
|
|
1845
|
+
const recordCrashCandidate = (input) => AnsightNative.recordCrashCandidate(input);
|
|
737
1846
|
const screenViewed = (name, details = {}) => AnsightNative.screenViewed({ name, details });
|
|
738
1847
|
const trackRoute = screenViewed;
|
|
739
1848
|
const setAppLifecycleState = (state) => AnsightNative.setAppLifecycleState({ state });
|
|
@@ -788,12 +1897,25 @@ const disableFramesPerSecond = () => AnsightNative.disableFramesPerSecond();
|
|
|
788
1897
|
const captureScreenFrame = (options = {}) => AnsightNative.captureScreenFrame(options);
|
|
789
1898
|
const enableTouchCapture = () => AnsightNative.enableTouchCapture();
|
|
790
1899
|
const disableTouchCapture = () => AnsightNative.disableTouchCapture();
|
|
791
|
-
const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({
|
|
1900
|
+
const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({
|
|
1901
|
+
properties: mergeSessionProperties(automaticSessionProperties(), properties),
|
|
1902
|
+
});
|
|
792
1903
|
const updateCustomProperties = updateSessionProperties;
|
|
793
|
-
const clearSessionProperties = () => AnsightNative.
|
|
1904
|
+
const clearSessionProperties = () => AnsightNative.updateSessionProperties({
|
|
1905
|
+
properties: automaticSessionProperties(),
|
|
1906
|
+
});
|
|
794
1907
|
const clearCustomProperties = clearSessionProperties;
|
|
795
1908
|
const registerCustomProperty = (group, key, value) => AnsightNative.registerCustomProperty({ group, key, value });
|
|
796
|
-
const removeCustomProperty = (group, key) =>
|
|
1909
|
+
const removeCustomProperty = (group, key) => {
|
|
1910
|
+
const automaticValue = automaticSessionPropertyValue(group, key);
|
|
1911
|
+
return automaticValue == null
|
|
1912
|
+
? AnsightNative.removeCustomProperty({ group, key })
|
|
1913
|
+
: AnsightNative.registerCustomProperty({
|
|
1914
|
+
group,
|
|
1915
|
+
key,
|
|
1916
|
+
value: automaticValue,
|
|
1917
|
+
});
|
|
1918
|
+
};
|
|
797
1919
|
function addHostConnectionStatusListener(listener, options = {}) {
|
|
798
1920
|
hostConnectionListeners.add(listener);
|
|
799
1921
|
if (options.emitCurrent !== false)
|
|
@@ -1039,6 +2161,18 @@ function installErrorHandlers(options = {}) {
|
|
|
1039
2161
|
const onError = (eventValue) => {
|
|
1040
2162
|
if (!captureErrors)
|
|
1041
2163
|
return;
|
|
2164
|
+
void recordCrashCandidate({
|
|
2165
|
+
runtime: "capacitor-javascript",
|
|
2166
|
+
kind: "unhandled_javascript_error",
|
|
2167
|
+
message: eventValue.message,
|
|
2168
|
+
stack: eventValue.error instanceof Error ? eventValue.error.stack : undefined,
|
|
2169
|
+
fatal: false,
|
|
2170
|
+
metadata: JSON.stringify({
|
|
2171
|
+
filename: eventValue.filename,
|
|
2172
|
+
line: eventValue.lineno,
|
|
2173
|
+
column: eventValue.colno,
|
|
2174
|
+
}),
|
|
2175
|
+
}).catch(() => undefined);
|
|
1042
2176
|
void event({
|
|
1043
2177
|
label: eventValue.message || "Unhandled JavaScript error",
|
|
1044
2178
|
type: "Exception",
|
|
@@ -1056,6 +2190,13 @@ function installErrorHandlers(options = {}) {
|
|
|
1056
2190
|
if (!captureRejections)
|
|
1057
2191
|
return;
|
|
1058
2192
|
const reason = eventValue.reason;
|
|
2193
|
+
void recordCrashCandidate({
|
|
2194
|
+
runtime: "capacitor-javascript",
|
|
2195
|
+
kind: "unhandled_promise_rejection",
|
|
2196
|
+
message: reason instanceof Error ? reason.message : String(reason),
|
|
2197
|
+
stack: reason instanceof Error ? reason.stack : undefined,
|
|
2198
|
+
fatal: false,
|
|
2199
|
+
}).catch(() => undefined);
|
|
1059
2200
|
void event({
|
|
1060
2201
|
label: reason instanceof Error
|
|
1061
2202
|
? reason.message
|
|
@@ -1125,6 +2266,10 @@ const Ansight = {
|
|
|
1125
2266
|
recordMetric,
|
|
1126
2267
|
event,
|
|
1127
2268
|
recordEvent,
|
|
2269
|
+
recordNetworkRequest,
|
|
2270
|
+
installNetworkCapture,
|
|
2271
|
+
uninstallNetworkCapture,
|
|
2272
|
+
sanitizeNetworkRequest,
|
|
1128
2273
|
screenViewed,
|
|
1129
2274
|
trackRoute,
|
|
1130
2275
|
setAppLifecycleState,
|
|
@@ -1222,14 +2367,17 @@ exports.initialize = initialize;
|
|
|
1222
2367
|
exports.initializeAndActivate = initializeAndActivate;
|
|
1223
2368
|
exports.installDomTools = installDomTools;
|
|
1224
2369
|
exports.installErrorHandlers = installErrorHandlers;
|
|
2370
|
+
exports.installNetworkCapture = installNetworkCapture;
|
|
1225
2371
|
exports.isFramesPerSecondEnabled = isFramesPerSecondEnabled;
|
|
1226
2372
|
exports.listRegisteredArtifactProviders = listRegisteredArtifactProviders;
|
|
1227
2373
|
exports.listRegisteredTools = listRegisteredTools;
|
|
1228
2374
|
exports.metric = metric;
|
|
1229
2375
|
exports.notifyHostConnectionConfigChanged = notifyHostConnectionConfigChanged;
|
|
1230
2376
|
exports.openSession = openSession;
|
|
2377
|
+
exports.recordCrashCandidate = recordCrashCandidate;
|
|
1231
2378
|
exports.recordEvent = recordEvent;
|
|
1232
2379
|
exports.recordMetric = recordMetric;
|
|
2380
|
+
exports.recordNetworkRequest = recordNetworkRequest;
|
|
1233
2381
|
exports.recordedEvents = recordedEvents;
|
|
1234
2382
|
exports.recordedMetrics = recordedMetrics;
|
|
1235
2383
|
exports.registerArtifactProvider = registerArtifactProvider;
|
|
@@ -1238,6 +2386,7 @@ exports.registerCustomProperty = registerCustomProperty;
|
|
|
1238
2386
|
exports.registerMetricChannel = registerMetricChannel;
|
|
1239
2387
|
exports.registerTool = registerTool;
|
|
1240
2388
|
exports.removeCustomProperty = removeCustomProperty;
|
|
2389
|
+
exports.sanitizeNetworkRequest = sanitizeNetworkRequest;
|
|
1241
2390
|
exports.savePairingConfig = savePairingConfig;
|
|
1242
2391
|
exports.scanPairingQrCode = scanPairingQrCode;
|
|
1243
2392
|
exports.screenViewed = screenViewed;
|
|
@@ -1251,6 +2400,7 @@ exports.stopAppStateTracking = stopAppStateTracking;
|
|
|
1251
2400
|
exports.stopLifecycleTracking = stopLifecycleTracking;
|
|
1252
2401
|
exports.trackRoute = trackRoute;
|
|
1253
2402
|
exports.uninstallDomTools = uninstallDomTools;
|
|
2403
|
+
exports.uninstallNetworkCapture = uninstallNetworkCapture;
|
|
1254
2404
|
exports.unregisterArtifactProvider = unregisterArtifactProvider;
|
|
1255
2405
|
exports.unregisterTool = unregisterTool;
|
|
1256
2406
|
exports.updateCustomProperties = updateCustomProperties;
|