@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/standalone.js
CHANGED
|
@@ -642,6 +642,18 @@
|
|
|
642
642
|
web: () => new SystemBarsPluginWeb(),
|
|
643
643
|
});
|
|
644
644
|
|
|
645
|
+
function createTypeRegistry() {
|
|
646
|
+
return { idsByTypeName: new Map(), types: [] };
|
|
647
|
+
}
|
|
648
|
+
function registerType(registry, typeName) {
|
|
649
|
+
const existingTypeId = registry.idsByTypeName.get(typeName);
|
|
650
|
+
if (existingTypeId !== undefined)
|
|
651
|
+
return existingTypeId;
|
|
652
|
+
const typeId = registry.types.length;
|
|
653
|
+
registry.types.push(typeName);
|
|
654
|
+
registry.idsByTypeName.set(typeName, typeId);
|
|
655
|
+
return typeId;
|
|
656
|
+
}
|
|
645
657
|
const nodeIds = new WeakMap();
|
|
646
658
|
let nextNodeId = 1;
|
|
647
659
|
function nodeId(element) {
|
|
@@ -688,6 +700,55 @@
|
|
|
688
700
|
element.id ??
|
|
689
701
|
undefined)?.trim() || undefined);
|
|
690
702
|
}
|
|
703
|
+
function semanticRole(element) {
|
|
704
|
+
const declared = element.getAttribute("role")?.trim().toLowerCase();
|
|
705
|
+
if (declared)
|
|
706
|
+
return declared;
|
|
707
|
+
if (element instanceof HTMLButtonElement)
|
|
708
|
+
return "button";
|
|
709
|
+
if (element instanceof HTMLInputElement) {
|
|
710
|
+
if (element.type === "checkbox")
|
|
711
|
+
return "checkbox";
|
|
712
|
+
if (element.type === "radio")
|
|
713
|
+
return "radio";
|
|
714
|
+
if (element.type === "range")
|
|
715
|
+
return "slider";
|
|
716
|
+
if (["button", "submit", "reset"].includes(element.type))
|
|
717
|
+
return "button";
|
|
718
|
+
return "textbox";
|
|
719
|
+
}
|
|
720
|
+
if (element instanceof HTMLTextAreaElement)
|
|
721
|
+
return "textbox";
|
|
722
|
+
if (element instanceof HTMLSelectElement)
|
|
723
|
+
return "combobox";
|
|
724
|
+
if (element.tagName === "A")
|
|
725
|
+
return "link";
|
|
726
|
+
if (/^H[1-6]$/.test(element.tagName))
|
|
727
|
+
return "heading";
|
|
728
|
+
return "view";
|
|
729
|
+
}
|
|
730
|
+
function supportedActions(element, allowActions) {
|
|
731
|
+
if (!allowActions)
|
|
732
|
+
return [];
|
|
733
|
+
const actions = [];
|
|
734
|
+
if (["A", "BUTTON", "SUMMARY"].includes(element.tagName) ||
|
|
735
|
+
element instanceof HTMLInputElement) {
|
|
736
|
+
actions.push("tap");
|
|
737
|
+
}
|
|
738
|
+
if (element instanceof HTMLInputElement ||
|
|
739
|
+
element instanceof HTMLTextAreaElement ||
|
|
740
|
+
element instanceof HTMLSelectElement) {
|
|
741
|
+
actions.push("typeText", "focus");
|
|
742
|
+
}
|
|
743
|
+
else if (element.tabIndex >= 0) {
|
|
744
|
+
actions.push("focus");
|
|
745
|
+
}
|
|
746
|
+
if (element.scrollHeight >
|
|
747
|
+
element.clientHeight) {
|
|
748
|
+
actions.push("scroll", "swipe");
|
|
749
|
+
}
|
|
750
|
+
return [...new Set(actions)];
|
|
751
|
+
}
|
|
691
752
|
function computedColorToArgbHex(value) {
|
|
692
753
|
const normalized = value.trim().toLowerCase();
|
|
693
754
|
if (!normalized)
|
|
@@ -753,7 +814,7 @@
|
|
|
753
814
|
const normalized = value?.replace(/\s+/g, " ").trim();
|
|
754
815
|
return normalized ? normalized.slice(0, 240) : undefined;
|
|
755
816
|
}
|
|
756
|
-
function captureNode(element, options, depth, limits) {
|
|
817
|
+
function captureNode(element, options, depth, limits, typeRegistry) {
|
|
757
818
|
if (limits.count >= limits.maxNodes || depth > limits.maxDepth)
|
|
758
819
|
return null;
|
|
759
820
|
const style = getComputedStyle(element);
|
|
@@ -763,17 +824,23 @@
|
|
|
763
824
|
return null;
|
|
764
825
|
limits.count += 1;
|
|
765
826
|
const children = Array.from(element.children)
|
|
766
|
-
.map((child) => captureNode(child, options, depth + 1, limits))
|
|
827
|
+
.map((child) => captureNode(child, options, depth + 1, limits, typeRegistry))
|
|
767
828
|
.filter((child) => child !== null);
|
|
768
829
|
const htmlElement = element;
|
|
769
830
|
const disabled = element.hasAttribute("disabled") ||
|
|
770
831
|
element.getAttribute("aria-disabled") === "true";
|
|
771
832
|
const parsedOpacity = Number.parseFloat(style.opacity);
|
|
772
|
-
|
|
833
|
+
const parsedZIndex = Number.parseFloat(style.zIndex);
|
|
834
|
+
const actions = supportedActions(element, options.allowActions);
|
|
835
|
+
const label = accessibleLabel(element, options.includeText);
|
|
836
|
+
const node = {
|
|
773
837
|
id: nodeId(element),
|
|
774
|
-
|
|
838
|
+
typeId: registerType(typeRegistry, element.tagName.toLowerCase()),
|
|
775
839
|
automationId: automationId(element),
|
|
776
|
-
label
|
|
840
|
+
label,
|
|
841
|
+
role: semanticRole(element),
|
|
842
|
+
supportedActions: actions,
|
|
843
|
+
interactable: visible && !disabled && actions.length > 0,
|
|
777
844
|
visible,
|
|
778
845
|
enabled: !disabled,
|
|
779
846
|
focusable: htmlElement.tabIndex >= 0 ||
|
|
@@ -798,12 +865,15 @@
|
|
|
798
865
|
id: element.id || undefined,
|
|
799
866
|
role: element.getAttribute("role") ?? undefined,
|
|
800
867
|
className: element.getAttribute("class") ?? undefined,
|
|
801
|
-
value: options.includeText ? displayedValue(element) : undefined,
|
|
802
868
|
checked: element instanceof HTMLInputElement ? element.checked : undefined,
|
|
803
869
|
attributes: options.includeAttributes ? attributes(element) : undefined,
|
|
804
870
|
},
|
|
805
871
|
children,
|
|
806
872
|
};
|
|
873
|
+
if (Number.isFinite(parsedZIndex) && parsedZIndex !== 0) {
|
|
874
|
+
node.z = parsedZIndex;
|
|
875
|
+
}
|
|
876
|
+
return node;
|
|
807
877
|
}
|
|
808
878
|
function findElement(id) {
|
|
809
879
|
return Array.from(document.querySelectorAll("*")).find((element) => nodeId(element) === id);
|
|
@@ -836,12 +906,15 @@
|
|
|
836
906
|
maxNodes: options.maxNodes,
|
|
837
907
|
count: 0,
|
|
838
908
|
};
|
|
839
|
-
const
|
|
909
|
+
const typeRegistry = createTypeRegistry();
|
|
910
|
+
const tree = captureNode(root, options, 0, limits, typeRegistry);
|
|
840
911
|
return successful({
|
|
912
|
+
format: "ansight.dom.visual-tree.compact.v2",
|
|
841
913
|
platform: "web",
|
|
842
914
|
source: options.source,
|
|
843
915
|
adapter: "@ansight/capacitor",
|
|
844
916
|
capturedAtUtc: new Date().toISOString(),
|
|
917
|
+
types: typeRegistry.types,
|
|
845
918
|
truncated: limits.count >= limits.maxNodes,
|
|
846
919
|
root: tree,
|
|
847
920
|
}, "DOM document captured.");
|
|
@@ -867,7 +940,17 @@
|
|
|
867
940
|
};
|
|
868
941
|
}
|
|
869
942
|
const limits = { maxDepth: 0, maxNodes: 1, count: 0 };
|
|
870
|
-
|
|
943
|
+
const typeRegistry = createTypeRegistry();
|
|
944
|
+
const node = captureNode(element, options, 0, limits, typeRegistry);
|
|
945
|
+
return successful({
|
|
946
|
+
format: "ansight.dom.visual-tree.compact.v2",
|
|
947
|
+
platform: "web",
|
|
948
|
+
source: options.source,
|
|
949
|
+
adapter: "@ansight/capacitor",
|
|
950
|
+
capturedAtUtc: new Date().toISOString(),
|
|
951
|
+
types: typeRegistry.types,
|
|
952
|
+
node,
|
|
953
|
+
}, "DOM node captured.");
|
|
871
954
|
}),
|
|
872
955
|
registerTool({
|
|
873
956
|
id: "dom.query_selector",
|
|
@@ -974,6 +1057,8 @@
|
|
|
974
1057
|
retentionPeriodSeconds: 120,
|
|
975
1058
|
enableFramesPerSecond: true,
|
|
976
1059
|
enableBatteryLevel: false,
|
|
1060
|
+
enableOpenFileHandleTracking: false,
|
|
1061
|
+
enableJniReferenceCountTracking: false,
|
|
977
1062
|
sessionJpegCapture: {
|
|
978
1063
|
intervalMilliseconds: 2000,
|
|
979
1064
|
quality: 60,
|
|
@@ -1016,6 +1101,22 @@
|
|
|
1016
1101
|
this.options.enableBatteryLevel = false;
|
|
1017
1102
|
return this;
|
|
1018
1103
|
}
|
|
1104
|
+
withOpenFileHandleTracking() {
|
|
1105
|
+
this.options.enableOpenFileHandleTracking = true;
|
|
1106
|
+
return this;
|
|
1107
|
+
}
|
|
1108
|
+
withoutOpenFileHandleTracking() {
|
|
1109
|
+
this.options.enableOpenFileHandleTracking = false;
|
|
1110
|
+
return this;
|
|
1111
|
+
}
|
|
1112
|
+
withJniReferenceCountTracking() {
|
|
1113
|
+
this.options.enableJniReferenceCountTracking = true;
|
|
1114
|
+
return this;
|
|
1115
|
+
}
|
|
1116
|
+
withoutJniReferenceCountTracking() {
|
|
1117
|
+
this.options.enableJniReferenceCountTracking = false;
|
|
1118
|
+
return this;
|
|
1119
|
+
}
|
|
1019
1120
|
withRetentionPeriodSeconds(value) {
|
|
1020
1121
|
this.options.retentionPeriodSeconds = value;
|
|
1021
1122
|
return this;
|
|
@@ -1054,7 +1155,7 @@
|
|
|
1054
1155
|
this.options.defaultMemoryChannels = current;
|
|
1055
1156
|
return this;
|
|
1056
1157
|
}
|
|
1057
|
-
withSessionJpegCapture(optionsOrIntervalMilliseconds = {}, quality = 60, maxWidth = 480, captureGpuBackedSurfaces = true, mode = "screenshotOnly") {
|
|
1158
|
+
withSessionJpegCapture(optionsOrIntervalMilliseconds = {}, quality = 60, maxWidth = 480, captureGpuBackedSurfaces = true, mode = "screenshotOnly", captureKeyboardPresence = false) {
|
|
1058
1159
|
this.options.sessionJpegCapture =
|
|
1059
1160
|
typeof optionsOrIntervalMilliseconds === "number"
|
|
1060
1161
|
? {
|
|
@@ -1063,11 +1164,13 @@
|
|
|
1063
1164
|
maxWidth,
|
|
1064
1165
|
captureGpuBackedSurfaces,
|
|
1065
1166
|
mode,
|
|
1167
|
+
captureKeyboardPresence,
|
|
1066
1168
|
}
|
|
1067
1169
|
: {
|
|
1068
1170
|
intervalMilliseconds: 2000,
|
|
1069
1171
|
quality: 60,
|
|
1070
1172
|
maxWidth: 480,
|
|
1173
|
+
captureKeyboardPresence: false,
|
|
1071
1174
|
mode: "screenshotOnly",
|
|
1072
1175
|
...optionsOrIntervalMilliseconds,
|
|
1073
1176
|
};
|
|
@@ -1085,6 +1188,14 @@
|
|
|
1085
1188
|
this.options.touchCapture = false;
|
|
1086
1189
|
return this;
|
|
1087
1190
|
}
|
|
1191
|
+
withCrashCapture(options = {}) {
|
|
1192
|
+
this.options.crashCapture = { ...options, enabled: true };
|
|
1193
|
+
return this;
|
|
1194
|
+
}
|
|
1195
|
+
withoutCrashCapture() {
|
|
1196
|
+
this.options.crashCapture = false;
|
|
1197
|
+
return this;
|
|
1198
|
+
}
|
|
1088
1199
|
withLifecycleCapture(options = {}) {
|
|
1089
1200
|
this.options.lifecycleCapture = {
|
|
1090
1201
|
...options,
|
|
@@ -1092,6 +1203,50 @@
|
|
|
1092
1203
|
};
|
|
1093
1204
|
return this;
|
|
1094
1205
|
}
|
|
1206
|
+
withNetworkCapture(options = {}) {
|
|
1207
|
+
this.options.networkCapture = { ...options };
|
|
1208
|
+
return this;
|
|
1209
|
+
}
|
|
1210
|
+
withNetworkRequestBodies(maximumBodyBytes) {
|
|
1211
|
+
if (typeof this.options.networkCapture !== "object")
|
|
1212
|
+
return this;
|
|
1213
|
+
const current = this.options.networkCapture;
|
|
1214
|
+
this.options.networkCapture = {
|
|
1215
|
+
...current,
|
|
1216
|
+
captureRequestBody: true,
|
|
1217
|
+
...(maximumBodyBytes == null ? {} : { maximumBodyBytes }),
|
|
1218
|
+
};
|
|
1219
|
+
return this;
|
|
1220
|
+
}
|
|
1221
|
+
withoutNetworkRequestBodies() {
|
|
1222
|
+
if (typeof this.options.networkCapture !== "object")
|
|
1223
|
+
return this;
|
|
1224
|
+
const current = this.options.networkCapture;
|
|
1225
|
+
this.options.networkCapture = { ...current, captureRequestBody: false };
|
|
1226
|
+
return this;
|
|
1227
|
+
}
|
|
1228
|
+
withNetworkResponseBodies(maximumBodyBytes) {
|
|
1229
|
+
if (typeof this.options.networkCapture !== "object")
|
|
1230
|
+
return this;
|
|
1231
|
+
const current = this.options.networkCapture;
|
|
1232
|
+
this.options.networkCapture = {
|
|
1233
|
+
...current,
|
|
1234
|
+
captureResponseBody: true,
|
|
1235
|
+
...(maximumBodyBytes == null ? {} : { maximumBodyBytes }),
|
|
1236
|
+
};
|
|
1237
|
+
return this;
|
|
1238
|
+
}
|
|
1239
|
+
withoutNetworkResponseBodies() {
|
|
1240
|
+
if (typeof this.options.networkCapture !== "object")
|
|
1241
|
+
return this;
|
|
1242
|
+
const current = this.options.networkCapture;
|
|
1243
|
+
this.options.networkCapture = { ...current, captureResponseBody: false };
|
|
1244
|
+
return this;
|
|
1245
|
+
}
|
|
1246
|
+
withoutNetworkCapture() {
|
|
1247
|
+
this.options.networkCapture = false;
|
|
1248
|
+
return this;
|
|
1249
|
+
}
|
|
1095
1250
|
withToolGuard(toolGuard) {
|
|
1096
1251
|
this.options.toolGuard = toolGuard;
|
|
1097
1252
|
return this;
|
|
@@ -1259,6 +1414,871 @@
|
|
|
1259
1414
|
return new AnsightOptionsBuilder(options);
|
|
1260
1415
|
}
|
|
1261
1416
|
|
|
1417
|
+
const networkRequestSchema = "ansight.network-request.v1";
|
|
1418
|
+
const redactedNetworkValue = "<redacted>";
|
|
1419
|
+
const maximumHeaderCount = 128;
|
|
1420
|
+
const maximumHeaderValueLength = 4096;
|
|
1421
|
+
const maximumErrorMessageLength = 4096;
|
|
1422
|
+
const maximumUrlLength = 16384;
|
|
1423
|
+
const defaultMaximumBodyBytes = 64 * 1024;
|
|
1424
|
+
const sensitiveHeaderNames = new Set([
|
|
1425
|
+
"authorization",
|
|
1426
|
+
"cookie",
|
|
1427
|
+
"proxy-authorization",
|
|
1428
|
+
"set-cookie",
|
|
1429
|
+
"x-api-key",
|
|
1430
|
+
"x-auth-token",
|
|
1431
|
+
]);
|
|
1432
|
+
const sensitiveQueryNames = new Set([
|
|
1433
|
+
"access_token",
|
|
1434
|
+
"accesskey",
|
|
1435
|
+
"access_key",
|
|
1436
|
+
"api_key",
|
|
1437
|
+
"apikey",
|
|
1438
|
+
"auth",
|
|
1439
|
+
"authorization",
|
|
1440
|
+
"client_secret",
|
|
1441
|
+
"code",
|
|
1442
|
+
"credential",
|
|
1443
|
+
"credentials",
|
|
1444
|
+
"id_token",
|
|
1445
|
+
"jwt",
|
|
1446
|
+
"key",
|
|
1447
|
+
"password",
|
|
1448
|
+
"passwd",
|
|
1449
|
+
"refresh_token",
|
|
1450
|
+
"sas",
|
|
1451
|
+
"sastoken",
|
|
1452
|
+
"secret",
|
|
1453
|
+
"secret_key",
|
|
1454
|
+
"security_token",
|
|
1455
|
+
"session_token",
|
|
1456
|
+
"sig",
|
|
1457
|
+
"signature",
|
|
1458
|
+
"token",
|
|
1459
|
+
]);
|
|
1460
|
+
const azureSasFingerprintNames = new Set([
|
|
1461
|
+
"se",
|
|
1462
|
+
"skoid",
|
|
1463
|
+
"sp",
|
|
1464
|
+
"sr",
|
|
1465
|
+
"srt",
|
|
1466
|
+
"ss",
|
|
1467
|
+
"sv",
|
|
1468
|
+
]);
|
|
1469
|
+
const azureSasQueryNames = new Set([
|
|
1470
|
+
"epk",
|
|
1471
|
+
"erk",
|
|
1472
|
+
"rscc",
|
|
1473
|
+
"rscd",
|
|
1474
|
+
"rsce",
|
|
1475
|
+
"rscl",
|
|
1476
|
+
"rsct",
|
|
1477
|
+
"saoid",
|
|
1478
|
+
"scid",
|
|
1479
|
+
"se",
|
|
1480
|
+
"sig",
|
|
1481
|
+
"si",
|
|
1482
|
+
"sip",
|
|
1483
|
+
"ske",
|
|
1484
|
+
"skoid",
|
|
1485
|
+
"sks",
|
|
1486
|
+
"skt",
|
|
1487
|
+
"sktid",
|
|
1488
|
+
"skv",
|
|
1489
|
+
"snapshot",
|
|
1490
|
+
"sp",
|
|
1491
|
+
"spk",
|
|
1492
|
+
"spr",
|
|
1493
|
+
"sr",
|
|
1494
|
+
"srk",
|
|
1495
|
+
"srt",
|
|
1496
|
+
"ss",
|
|
1497
|
+
"st",
|
|
1498
|
+
"suoid",
|
|
1499
|
+
"tn",
|
|
1500
|
+
"versionid",
|
|
1501
|
+
"sv",
|
|
1502
|
+
]);
|
|
1503
|
+
function truncate(value, maximumLength) {
|
|
1504
|
+
const text = String(value);
|
|
1505
|
+
return text.length <= maximumLength
|
|
1506
|
+
? text
|
|
1507
|
+
: `${text.slice(0, maximumLength)}…`;
|
|
1508
|
+
}
|
|
1509
|
+
function normalizeRequired(value, fallback, maximumLength) {
|
|
1510
|
+
const normalized = value == null ? "" : String(value).trim();
|
|
1511
|
+
return truncate(normalized || fallback, maximumLength);
|
|
1512
|
+
}
|
|
1513
|
+
function normalizeOptional(value, maximumLength) {
|
|
1514
|
+
if (value == null)
|
|
1515
|
+
return undefined;
|
|
1516
|
+
const normalized = String(value).trim();
|
|
1517
|
+
return normalized ? truncate(normalized, maximumLength) : undefined;
|
|
1518
|
+
}
|
|
1519
|
+
function lowercaseSet(values) {
|
|
1520
|
+
return new Set((values ?? []).map((value) => value.toLowerCase()));
|
|
1521
|
+
}
|
|
1522
|
+
function isSensitiveHeader(name, options) {
|
|
1523
|
+
const lowered = name.toLowerCase();
|
|
1524
|
+
if (sensitiveHeaderNames.has(lowered) ||
|
|
1525
|
+
lowercaseSet(options.additionalSensitiveHeaderNames).has(lowered)) {
|
|
1526
|
+
return true;
|
|
1527
|
+
}
|
|
1528
|
+
const compact = lowered.replaceAll("-", "");
|
|
1529
|
+
return (compact.includes("token") ||
|
|
1530
|
+
compact.includes("secret") ||
|
|
1531
|
+
compact.includes("apikey"));
|
|
1532
|
+
}
|
|
1533
|
+
function headerEntries(headers) {
|
|
1534
|
+
if (!headers)
|
|
1535
|
+
return [];
|
|
1536
|
+
if (Array.isArray(headers)) {
|
|
1537
|
+
return headers.flatMap((header) => {
|
|
1538
|
+
if (Array.isArray(header))
|
|
1539
|
+
return [[header[0], header[1]]];
|
|
1540
|
+
if (header && typeof header === "object") {
|
|
1541
|
+
const value = header;
|
|
1542
|
+
return [[value.name, value.value]];
|
|
1543
|
+
}
|
|
1544
|
+
return [];
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
if (typeof headers.forEach === "function") {
|
|
1548
|
+
const entries = [];
|
|
1549
|
+
headers.forEach((value, name) => entries.push([name, value]));
|
|
1550
|
+
return entries;
|
|
1551
|
+
}
|
|
1552
|
+
return typeof headers === "object" ? Object.entries(headers) : [];
|
|
1553
|
+
}
|
|
1554
|
+
function sanitizeHeaders(headers, options) {
|
|
1555
|
+
return headerEntries(headers)
|
|
1556
|
+
.filter(([name]) => name != null && String(name).trim())
|
|
1557
|
+
.slice(0, maximumHeaderCount)
|
|
1558
|
+
.map(([rawName, rawValue]) => {
|
|
1559
|
+
const name = normalizeRequired(rawName, "Header", 256);
|
|
1560
|
+
return {
|
|
1561
|
+
name,
|
|
1562
|
+
value: isSensitiveHeader(name, options)
|
|
1563
|
+
? redactedNetworkValue
|
|
1564
|
+
: normalizeRequired(rawValue, "", maximumHeaderValueLength),
|
|
1565
|
+
};
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
function sanitizeQuery(query, options) {
|
|
1569
|
+
const appSensitive = lowercaseSet(options.additionalSensitiveQueryParameterNames);
|
|
1570
|
+
const pairs = query.split("&");
|
|
1571
|
+
const decodedNames = new Set(pairs.map((pair) => decodeQueryName(pair).toLowerCase()));
|
|
1572
|
+
const hasAzureSas = decodedNames.has("sig") &&
|
|
1573
|
+
[...azureSasFingerprintNames].some((name) => decodedNames.has(name));
|
|
1574
|
+
const hasAwsSignature = decodedNames.has("x-amz-signature");
|
|
1575
|
+
const hasGoogleSignature = decodedNames.has("x-goog-signature");
|
|
1576
|
+
const hasCloudFrontSignature = decodedNames.has("signature") &&
|
|
1577
|
+
["key-pair-id", "policy", "expires"].some((name) => decodedNames.has(name));
|
|
1578
|
+
const hasLegacyGoogleSignature = decodedNames.has("signature") && decodedNames.has("googleaccessid");
|
|
1579
|
+
const hasAlibabaSignature = (decodedNames.has("signature") && decodedNames.has("ossaccesskeyid")) ||
|
|
1580
|
+
decodedNames.has("x-oss-signature");
|
|
1581
|
+
return pairs
|
|
1582
|
+
.map((pair) => {
|
|
1583
|
+
const equalsIndex = pair.indexOf("=");
|
|
1584
|
+
const encodedName = equalsIndex < 0 ? pair : pair.slice(0, equalsIndex);
|
|
1585
|
+
const decodedName = decodeQueryName(pair);
|
|
1586
|
+
const lowered = decodedName.toLowerCase();
|
|
1587
|
+
const providerSensitive = (hasAzureSas && azureSasQueryNames.has(lowered)) ||
|
|
1588
|
+
(hasAwsSignature && lowered.startsWith("x-amz-")) ||
|
|
1589
|
+
(hasGoogleSignature && lowered.startsWith("x-goog-")) ||
|
|
1590
|
+
(hasCloudFrontSignature &&
|
|
1591
|
+
[
|
|
1592
|
+
"signature",
|
|
1593
|
+
"key-pair-id",
|
|
1594
|
+
"policy",
|
|
1595
|
+
"expires",
|
|
1596
|
+
"hash-algorithm",
|
|
1597
|
+
].includes(lowered)) ||
|
|
1598
|
+
(hasLegacyGoogleSignature &&
|
|
1599
|
+
["signature", "googleaccessid", "expires"].includes(lowered)) ||
|
|
1600
|
+
(hasAlibabaSignature &&
|
|
1601
|
+
(lowered.startsWith("x-oss-") ||
|
|
1602
|
+
["signature", "ossaccesskeyid", "security-token"].includes(lowered)));
|
|
1603
|
+
return providerSensitive ||
|
|
1604
|
+
sensitiveQueryNames.has(lowered) ||
|
|
1605
|
+
appSensitive.has(lowered)
|
|
1606
|
+
? `${encodedName}=${encodeURIComponent(redactedNetworkValue)}`
|
|
1607
|
+
: pair;
|
|
1608
|
+
})
|
|
1609
|
+
.join("&");
|
|
1610
|
+
}
|
|
1611
|
+
function decodeQueryName(pair) {
|
|
1612
|
+
const equalsIndex = pair.indexOf("=");
|
|
1613
|
+
const encodedName = equalsIndex < 0 ? pair : pair.slice(0, equalsIndex);
|
|
1614
|
+
try {
|
|
1615
|
+
return decodeURIComponent(encodedName.replaceAll("+", " "));
|
|
1616
|
+
}
|
|
1617
|
+
catch {
|
|
1618
|
+
return encodedName;
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
function sanitizeUrl(value, options) {
|
|
1622
|
+
let normalized = normalizeRequired(value, "<unknown>", maximumUrlLength);
|
|
1623
|
+
normalized = normalized.replace(/^(https?:\/\/)[^/@]+@/i, `$1${redactedNetworkValue}@`);
|
|
1624
|
+
const queryIndex = normalized.indexOf("?");
|
|
1625
|
+
if (queryIndex < 0)
|
|
1626
|
+
return truncate(normalized, maximumUrlLength);
|
|
1627
|
+
const fragmentIndex = normalized.indexOf("#", queryIndex);
|
|
1628
|
+
if (options.includeQueryString === false) {
|
|
1629
|
+
return truncate(normalized.slice(0, queryIndex) +
|
|
1630
|
+
(fragmentIndex < 0 ? "" : normalized.slice(fragmentIndex)), maximumUrlLength);
|
|
1631
|
+
}
|
|
1632
|
+
const queryEnd = fragmentIndex < 0 ? normalized.length : fragmentIndex;
|
|
1633
|
+
return truncate(normalized.slice(0, queryIndex + 1) +
|
|
1634
|
+
sanitizeQuery(normalized.slice(queryIndex + 1, queryEnd), options) +
|
|
1635
|
+
(fragmentIndex < 0 ? "" : normalized.slice(fragmentIndex)), maximumUrlLength);
|
|
1636
|
+
}
|
|
1637
|
+
function sanitizeErrorMessage(value, options) {
|
|
1638
|
+
const normalized = normalizeOptional(value, maximumErrorMessageLength);
|
|
1639
|
+
if (!normalized)
|
|
1640
|
+
return undefined;
|
|
1641
|
+
return truncate(normalized
|
|
1642
|
+
.replace(/(access_token|api_key|apikey|auth|authorization|code|key|password|passwd|secret|signature|token)(\s*=\s*)([^&\s,;]+)/gi, `$1$2${redactedNetworkValue}`)
|
|
1643
|
+
.replace(/https?:\/\/[^\s"'<>]+/gi, (url) => sanitizeUrl(url, options)), maximumErrorMessageLength);
|
|
1644
|
+
}
|
|
1645
|
+
function normalizeTimestamp(value, fallback) {
|
|
1646
|
+
const date = new Date(value == null ? fallback : String(value));
|
|
1647
|
+
return Number.isFinite(date.valueOf()) ? date.toISOString() : fallback;
|
|
1648
|
+
}
|
|
1649
|
+
function generateId(globalObject) {
|
|
1650
|
+
if (typeof globalObject.crypto?.randomUUID === "function") {
|
|
1651
|
+
return globalObject.crypto.randomUUID().replaceAll("-", "");
|
|
1652
|
+
}
|
|
1653
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
1654
|
+
}
|
|
1655
|
+
function normalizeSize(value) {
|
|
1656
|
+
const number = Number(value);
|
|
1657
|
+
return Number.isFinite(number) && number >= 0
|
|
1658
|
+
? Math.round(number)
|
|
1659
|
+
: undefined;
|
|
1660
|
+
}
|
|
1661
|
+
function maximumBodyBytes(options) {
|
|
1662
|
+
const configured = Number(options.maximumBodyBytes);
|
|
1663
|
+
const value = Number.isFinite(configured)
|
|
1664
|
+
? Math.round(configured)
|
|
1665
|
+
: defaultMaximumBodyBytes;
|
|
1666
|
+
return Math.max(0, value);
|
|
1667
|
+
}
|
|
1668
|
+
function sanitizeSensitiveText(value, options) {
|
|
1669
|
+
return value
|
|
1670
|
+
.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}`)
|
|
1671
|
+
.replace(/https?:\/\/[^\s"'<>]+/gi, (url) => sanitizeUrl(url, options));
|
|
1672
|
+
}
|
|
1673
|
+
function truncateUtf8(bytes, maximum) {
|
|
1674
|
+
let length = Math.min(bytes.length, maximum);
|
|
1675
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1676
|
+
while (length > 0) {
|
|
1677
|
+
try {
|
|
1678
|
+
decoder.decode(bytes.slice(0, length));
|
|
1679
|
+
return bytes.slice(0, length);
|
|
1680
|
+
}
|
|
1681
|
+
catch {
|
|
1682
|
+
length -= 1;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return new Uint8Array();
|
|
1686
|
+
}
|
|
1687
|
+
function bytesToBase64$1(bytes) {
|
|
1688
|
+
let binary = "";
|
|
1689
|
+
for (const byte of bytes)
|
|
1690
|
+
binary += String.fromCharCode(byte);
|
|
1691
|
+
return btoa(binary);
|
|
1692
|
+
}
|
|
1693
|
+
function base64ToBytes(value) {
|
|
1694
|
+
const binary = atob(value);
|
|
1695
|
+
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
1696
|
+
}
|
|
1697
|
+
function normalizeBody(body, options) {
|
|
1698
|
+
const maximum = maximumBodyBytes(options);
|
|
1699
|
+
if (!body || maximum <= 0)
|
|
1700
|
+
return undefined;
|
|
1701
|
+
const encoding = body.encoding?.toLowerCase();
|
|
1702
|
+
let bytes;
|
|
1703
|
+
try {
|
|
1704
|
+
if (encoding === "utf8") {
|
|
1705
|
+
bytes = new TextEncoder().encode(sanitizeSensitiveText(body.data, options));
|
|
1706
|
+
}
|
|
1707
|
+
else if (encoding === "base64" && options.captureBinaryBodies === true) {
|
|
1708
|
+
bytes = base64ToBytes(body.data);
|
|
1709
|
+
}
|
|
1710
|
+
else {
|
|
1711
|
+
return undefined;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
catch {
|
|
1715
|
+
return undefined;
|
|
1716
|
+
}
|
|
1717
|
+
const originalLength = bytes.length;
|
|
1718
|
+
const captured = encoding === "utf8"
|
|
1719
|
+
? truncateUtf8(bytes, maximum)
|
|
1720
|
+
: bytes.slice(0, maximum);
|
|
1721
|
+
const totalBytes = normalizeSize(body.totalBytes);
|
|
1722
|
+
return {
|
|
1723
|
+
contentType: normalizeOptional(body.contentType, 512),
|
|
1724
|
+
encoding,
|
|
1725
|
+
data: encoding === "base64"
|
|
1726
|
+
? bytesToBase64$1(captured)
|
|
1727
|
+
: new TextDecoder().decode(captured),
|
|
1728
|
+
capturedBytes: captured.length,
|
|
1729
|
+
totalBytes,
|
|
1730
|
+
truncated: body.truncated ||
|
|
1731
|
+
originalLength > captured.length ||
|
|
1732
|
+
(totalBytes != null && totalBytes > captured.length),
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
function normalizeRecord(input, options, globalObject) {
|
|
1736
|
+
const now = new Date().toISOString();
|
|
1737
|
+
const startedAtUtc = normalizeTimestamp(input.startedAtUtc, now);
|
|
1738
|
+
const completedAtUtc = normalizeTimestamp(input.completedAtUtc, startedAtUtc);
|
|
1739
|
+
const duration = Number(input.durationMilliseconds);
|
|
1740
|
+
return {
|
|
1741
|
+
schema: networkRequestSchema,
|
|
1742
|
+
id: normalizeRequired(input.id, generateId(globalObject), 128),
|
|
1743
|
+
source: normalizeRequired(input.source, "unknown", 128),
|
|
1744
|
+
startedAtUtc,
|
|
1745
|
+
completedAtUtc: completedAtUtc < startedAtUtc ? startedAtUtc : completedAtUtc,
|
|
1746
|
+
durationMilliseconds: Number.isFinite(duration) && duration >= 0 ? duration : 0,
|
|
1747
|
+
method: normalizeRequired(input.method, "GET", 32).toUpperCase(),
|
|
1748
|
+
url: sanitizeUrl(input.url, options),
|
|
1749
|
+
protocol: normalizeOptional(input.protocol, 64),
|
|
1750
|
+
requestHeaders: options.includeRequestHeaders === false
|
|
1751
|
+
? []
|
|
1752
|
+
: sanitizeHeaders(input.requestHeaders, options),
|
|
1753
|
+
requestBodySizeBytes: options.includeBodySizes === false
|
|
1754
|
+
? undefined
|
|
1755
|
+
: normalizeSize(input.requestBodySizeBytes),
|
|
1756
|
+
requestBody: options.captureRequestBody !== false
|
|
1757
|
+
? normalizeBody(input.requestBody, options)
|
|
1758
|
+
: undefined,
|
|
1759
|
+
statusCode: Number.isInteger(Number(input.statusCode)) &&
|
|
1760
|
+
Number(input.statusCode) >= 100 &&
|
|
1761
|
+
Number(input.statusCode) <= 999
|
|
1762
|
+
? Number(input.statusCode)
|
|
1763
|
+
: undefined,
|
|
1764
|
+
reasonPhrase: normalizeOptional(input.reasonPhrase, 512),
|
|
1765
|
+
responseHeaders: options.includeResponseHeaders === false
|
|
1766
|
+
? []
|
|
1767
|
+
: sanitizeHeaders(input.responseHeaders, options),
|
|
1768
|
+
responseBodySizeBytes: options.includeBodySizes === false
|
|
1769
|
+
? undefined
|
|
1770
|
+
: normalizeSize(input.responseBodySizeBytes),
|
|
1771
|
+
responseBody: options.captureResponseBody !== false
|
|
1772
|
+
? normalizeBody(input.responseBody, options)
|
|
1773
|
+
: undefined,
|
|
1774
|
+
errorType: normalizeOptional(input.errorType, 512),
|
|
1775
|
+
errorMessage: sanitizeErrorMessage(input.errorMessage, options),
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
function sanitizeNetworkRequest(input, options = {}, globalObject = globalThis) {
|
|
1779
|
+
try {
|
|
1780
|
+
let normalized = normalizeRecord(input, options, globalObject);
|
|
1781
|
+
if (options.urlSanitizer) {
|
|
1782
|
+
normalized = normalizeRecord({ ...normalized, url: options.urlSanitizer(normalized.url) }, options, globalObject);
|
|
1783
|
+
}
|
|
1784
|
+
if (options.requestSanitizer) {
|
|
1785
|
+
const transformed = options.requestSanitizer(normalized);
|
|
1786
|
+
if (transformed == null)
|
|
1787
|
+
return null;
|
|
1788
|
+
normalized = normalizeRecord(transformed, options, globalObject);
|
|
1789
|
+
}
|
|
1790
|
+
return normalized;
|
|
1791
|
+
}
|
|
1792
|
+
catch {
|
|
1793
|
+
return null;
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
function parseContentLength(headers) {
|
|
1797
|
+
const entry = headerEntries(headers).find(([name]) => String(name).toLowerCase() === "content-length");
|
|
1798
|
+
return entry ? normalizeSize(entry[1]) : undefined;
|
|
1799
|
+
}
|
|
1800
|
+
function headerValue(headers, wantedName) {
|
|
1801
|
+
const entry = headerEntries(headers).find(([name]) => String(name).toLowerCase() === wantedName);
|
|
1802
|
+
return entry == null ? undefined : String(entry[1]);
|
|
1803
|
+
}
|
|
1804
|
+
function isTextContentType(contentType) {
|
|
1805
|
+
if (!contentType)
|
|
1806
|
+
return true;
|
|
1807
|
+
const mediaType = contentType.split(";", 1)[0].trim().toLowerCase();
|
|
1808
|
+
return (mediaType.startsWith("text/") ||
|
|
1809
|
+
mediaType.endsWith("+json") ||
|
|
1810
|
+
mediaType.endsWith("+xml") ||
|
|
1811
|
+
[
|
|
1812
|
+
"application/json",
|
|
1813
|
+
"application/xml",
|
|
1814
|
+
"application/graphql",
|
|
1815
|
+
"application/javascript",
|
|
1816
|
+
"application/x-www-form-urlencoded",
|
|
1817
|
+
].includes(mediaType));
|
|
1818
|
+
}
|
|
1819
|
+
function bodyFromBytes(bytes, totalBytes, contentType, options) {
|
|
1820
|
+
const binary = !isTextContentType(contentType);
|
|
1821
|
+
if (binary && options.captureBinaryBodies !== true)
|
|
1822
|
+
return undefined;
|
|
1823
|
+
const maximum = maximumBodyBytes(options);
|
|
1824
|
+
if (maximum <= 0)
|
|
1825
|
+
return undefined;
|
|
1826
|
+
const captured = binary
|
|
1827
|
+
? bytes.slice(0, maximum)
|
|
1828
|
+
: truncateUtf8(bytes, maximum);
|
|
1829
|
+
return {
|
|
1830
|
+
contentType,
|
|
1831
|
+
encoding: binary ? "base64" : "utf8",
|
|
1832
|
+
data: binary ? bytesToBase64$1(captured) : new TextDecoder().decode(captured),
|
|
1833
|
+
capturedBytes: captured.length,
|
|
1834
|
+
totalBytes,
|
|
1835
|
+
truncated: bytes.length > captured.length ||
|
|
1836
|
+
(totalBytes != null && totalBytes > captured.length),
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
function bodyFromValue(value, headers, options) {
|
|
1840
|
+
if (value == null || options.captureRequestBody === false)
|
|
1841
|
+
return undefined;
|
|
1842
|
+
const contentType = headerValue(headers, "content-type");
|
|
1843
|
+
if (typeof value === "string" || value instanceof URLSearchParams) {
|
|
1844
|
+
const bytes = new TextEncoder().encode(String(value));
|
|
1845
|
+
return bodyFromBytes(bytes, bytes.length, contentType ||
|
|
1846
|
+
(value instanceof URLSearchParams
|
|
1847
|
+
? "application/x-www-form-urlencoded"
|
|
1848
|
+
: undefined), options);
|
|
1849
|
+
}
|
|
1850
|
+
if (value instanceof ArrayBuffer) {
|
|
1851
|
+
const bytes = new Uint8Array(value);
|
|
1852
|
+
return bodyFromBytes(bytes, bytes.length, contentType, options);
|
|
1853
|
+
}
|
|
1854
|
+
if (ArrayBuffer.isView(value)) {
|
|
1855
|
+
const bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
1856
|
+
return bodyFromBytes(bytes, bytes.length, contentType, options);
|
|
1857
|
+
}
|
|
1858
|
+
return undefined;
|
|
1859
|
+
}
|
|
1860
|
+
async function bodyFromFetchResponse(response, headers, options, shouldContinue = () => true) {
|
|
1861
|
+
if (!shouldContinue() || options.captureResponseBody === false)
|
|
1862
|
+
return undefined;
|
|
1863
|
+
const contentType = headerValue(headers, "content-type");
|
|
1864
|
+
if (!isTextContentType(contentType) && options.captureBinaryBodies !== true) {
|
|
1865
|
+
return undefined;
|
|
1866
|
+
}
|
|
1867
|
+
const totalBytes = parseContentLength(headers);
|
|
1868
|
+
const maximum = maximumBodyBytes(options);
|
|
1869
|
+
if (maximum <= 0)
|
|
1870
|
+
return undefined;
|
|
1871
|
+
const clone = response.clone();
|
|
1872
|
+
if (clone.body) {
|
|
1873
|
+
const reader = clone.body.getReader();
|
|
1874
|
+
const chunks = [];
|
|
1875
|
+
let capturedLength = 0;
|
|
1876
|
+
let observedLength = 0;
|
|
1877
|
+
try {
|
|
1878
|
+
while (capturedLength <= maximum) {
|
|
1879
|
+
if (!shouldContinue()) {
|
|
1880
|
+
await reader.cancel().catch(() => undefined);
|
|
1881
|
+
return undefined;
|
|
1882
|
+
}
|
|
1883
|
+
const result = await reader.read();
|
|
1884
|
+
if (!shouldContinue()) {
|
|
1885
|
+
await reader.cancel().catch(() => undefined);
|
|
1886
|
+
return undefined;
|
|
1887
|
+
}
|
|
1888
|
+
if (result.done)
|
|
1889
|
+
break;
|
|
1890
|
+
const chunk = result.value;
|
|
1891
|
+
observedLength += chunk.length;
|
|
1892
|
+
const remaining = maximum - capturedLength;
|
|
1893
|
+
if (remaining > 0) {
|
|
1894
|
+
const kept = chunk.slice(0, remaining);
|
|
1895
|
+
chunks.push(kept);
|
|
1896
|
+
capturedLength += kept.length;
|
|
1897
|
+
}
|
|
1898
|
+
if (observedLength > maximum) {
|
|
1899
|
+
await reader.cancel().catch(() => undefined);
|
|
1900
|
+
break;
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
finally {
|
|
1905
|
+
reader.releaseLock();
|
|
1906
|
+
}
|
|
1907
|
+
const joined = new Uint8Array(capturedLength);
|
|
1908
|
+
let offset = 0;
|
|
1909
|
+
for (const chunk of chunks) {
|
|
1910
|
+
joined.set(chunk, offset);
|
|
1911
|
+
offset += chunk.length;
|
|
1912
|
+
}
|
|
1913
|
+
return bodyFromBytes(joined, totalBytes ?? observedLength, contentType, options);
|
|
1914
|
+
}
|
|
1915
|
+
if (totalBytes == null || totalBytes > maximum)
|
|
1916
|
+
return undefined;
|
|
1917
|
+
const bytes = new Uint8Array(await clone.arrayBuffer());
|
|
1918
|
+
if (!shouldContinue())
|
|
1919
|
+
return undefined;
|
|
1920
|
+
return bodyFromBytes(bytes, totalBytes, contentType, options);
|
|
1921
|
+
}
|
|
1922
|
+
function parseXhrResponseHeaders(value) {
|
|
1923
|
+
return value
|
|
1924
|
+
.trim()
|
|
1925
|
+
.split(/[\r\n]+/)
|
|
1926
|
+
.flatMap((line) => {
|
|
1927
|
+
const separator = line.indexOf(":");
|
|
1928
|
+
return separator < 0
|
|
1929
|
+
? []
|
|
1930
|
+
: [
|
|
1931
|
+
{
|
|
1932
|
+
name: line.slice(0, separator),
|
|
1933
|
+
value: line.slice(separator + 1),
|
|
1934
|
+
},
|
|
1935
|
+
];
|
|
1936
|
+
});
|
|
1937
|
+
}
|
|
1938
|
+
function monotonicNow(globalObject) {
|
|
1939
|
+
return typeof globalObject.performance?.now === "function"
|
|
1940
|
+
? globalObject.performance.now()
|
|
1941
|
+
: Date.now();
|
|
1942
|
+
}
|
|
1943
|
+
function installBrowserNetworkCapture(capture, options = {}, sourcePrefix = "capacitor", globalObject = globalThis) {
|
|
1944
|
+
const cleanups = [];
|
|
1945
|
+
let active = true;
|
|
1946
|
+
let fetchInvocationDepth = 0;
|
|
1947
|
+
if (options.captureFetch !== false &&
|
|
1948
|
+
typeof globalObject.fetch === "function") {
|
|
1949
|
+
const originalFetch = globalObject.fetch;
|
|
1950
|
+
const wrappedFetch = function (input, init) {
|
|
1951
|
+
const startedAtUtc = new Date().toISOString();
|
|
1952
|
+
const started = monotonicNow(globalObject);
|
|
1953
|
+
const inputRequest = typeof input === "object" && "headers" in input && "method" in input
|
|
1954
|
+
? input
|
|
1955
|
+
: undefined;
|
|
1956
|
+
const requestHeaders = headerEntries(inputRequest?.headers).concat(headerEntries(init?.headers));
|
|
1957
|
+
const requestBody = bodyFromValue(init?.body, requestHeaders, options);
|
|
1958
|
+
const method = init?.method || inputRequest?.method || "GET";
|
|
1959
|
+
const url = typeof input === "string" ? input : inputRequest?.url || String(input);
|
|
1960
|
+
let promise;
|
|
1961
|
+
fetchInvocationDepth += 1;
|
|
1962
|
+
try {
|
|
1963
|
+
promise = originalFetch(input, init);
|
|
1964
|
+
}
|
|
1965
|
+
catch (error) {
|
|
1966
|
+
fetchInvocationDepth -= 1;
|
|
1967
|
+
const request = sanitizeNetworkRequest({
|
|
1968
|
+
source: `${sourcePrefix}.fetch`,
|
|
1969
|
+
startedAtUtc,
|
|
1970
|
+
completedAtUtc: new Date().toISOString(),
|
|
1971
|
+
durationMilliseconds: monotonicNow(globalObject) - started,
|
|
1972
|
+
method,
|
|
1973
|
+
url,
|
|
1974
|
+
requestHeaders: sanitizeHeaders(requestHeaders, {}),
|
|
1975
|
+
requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
|
|
1976
|
+
requestBody,
|
|
1977
|
+
errorType: error instanceof Error ? error.name : "Error",
|
|
1978
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
1979
|
+
}, options, globalObject);
|
|
1980
|
+
if (active && request)
|
|
1981
|
+
Promise.resolve(capture(request)).catch(() => undefined);
|
|
1982
|
+
throw error;
|
|
1983
|
+
}
|
|
1984
|
+
fetchInvocationDepth -= 1;
|
|
1985
|
+
return promise.then((response) => {
|
|
1986
|
+
if (!active)
|
|
1987
|
+
return response;
|
|
1988
|
+
const responseHeaders = headerEntries(response.headers);
|
|
1989
|
+
const responseRecord = {
|
|
1990
|
+
source: `${sourcePrefix}.fetch`,
|
|
1991
|
+
startedAtUtc,
|
|
1992
|
+
completedAtUtc: new Date().toISOString(),
|
|
1993
|
+
durationMilliseconds: monotonicNow(globalObject) - started,
|
|
1994
|
+
method,
|
|
1995
|
+
url: response.url || url,
|
|
1996
|
+
requestHeaders: sanitizeHeaders(requestHeaders, {}),
|
|
1997
|
+
requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
|
|
1998
|
+
requestBody,
|
|
1999
|
+
statusCode: response.status,
|
|
2000
|
+
reasonPhrase: response.statusText,
|
|
2001
|
+
responseHeaders: sanitizeHeaders(responseHeaders, {}),
|
|
2002
|
+
responseBodySizeBytes: parseContentLength(responseHeaders),
|
|
2003
|
+
};
|
|
2004
|
+
return bodyFromFetchResponse(response, responseHeaders, options, () => active).then((responseBody) => {
|
|
2005
|
+
const request = sanitizeNetworkRequest({
|
|
2006
|
+
...responseRecord,
|
|
2007
|
+
responseBody,
|
|
2008
|
+
responseBodySizeBytes: responseRecord.responseBodySizeBytes ??
|
|
2009
|
+
responseBody?.totalBytes,
|
|
2010
|
+
}, options, globalObject);
|
|
2011
|
+
if (active && request)
|
|
2012
|
+
void Promise.resolve(capture(request)).catch(() => undefined);
|
|
2013
|
+
return response;
|
|
2014
|
+
}, () => {
|
|
2015
|
+
const request = sanitizeNetworkRequest(responseRecord, options, globalObject);
|
|
2016
|
+
if (active && request)
|
|
2017
|
+
void Promise.resolve(capture(request)).catch(() => undefined);
|
|
2018
|
+
return response;
|
|
2019
|
+
});
|
|
2020
|
+
}, (error) => {
|
|
2021
|
+
const request = sanitizeNetworkRequest({
|
|
2022
|
+
source: `${sourcePrefix}.fetch`,
|
|
2023
|
+
startedAtUtc,
|
|
2024
|
+
completedAtUtc: new Date().toISOString(),
|
|
2025
|
+
durationMilliseconds: monotonicNow(globalObject) - started,
|
|
2026
|
+
method,
|
|
2027
|
+
url,
|
|
2028
|
+
requestHeaders: sanitizeHeaders(requestHeaders, {}),
|
|
2029
|
+
requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
|
|
2030
|
+
requestBody,
|
|
2031
|
+
errorType: error instanceof Error ? error.name : "Error",
|
|
2032
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
2033
|
+
}, options, globalObject);
|
|
2034
|
+
if (active && request)
|
|
2035
|
+
Promise.resolve(capture(request)).catch(() => undefined);
|
|
2036
|
+
throw error;
|
|
2037
|
+
});
|
|
2038
|
+
};
|
|
2039
|
+
globalObject.fetch = wrappedFetch;
|
|
2040
|
+
cleanups.push(() => {
|
|
2041
|
+
if (globalObject.fetch === wrappedFetch)
|
|
2042
|
+
globalObject.fetch = originalFetch;
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
const Xhr = globalObject.XMLHttpRequest;
|
|
2046
|
+
if (options.captureXmlHttpRequest !== false && Xhr?.prototype) {
|
|
2047
|
+
const states = new WeakMap();
|
|
2048
|
+
const prototype = Xhr.prototype;
|
|
2049
|
+
const originalOpen = prototype.open;
|
|
2050
|
+
const originalSend = prototype.send;
|
|
2051
|
+
const originalSetRequestHeader = prototype.setRequestHeader;
|
|
2052
|
+
const wrappedOpen = function (method, url, ...rest) {
|
|
2053
|
+
states.set(this, {
|
|
2054
|
+
method,
|
|
2055
|
+
url: String(url),
|
|
2056
|
+
requestHeaders: [],
|
|
2057
|
+
suppressed: fetchInvocationDepth > 0,
|
|
2058
|
+
});
|
|
2059
|
+
Reflect.apply(originalOpen, this, [method, url, ...rest]);
|
|
2060
|
+
};
|
|
2061
|
+
const wrappedSetRequestHeader = function (name, value) {
|
|
2062
|
+
states.get(this)?.requestHeaders.push({ name, value });
|
|
2063
|
+
Reflect.apply(originalSetRequestHeader, this, [name, value]);
|
|
2064
|
+
};
|
|
2065
|
+
const wrappedSend = function (body) {
|
|
2066
|
+
const state = states.get(this);
|
|
2067
|
+
if (!state || state.suppressed) {
|
|
2068
|
+
Reflect.apply(originalSend, this, [body]);
|
|
2069
|
+
return;
|
|
2070
|
+
}
|
|
2071
|
+
state.startedAtUtc = new Date().toISOString();
|
|
2072
|
+
state.started = monotonicNow(globalObject);
|
|
2073
|
+
state.requestBody = bodyFromValue(body, state.requestHeaders, options);
|
|
2074
|
+
let failure;
|
|
2075
|
+
const markFailure = (event) => {
|
|
2076
|
+
failure = event.type;
|
|
2077
|
+
};
|
|
2078
|
+
const complete = () => {
|
|
2079
|
+
if (!active)
|
|
2080
|
+
return;
|
|
2081
|
+
let responseHeaders = [];
|
|
2082
|
+
try {
|
|
2083
|
+
responseHeaders = parseXhrResponseHeaders(this.getAllResponseHeaders());
|
|
2084
|
+
}
|
|
2085
|
+
catch {
|
|
2086
|
+
// Some WebViews throw before response headers exist.
|
|
2087
|
+
}
|
|
2088
|
+
let responseBody;
|
|
2089
|
+
try {
|
|
2090
|
+
const responseType = this.responseType || "text";
|
|
2091
|
+
const responseOptions = {
|
|
2092
|
+
...options,
|
|
2093
|
+
captureRequestBody: options.captureResponseBody,
|
|
2094
|
+
};
|
|
2095
|
+
if (responseType === "text") {
|
|
2096
|
+
responseBody = bodyFromValue(this.responseText, responseHeaders, responseOptions);
|
|
2097
|
+
}
|
|
2098
|
+
else if (responseType === "arraybuffer") {
|
|
2099
|
+
responseBody = bodyFromValue(this.response, responseHeaders, responseOptions);
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
catch {
|
|
2103
|
+
// Response data is not readable for every XHR response type.
|
|
2104
|
+
}
|
|
2105
|
+
const request = sanitizeNetworkRequest({
|
|
2106
|
+
source: `${sourcePrefix}.xhr`,
|
|
2107
|
+
startedAtUtc: state.startedAtUtc,
|
|
2108
|
+
completedAtUtc: new Date().toISOString(),
|
|
2109
|
+
durationMilliseconds: monotonicNow(globalObject) - (state.started ?? 0),
|
|
2110
|
+
method: state.method,
|
|
2111
|
+
url: this.responseURL || state.url,
|
|
2112
|
+
requestHeaders: state.requestHeaders,
|
|
2113
|
+
requestBodySizeBytes: parseContentLength(state.requestHeaders) ??
|
|
2114
|
+
state.requestBody?.totalBytes,
|
|
2115
|
+
requestBody: state.requestBody,
|
|
2116
|
+
statusCode: this.status || undefined,
|
|
2117
|
+
reasonPhrase: this.statusText,
|
|
2118
|
+
responseHeaders,
|
|
2119
|
+
responseBodySizeBytes: parseContentLength(responseHeaders) ?? responseBody?.totalBytes,
|
|
2120
|
+
responseBody,
|
|
2121
|
+
errorType: failure,
|
|
2122
|
+
errorMessage: failure ? `XMLHttpRequest ${failure}` : undefined,
|
|
2123
|
+
}, options, globalObject);
|
|
2124
|
+
if (request)
|
|
2125
|
+
Promise.resolve(capture(request)).catch(() => undefined);
|
|
2126
|
+
};
|
|
2127
|
+
this.addEventListener("error", markFailure);
|
|
2128
|
+
this.addEventListener("abort", markFailure);
|
|
2129
|
+
this.addEventListener("timeout", markFailure);
|
|
2130
|
+
this.addEventListener("loadend", complete, { once: true });
|
|
2131
|
+
Reflect.apply(originalSend, this, [body]);
|
|
2132
|
+
};
|
|
2133
|
+
prototype.open = wrappedOpen;
|
|
2134
|
+
prototype.setRequestHeader = wrappedSetRequestHeader;
|
|
2135
|
+
prototype.send = wrappedSend;
|
|
2136
|
+
cleanups.push(() => {
|
|
2137
|
+
if (prototype.open === wrappedOpen)
|
|
2138
|
+
prototype.open = originalOpen;
|
|
2139
|
+
if (prototype.send === wrappedSend)
|
|
2140
|
+
prototype.send = originalSend;
|
|
2141
|
+
if (prototype.setRequestHeader === wrappedSetRequestHeader) {
|
|
2142
|
+
prototype.setRequestHeader = originalSetRequestHeader;
|
|
2143
|
+
}
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
let removed = false;
|
|
2147
|
+
return {
|
|
2148
|
+
remove() {
|
|
2149
|
+
if (removed)
|
|
2150
|
+
return;
|
|
2151
|
+
removed = true;
|
|
2152
|
+
active = false;
|
|
2153
|
+
for (const cleanup of cleanups.reverse())
|
|
2154
|
+
cleanup();
|
|
2155
|
+
},
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
const ANSIGHT_CAPACITOR_SDK_VERSION = "1.3.0-preview.11";
|
|
2160
|
+
const COMPILED_CAPACITOR_CORE_VERSION = "8.4.2";
|
|
2161
|
+
const CAPACITOR_GROUP = "capacitor";
|
|
2162
|
+
const LOCALIZATION_GROUP = "localization";
|
|
2163
|
+
function normalized(value) {
|
|
2164
|
+
if (value == null)
|
|
2165
|
+
return undefined;
|
|
2166
|
+
const result = String(value).trim();
|
|
2167
|
+
return result || undefined;
|
|
2168
|
+
}
|
|
2169
|
+
function canonicalizeLocale(value) {
|
|
2170
|
+
const locale = normalized(value)?.replace(/_/g, "-");
|
|
2171
|
+
if (!locale)
|
|
2172
|
+
return undefined;
|
|
2173
|
+
try {
|
|
2174
|
+
return Intl.getCanonicalLocales(locale)[0] ?? locale;
|
|
2175
|
+
}
|
|
2176
|
+
catch {
|
|
2177
|
+
return locale;
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
function parseLocale(locale) {
|
|
2181
|
+
const parts = (locale ?? "").split("-").filter(Boolean);
|
|
2182
|
+
const language = parts[0]?.toLowerCase();
|
|
2183
|
+
const region = parts.find((part, index) => index > 0 && (/^[A-Za-z]{2}$/.test(part) || /^\d{3}$/.test(part)));
|
|
2184
|
+
return { language, region: region?.toUpperCase() };
|
|
2185
|
+
}
|
|
2186
|
+
function webViewDetails(platform, nativePlatform, userAgent) {
|
|
2187
|
+
const agent = userAgent ?? "";
|
|
2188
|
+
if (nativePlatform && platform === "ios") {
|
|
2189
|
+
return {
|
|
2190
|
+
engine: "wkWebView",
|
|
2191
|
+
version: /AppleWebKit\/([^\s]+)/.exec(agent)?.[1],
|
|
2192
|
+
};
|
|
2193
|
+
}
|
|
2194
|
+
if (nativePlatform && platform === "android") {
|
|
2195
|
+
return {
|
|
2196
|
+
engine: "chromiumWebView",
|
|
2197
|
+
version: /(?:Chrome|Chromium)\/([^\s]+)/.exec(agent)?.[1],
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
if (/Firefox\/([^\s]+)/.test(agent)) {
|
|
2201
|
+
return { engine: "gecko", version: /Firefox\/([^\s]+)/.exec(agent)?.[1] };
|
|
2202
|
+
}
|
|
2203
|
+
if (/(?:Chrome|Chromium)\/([^\s]+)/.test(agent)) {
|
|
2204
|
+
return {
|
|
2205
|
+
engine: "chromium",
|
|
2206
|
+
version: /(?:Chrome|Chromium)\/([^\s]+)/.exec(agent)?.[1],
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
if (/AppleWebKit\/([^\s]+)/.test(agent)) {
|
|
2210
|
+
return {
|
|
2211
|
+
engine: "webkit",
|
|
2212
|
+
version: /AppleWebKit\/([^\s]+)/.exec(agent)?.[1],
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
return { engine: "unknown" };
|
|
2216
|
+
}
|
|
2217
|
+
function currentCapacitorSessionEnvironment(platform, nativePlatform) {
|
|
2218
|
+
let resolved;
|
|
2219
|
+
try {
|
|
2220
|
+
resolved = Intl.DateTimeFormat().resolvedOptions();
|
|
2221
|
+
}
|
|
2222
|
+
catch {
|
|
2223
|
+
resolved = undefined;
|
|
2224
|
+
}
|
|
2225
|
+
return {
|
|
2226
|
+
platform,
|
|
2227
|
+
nativePlatform,
|
|
2228
|
+
userAgent: typeof navigator === "undefined" ? undefined : navigator.userAgent,
|
|
2229
|
+
locale: resolved?.locale ??
|
|
2230
|
+
(typeof navigator === "undefined" ? undefined : navigator.language),
|
|
2231
|
+
timeZone: resolved?.timeZone,
|
|
2232
|
+
utcOffsetMinutes: -new Date().getTimezoneOffset(),
|
|
2233
|
+
};
|
|
2234
|
+
}
|
|
2235
|
+
function createAutomaticSessionProperties(environment) {
|
|
2236
|
+
const platform = normalized(environment.platform) ?? "unknown";
|
|
2237
|
+
const userAgent = normalized(environment.userAgent);
|
|
2238
|
+
const webView = webViewDetails(platform, environment.nativePlatform, userAgent);
|
|
2239
|
+
const locale = canonicalizeLocale(environment.locale);
|
|
2240
|
+
const parsedLocale = parseLocale(locale);
|
|
2241
|
+
const capacitor = {
|
|
2242
|
+
sdkVersion: ANSIGHT_CAPACITOR_SDK_VERSION,
|
|
2243
|
+
capacitorVersion: "8.x",
|
|
2244
|
+
compiledCapacitorVersion: COMPILED_CAPACITOR_CORE_VERSION,
|
|
2245
|
+
platform,
|
|
2246
|
+
runtimeLanguage: "javascript",
|
|
2247
|
+
executionMode: environment.nativePlatform ? "native" : "web",
|
|
2248
|
+
webViewEngine: webView.engine,
|
|
2249
|
+
};
|
|
2250
|
+
if (webView.version)
|
|
2251
|
+
capacitor.webViewEngineVersion = webView.version;
|
|
2252
|
+
if (userAgent)
|
|
2253
|
+
capacitor.userAgent = userAgent;
|
|
2254
|
+
const localization = {
|
|
2255
|
+
utcOffsetMinutes: String(environment.utcOffsetMinutes ?? -new Date().getTimezoneOffset()),
|
|
2256
|
+
};
|
|
2257
|
+
if (locale)
|
|
2258
|
+
localization.locale = locale;
|
|
2259
|
+
if (parsedLocale.language)
|
|
2260
|
+
localization.language = parsedLocale.language;
|
|
2261
|
+
if (parsedLocale.region)
|
|
2262
|
+
localization.region = parsedLocale.region;
|
|
2263
|
+
if (normalized(environment.timeZone)) {
|
|
2264
|
+
localization.timeZone = String(environment.timeZone).trim();
|
|
2265
|
+
}
|
|
2266
|
+
return {
|
|
2267
|
+
[CAPACITOR_GROUP]: capacitor,
|
|
2268
|
+
[LOCALIZATION_GROUP]: localization,
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
function mergeSessionProperties(automaticProperties, customProperties) {
|
|
2272
|
+
const merged = Object.fromEntries(Object.entries(automaticProperties).map(([group, properties]) => [
|
|
2273
|
+
group,
|
|
2274
|
+
{ ...properties },
|
|
2275
|
+
]));
|
|
2276
|
+
for (const [group, properties] of Object.entries(customProperties ?? {})) {
|
|
2277
|
+
merged[group] = { ...(merged[group] ?? {}), ...properties };
|
|
2278
|
+
}
|
|
2279
|
+
return merged;
|
|
2280
|
+
}
|
|
2281
|
+
|
|
1262
2282
|
const AnsightNative = registerPlugin("Ansight");
|
|
1263
2283
|
const toolHandlers = new Map();
|
|
1264
2284
|
const artifactProviders = new Map();
|
|
@@ -1269,6 +2289,9 @@
|
|
|
1269
2289
|
let lifecycleCleanup;
|
|
1270
2290
|
let artifactToolRegistrations = [];
|
|
1271
2291
|
let domToolRegistration;
|
|
2292
|
+
let networkCaptureSubscription;
|
|
2293
|
+
let networkCaptureRegistration;
|
|
2294
|
+
let networkConnectionListener;
|
|
1272
2295
|
function normalizePairingPayload(payload) {
|
|
1273
2296
|
if (payload == null)
|
|
1274
2297
|
return payload;
|
|
@@ -1276,11 +2299,19 @@
|
|
|
1276
2299
|
}
|
|
1277
2300
|
function normalizeOptions(input) {
|
|
1278
2301
|
const options = JSON.parse(JSON.stringify(input));
|
|
2302
|
+
options.customProperties = mergeSessionProperties(automaticSessionProperties(), options.customProperties);
|
|
1279
2303
|
delete options.domTools;
|
|
1280
2304
|
delete options.errorCapture;
|
|
1281
2305
|
delete options.lifecycle;
|
|
2306
|
+
delete options.networkCapture;
|
|
1282
2307
|
return options;
|
|
1283
2308
|
}
|
|
2309
|
+
function automaticSessionProperties() {
|
|
2310
|
+
return createAutomaticSessionProperties(currentCapacitorSessionEnvironment(Capacitor.getPlatform(), Capacitor.isNativePlatform()));
|
|
2311
|
+
}
|
|
2312
|
+
function automaticSessionPropertyValue(group, key) {
|
|
2313
|
+
return automaticSessionProperties()[group]?.[key];
|
|
2314
|
+
}
|
|
1284
2315
|
function normalizeToolResult(value) {
|
|
1285
2316
|
if (value && typeof value === "object" && "success" in value) {
|
|
1286
2317
|
return value;
|
|
@@ -1330,11 +2361,13 @@
|
|
|
1330
2361
|
}
|
|
1331
2362
|
async function afterConnectionChange(operation) {
|
|
1332
2363
|
const result = await operation();
|
|
2364
|
+
await refreshNetworkCaptureConnection();
|
|
1333
2365
|
await emitHostConnectionStatus();
|
|
1334
2366
|
return result;
|
|
1335
2367
|
}
|
|
1336
2368
|
async function initialize(options = {}) {
|
|
1337
2369
|
const result = await AnsightNative.initialize(normalizeOptions(options));
|
|
2370
|
+
await configureNetworkCapture(options.networkCapture);
|
|
1338
2371
|
if (options.lifecycle !== false)
|
|
1339
2372
|
startLifecycleTracking();
|
|
1340
2373
|
if (options.errorCapture) {
|
|
@@ -1348,6 +2381,7 @@
|
|
|
1348
2381
|
}
|
|
1349
2382
|
async function initializeAndActivate(options = {}) {
|
|
1350
2383
|
const result = await AnsightNative.initializeAndActivate(normalizeOptions(options));
|
|
2384
|
+
await configureNetworkCapture(options.networkCapture);
|
|
1351
2385
|
if (options.lifecycle !== false)
|
|
1352
2386
|
startLifecycleTracking();
|
|
1353
2387
|
if (options.errorCapture) {
|
|
@@ -1372,6 +2406,81 @@
|
|
|
1372
2406
|
return AnsightNative.recordEvent(typeof input === "string" ? { label: input } : input);
|
|
1373
2407
|
}
|
|
1374
2408
|
const recordEvent = event;
|
|
2409
|
+
async function recordNetworkRequest(input, sanitizationOptions = {}) {
|
|
2410
|
+
const request = sanitizeNetworkRequest(input, sanitizationOptions);
|
|
2411
|
+
if (!request) {
|
|
2412
|
+
return {
|
|
2413
|
+
success: false,
|
|
2414
|
+
message: "Network request capture was suppressed by the sanitizer.",
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
return AnsightNative.recordNetworkRequest(request);
|
|
2418
|
+
}
|
|
2419
|
+
function installNetworkCapture(options = {}) {
|
|
2420
|
+
uninstallNetworkCapture();
|
|
2421
|
+
const registration = { options };
|
|
2422
|
+
networkCaptureRegistration = registration;
|
|
2423
|
+
ensureNetworkConnectionListener();
|
|
2424
|
+
void refreshNetworkCaptureConnection();
|
|
2425
|
+
return {
|
|
2426
|
+
remove() {
|
|
2427
|
+
if (networkCaptureRegistration === registration) {
|
|
2428
|
+
uninstallNetworkCapture();
|
|
2429
|
+
}
|
|
2430
|
+
},
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
function uninstallNetworkCapture() {
|
|
2434
|
+
networkCaptureRegistration = undefined;
|
|
2435
|
+
const listener = networkConnectionListener;
|
|
2436
|
+
networkConnectionListener = undefined;
|
|
2437
|
+
if (listener)
|
|
2438
|
+
void listener.then((value) => value.remove());
|
|
2439
|
+
detachNetworkCapture();
|
|
2440
|
+
}
|
|
2441
|
+
function detachNetworkCapture() {
|
|
2442
|
+
networkCaptureSubscription?.remove();
|
|
2443
|
+
networkCaptureSubscription = undefined;
|
|
2444
|
+
}
|
|
2445
|
+
async function refreshNetworkCaptureConnection() {
|
|
2446
|
+
const registration = networkCaptureRegistration;
|
|
2447
|
+
if (!registration) {
|
|
2448
|
+
detachNetworkCapture();
|
|
2449
|
+
return;
|
|
2450
|
+
}
|
|
2451
|
+
try {
|
|
2452
|
+
const status = await AnsightNative.hostConnectionStatus();
|
|
2453
|
+
if (networkCaptureRegistration !== registration)
|
|
2454
|
+
return;
|
|
2455
|
+
applyNetworkConnectionStatus(status);
|
|
2456
|
+
}
|
|
2457
|
+
catch {
|
|
2458
|
+
if (networkCaptureRegistration === registration)
|
|
2459
|
+
detachNetworkCapture();
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
function ensureNetworkConnectionListener() {
|
|
2463
|
+
networkConnectionListener ??= AnsightNative.addListener("ansightHostConnectionStatus", applyNetworkConnectionStatus);
|
|
2464
|
+
}
|
|
2465
|
+
function applyNetworkConnectionStatus(status) {
|
|
2466
|
+
const registration = networkCaptureRegistration;
|
|
2467
|
+
if (!registration || status.isConnected !== true) {
|
|
2468
|
+
detachNetworkCapture();
|
|
2469
|
+
return;
|
|
2470
|
+
}
|
|
2471
|
+
networkCaptureSubscription ??= installBrowserNetworkCapture((request) => AnsightNative.recordNetworkRequest(request), registration.options);
|
|
2472
|
+
}
|
|
2473
|
+
async function configureNetworkCapture(value) {
|
|
2474
|
+
uninstallNetworkCapture();
|
|
2475
|
+
if (!value)
|
|
2476
|
+
return;
|
|
2477
|
+
networkCaptureRegistration = {
|
|
2478
|
+
options: typeof value === "object" ? value : {},
|
|
2479
|
+
};
|
|
2480
|
+
ensureNetworkConnectionListener();
|
|
2481
|
+
await refreshNetworkCaptureConnection();
|
|
2482
|
+
}
|
|
2483
|
+
const recordCrashCandidate = (input) => AnsightNative.recordCrashCandidate(input);
|
|
1375
2484
|
const screenViewed = (name, details = {}) => AnsightNative.screenViewed({ name, details });
|
|
1376
2485
|
const trackRoute = screenViewed;
|
|
1377
2486
|
const setAppLifecycleState = (state) => AnsightNative.setAppLifecycleState({ state });
|
|
@@ -1426,12 +2535,25 @@
|
|
|
1426
2535
|
const captureScreenFrame = (options = {}) => AnsightNative.captureScreenFrame(options);
|
|
1427
2536
|
const enableTouchCapture = () => AnsightNative.enableTouchCapture();
|
|
1428
2537
|
const disableTouchCapture = () => AnsightNative.disableTouchCapture();
|
|
1429
|
-
const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({
|
|
2538
|
+
const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({
|
|
2539
|
+
properties: mergeSessionProperties(automaticSessionProperties(), properties),
|
|
2540
|
+
});
|
|
1430
2541
|
const updateCustomProperties = updateSessionProperties;
|
|
1431
|
-
const clearSessionProperties = () => AnsightNative.
|
|
2542
|
+
const clearSessionProperties = () => AnsightNative.updateSessionProperties({
|
|
2543
|
+
properties: automaticSessionProperties(),
|
|
2544
|
+
});
|
|
1432
2545
|
const clearCustomProperties = clearSessionProperties;
|
|
1433
2546
|
const registerCustomProperty = (group, key, value) => AnsightNative.registerCustomProperty({ group, key, value });
|
|
1434
|
-
const removeCustomProperty = (group, key) =>
|
|
2547
|
+
const removeCustomProperty = (group, key) => {
|
|
2548
|
+
const automaticValue = automaticSessionPropertyValue(group, key);
|
|
2549
|
+
return automaticValue == null
|
|
2550
|
+
? AnsightNative.removeCustomProperty({ group, key })
|
|
2551
|
+
: AnsightNative.registerCustomProperty({
|
|
2552
|
+
group,
|
|
2553
|
+
key,
|
|
2554
|
+
value: automaticValue,
|
|
2555
|
+
});
|
|
2556
|
+
};
|
|
1435
2557
|
function addHostConnectionStatusListener(listener, options = {}) {
|
|
1436
2558
|
hostConnectionListeners.add(listener);
|
|
1437
2559
|
if (options.emitCurrent !== false)
|
|
@@ -1677,6 +2799,18 @@
|
|
|
1677
2799
|
const onError = (eventValue) => {
|
|
1678
2800
|
if (!captureErrors)
|
|
1679
2801
|
return;
|
|
2802
|
+
void recordCrashCandidate({
|
|
2803
|
+
runtime: "capacitor-javascript",
|
|
2804
|
+
kind: "unhandled_javascript_error",
|
|
2805
|
+
message: eventValue.message,
|
|
2806
|
+
stack: eventValue.error instanceof Error ? eventValue.error.stack : undefined,
|
|
2807
|
+
fatal: false,
|
|
2808
|
+
metadata: JSON.stringify({
|
|
2809
|
+
filename: eventValue.filename,
|
|
2810
|
+
line: eventValue.lineno,
|
|
2811
|
+
column: eventValue.colno,
|
|
2812
|
+
}),
|
|
2813
|
+
}).catch(() => undefined);
|
|
1680
2814
|
void event({
|
|
1681
2815
|
label: eventValue.message || "Unhandled JavaScript error",
|
|
1682
2816
|
type: "Exception",
|
|
@@ -1694,6 +2828,13 @@
|
|
|
1694
2828
|
if (!captureRejections)
|
|
1695
2829
|
return;
|
|
1696
2830
|
const reason = eventValue.reason;
|
|
2831
|
+
void recordCrashCandidate({
|
|
2832
|
+
runtime: "capacitor-javascript",
|
|
2833
|
+
kind: "unhandled_promise_rejection",
|
|
2834
|
+
message: reason instanceof Error ? reason.message : String(reason),
|
|
2835
|
+
stack: reason instanceof Error ? reason.stack : undefined,
|
|
2836
|
+
fatal: false,
|
|
2837
|
+
}).catch(() => undefined);
|
|
1697
2838
|
void event({
|
|
1698
2839
|
label: reason instanceof Error
|
|
1699
2840
|
? reason.message
|
|
@@ -1763,6 +2904,10 @@
|
|
|
1763
2904
|
recordMetric,
|
|
1764
2905
|
event,
|
|
1765
2906
|
recordEvent,
|
|
2907
|
+
recordNetworkRequest,
|
|
2908
|
+
installNetworkCapture,
|
|
2909
|
+
uninstallNetworkCapture,
|
|
2910
|
+
sanitizeNetworkRequest,
|
|
1766
2911
|
screenViewed,
|
|
1767
2912
|
trackRoute,
|
|
1768
2913
|
setAppLifecycleState,
|