@ansight/capacitor 1.3.0-preview.10 → 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 +53 -1
- package/android/build.gradle +2 -2
- package/android/src/main/kotlin/ai/ansight/capacitor/AnsightCapacitorPlugin.kt +21 -0
- package/dist/esm/definitions.d.ts +70 -0
- package/dist/esm/definitions.d.ts.map +1 -1
- package/dist/esm/index.d.ts +10 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +111 -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 +7 -1
- package/dist/esm/options.d.ts.map +1 -1
- package/dist/esm/options.js +44 -0
- 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 +1021 -3
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +1021 -3
- package/dist/plugin.js.map +1 -1
- package/dist/standalone.js +1017 -3
- package/dist/standalone.js.map +1 -1
- package/ios/Sources/AnsightCapacitorPlugin/AnsightCapacitorPlugin.swift +28 -0
- package/package.json +1 -1
package/dist/plugin.cjs.js
CHANGED
|
@@ -565,6 +565,50 @@ class AnsightOptionsBuilder {
|
|
|
565
565
|
};
|
|
566
566
|
return this;
|
|
567
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
|
+
}
|
|
568
612
|
withToolGuard(toolGuard) {
|
|
569
613
|
this.options.toolGuard = toolGuard;
|
|
570
614
|
return this;
|
|
@@ -732,6 +776,871 @@ function createOptionsBuilder(options = {}) {
|
|
|
732
776
|
return new AnsightOptionsBuilder(options);
|
|
733
777
|
}
|
|
734
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
|
+
|
|
735
1644
|
const AnsightNative = core.registerPlugin("Ansight");
|
|
736
1645
|
const toolHandlers = new Map();
|
|
737
1646
|
const artifactProviders = new Map();
|
|
@@ -742,6 +1651,9 @@ let logListener;
|
|
|
742
1651
|
let lifecycleCleanup;
|
|
743
1652
|
let artifactToolRegistrations = [];
|
|
744
1653
|
let domToolRegistration;
|
|
1654
|
+
let networkCaptureSubscription;
|
|
1655
|
+
let networkCaptureRegistration;
|
|
1656
|
+
let networkConnectionListener;
|
|
745
1657
|
function normalizePairingPayload(payload) {
|
|
746
1658
|
if (payload == null)
|
|
747
1659
|
return payload;
|
|
@@ -749,11 +1661,19 @@ function normalizePairingPayload(payload) {
|
|
|
749
1661
|
}
|
|
750
1662
|
function normalizeOptions(input) {
|
|
751
1663
|
const options = JSON.parse(JSON.stringify(input));
|
|
1664
|
+
options.customProperties = mergeSessionProperties(automaticSessionProperties(), options.customProperties);
|
|
752
1665
|
delete options.domTools;
|
|
753
1666
|
delete options.errorCapture;
|
|
754
1667
|
delete options.lifecycle;
|
|
1668
|
+
delete options.networkCapture;
|
|
755
1669
|
return options;
|
|
756
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
|
+
}
|
|
757
1677
|
function normalizeToolResult(value) {
|
|
758
1678
|
if (value && typeof value === "object" && "success" in value) {
|
|
759
1679
|
return value;
|
|
@@ -803,11 +1723,13 @@ async function emitHostConnectionStatus() {
|
|
|
803
1723
|
}
|
|
804
1724
|
async function afterConnectionChange(operation) {
|
|
805
1725
|
const result = await operation();
|
|
1726
|
+
await refreshNetworkCaptureConnection();
|
|
806
1727
|
await emitHostConnectionStatus();
|
|
807
1728
|
return result;
|
|
808
1729
|
}
|
|
809
1730
|
async function initialize(options = {}) {
|
|
810
1731
|
const result = await AnsightNative.initialize(normalizeOptions(options));
|
|
1732
|
+
await configureNetworkCapture(options.networkCapture);
|
|
811
1733
|
if (options.lifecycle !== false)
|
|
812
1734
|
startLifecycleTracking();
|
|
813
1735
|
if (options.errorCapture) {
|
|
@@ -821,6 +1743,7 @@ async function initialize(options = {}) {
|
|
|
821
1743
|
}
|
|
822
1744
|
async function initializeAndActivate(options = {}) {
|
|
823
1745
|
const result = await AnsightNative.initializeAndActivate(normalizeOptions(options));
|
|
1746
|
+
await configureNetworkCapture(options.networkCapture);
|
|
824
1747
|
if (options.lifecycle !== false)
|
|
825
1748
|
startLifecycleTracking();
|
|
826
1749
|
if (options.errorCapture) {
|
|
@@ -845,6 +1768,80 @@ function event(input) {
|
|
|
845
1768
|
return AnsightNative.recordEvent(typeof input === "string" ? { label: input } : input);
|
|
846
1769
|
}
|
|
847
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
|
+
}
|
|
848
1845
|
const recordCrashCandidate = (input) => AnsightNative.recordCrashCandidate(input);
|
|
849
1846
|
const screenViewed = (name, details = {}) => AnsightNative.screenViewed({ name, details });
|
|
850
1847
|
const trackRoute = screenViewed;
|
|
@@ -900,12 +1897,25 @@ const disableFramesPerSecond = () => AnsightNative.disableFramesPerSecond();
|
|
|
900
1897
|
const captureScreenFrame = (options = {}) => AnsightNative.captureScreenFrame(options);
|
|
901
1898
|
const enableTouchCapture = () => AnsightNative.enableTouchCapture();
|
|
902
1899
|
const disableTouchCapture = () => AnsightNative.disableTouchCapture();
|
|
903
|
-
const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({
|
|
1900
|
+
const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({
|
|
1901
|
+
properties: mergeSessionProperties(automaticSessionProperties(), properties),
|
|
1902
|
+
});
|
|
904
1903
|
const updateCustomProperties = updateSessionProperties;
|
|
905
|
-
const clearSessionProperties = () => AnsightNative.
|
|
1904
|
+
const clearSessionProperties = () => AnsightNative.updateSessionProperties({
|
|
1905
|
+
properties: automaticSessionProperties(),
|
|
1906
|
+
});
|
|
906
1907
|
const clearCustomProperties = clearSessionProperties;
|
|
907
1908
|
const registerCustomProperty = (group, key, value) => AnsightNative.registerCustomProperty({ group, key, value });
|
|
908
|
-
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
|
+
};
|
|
909
1919
|
function addHostConnectionStatusListener(listener, options = {}) {
|
|
910
1920
|
hostConnectionListeners.add(listener);
|
|
911
1921
|
if (options.emitCurrent !== false)
|
|
@@ -1256,6 +2266,10 @@ const Ansight = {
|
|
|
1256
2266
|
recordMetric,
|
|
1257
2267
|
event,
|
|
1258
2268
|
recordEvent,
|
|
2269
|
+
recordNetworkRequest,
|
|
2270
|
+
installNetworkCapture,
|
|
2271
|
+
uninstallNetworkCapture,
|
|
2272
|
+
sanitizeNetworkRequest,
|
|
1259
2273
|
screenViewed,
|
|
1260
2274
|
trackRoute,
|
|
1261
2275
|
setAppLifecycleState,
|
|
@@ -1353,6 +2367,7 @@ exports.initialize = initialize;
|
|
|
1353
2367
|
exports.initializeAndActivate = initializeAndActivate;
|
|
1354
2368
|
exports.installDomTools = installDomTools;
|
|
1355
2369
|
exports.installErrorHandlers = installErrorHandlers;
|
|
2370
|
+
exports.installNetworkCapture = installNetworkCapture;
|
|
1356
2371
|
exports.isFramesPerSecondEnabled = isFramesPerSecondEnabled;
|
|
1357
2372
|
exports.listRegisteredArtifactProviders = listRegisteredArtifactProviders;
|
|
1358
2373
|
exports.listRegisteredTools = listRegisteredTools;
|
|
@@ -1362,6 +2377,7 @@ exports.openSession = openSession;
|
|
|
1362
2377
|
exports.recordCrashCandidate = recordCrashCandidate;
|
|
1363
2378
|
exports.recordEvent = recordEvent;
|
|
1364
2379
|
exports.recordMetric = recordMetric;
|
|
2380
|
+
exports.recordNetworkRequest = recordNetworkRequest;
|
|
1365
2381
|
exports.recordedEvents = recordedEvents;
|
|
1366
2382
|
exports.recordedMetrics = recordedMetrics;
|
|
1367
2383
|
exports.registerArtifactProvider = registerArtifactProvider;
|
|
@@ -1370,6 +2386,7 @@ exports.registerCustomProperty = registerCustomProperty;
|
|
|
1370
2386
|
exports.registerMetricChannel = registerMetricChannel;
|
|
1371
2387
|
exports.registerTool = registerTool;
|
|
1372
2388
|
exports.removeCustomProperty = removeCustomProperty;
|
|
2389
|
+
exports.sanitizeNetworkRequest = sanitizeNetworkRequest;
|
|
1373
2390
|
exports.savePairingConfig = savePairingConfig;
|
|
1374
2391
|
exports.scanPairingQrCode = scanPairingQrCode;
|
|
1375
2392
|
exports.screenViewed = screenViewed;
|
|
@@ -1383,6 +2400,7 @@ exports.stopAppStateTracking = stopAppStateTracking;
|
|
|
1383
2400
|
exports.stopLifecycleTracking = stopLifecycleTracking;
|
|
1384
2401
|
exports.trackRoute = trackRoute;
|
|
1385
2402
|
exports.uninstallDomTools = uninstallDomTools;
|
|
2403
|
+
exports.uninstallNetworkCapture = uninstallNetworkCapture;
|
|
1386
2404
|
exports.unregisterArtifactProvider = unregisterArtifactProvider;
|
|
1387
2405
|
exports.unregisterTool = unregisterTool;
|
|
1388
2406
|
exports.updateCustomProperties = updateCustomProperties;
|