@ansight/capacitor 1.3.0-preview.10 → 1.3.0-preview.12

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.
Files changed (42) hide show
  1. package/AnsightCapacitor.podspec +1 -1
  2. package/Package.swift +1 -1
  3. package/README.md +112 -25
  4. package/android/build.gradle +2 -2
  5. package/android/src/main/kotlin/ai/ansight/capacitor/AnsightCapacitorPlugin.kt +21 -0
  6. package/dist/esm/definitions.d.ts +70 -0
  7. package/dist/esm/definitions.d.ts.map +1 -1
  8. package/dist/esm/dom.d.ts +11 -0
  9. package/dist/esm/dom.d.ts.map +1 -1
  10. package/dist/esm/dom.js +69 -14
  11. package/dist/esm/dom.js.map +1 -1
  12. package/dist/esm/index.d.ts +10 -1
  13. package/dist/esm/index.d.ts.map +1 -1
  14. package/dist/esm/index.js +111 -3
  15. package/dist/esm/index.js.map +1 -1
  16. package/dist/esm/network.d.ts +6 -0
  17. package/dist/esm/network.d.ts.map +1 -0
  18. package/dist/esm/network.js +742 -0
  19. package/dist/esm/network.js.map +1 -0
  20. package/dist/esm/options.d.ts +7 -1
  21. package/dist/esm/options.d.ts.map +1 -1
  22. package/dist/esm/options.js +44 -0
  23. package/dist/esm/options.js.map +1 -1
  24. package/dist/esm/session-properties.d.ts +16 -0
  25. package/dist/esm/session-properties.d.ts.map +1 -0
  26. package/dist/esm/session-properties.js +123 -0
  27. package/dist/esm/session-properties.js.map +1 -0
  28. package/dist/esm/standalone-options.d.ts +3 -0
  29. package/dist/esm/standalone-options.d.ts.map +1 -0
  30. package/dist/esm/standalone-options.js +15 -0
  31. package/dist/esm/standalone-options.js.map +1 -0
  32. package/dist/esm/standalone.d.ts.map +1 -1
  33. package/dist/esm/standalone.js +2 -11
  34. package/dist/esm/standalone.js.map +1 -1
  35. package/dist/plugin.cjs.js +1090 -17
  36. package/dist/plugin.cjs.js.map +1 -1
  37. package/dist/plugin.js +1090 -17
  38. package/dist/plugin.js.map +1 -1
  39. package/dist/standalone.js +1093 -20
  40. package/dist/standalone.js.map +1 -1
  41. package/ios/Sources/AnsightCapacitorPlugin/AnsightCapacitorPlugin.swift +28 -0
  42. package/package.json +1 -1
@@ -62,6 +62,19 @@ function automationId(element) {
62
62
  element.id ??
63
63
  undefined)?.trim() || undefined);
64
64
  }
65
+ const tapRoles = new Set([
66
+ "button",
67
+ "checkbox",
68
+ "combobox",
69
+ "link",
70
+ "menuitem",
71
+ "menuitemcheckbox",
72
+ "menuitemradio",
73
+ "option",
74
+ "radio",
75
+ "switch",
76
+ "tab",
77
+ ]);
65
78
  function semanticRole(element) {
66
79
  const declared = element.getAttribute("role")?.trim().toLowerCase();
67
80
  if (declared)
@@ -93,24 +106,54 @@ function supportedActions(element, allowActions) {
93
106
  if (!allowActions)
94
107
  return [];
95
108
  const actions = [];
109
+ const htmlElement = element;
110
+ const role = semanticRole(element);
96
111
  if (["A", "BUTTON", "SUMMARY"].includes(element.tagName) ||
97
- element instanceof HTMLInputElement) {
112
+ element instanceof HTMLInputElement ||
113
+ tapRoles.has(role) ||
114
+ element.hasAttribute("onclick") ||
115
+ typeof htmlElement.onclick === "function") {
98
116
  actions.push("tap");
99
117
  }
100
118
  if (element instanceof HTMLInputElement ||
101
119
  element instanceof HTMLTextAreaElement ||
102
- element instanceof HTMLSelectElement) {
120
+ element instanceof HTMLSelectElement ||
121
+ htmlElement.isContentEditable) {
103
122
  actions.push("typeText", "focus");
104
123
  }
105
- else if (element.tabIndex >= 0) {
124
+ else if (htmlElement.tabIndex >= 0) {
106
125
  actions.push("focus");
107
126
  }
108
- if (element.scrollHeight >
109
- element.clientHeight) {
127
+ if (htmlElement.scrollHeight > htmlElement.clientHeight ||
128
+ htmlElement.scrollWidth > htmlElement.clientWidth) {
110
129
  actions.push("scroll", "swipe");
111
130
  }
112
131
  return [...new Set(actions)];
113
132
  }
133
+ function positiveFinite(value) {
134
+ const parsed = Number(value);
135
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
136
+ }
137
+ function createDomCoordinateSpace(browserWindow = typeof window === "undefined"
138
+ ? undefined
139
+ : window, documentElement = typeof document === "undefined"
140
+ ? null
141
+ : document.documentElement) {
142
+ const width = positiveFinite(browserWindow?.innerWidth) ??
143
+ positiveFinite(documentElement?.clientWidth);
144
+ const height = positiveFinite(browserWindow?.innerHeight) ??
145
+ positiveFinite(documentElement?.clientHeight);
146
+ if (width === undefined || height === undefined)
147
+ return undefined;
148
+ return { x: 0, y: 0, width, height, source: "dom.viewport" };
149
+ }
150
+ function normalizeDomAction(action) {
151
+ if (action === "tap")
152
+ return "click";
153
+ if (action === "typeText")
154
+ return "setValue";
155
+ return String(action ?? "");
156
+ }
114
157
  function computedColorToArgbHex(value) {
115
158
  const normalized = value.trim().toLowerCase();
116
159
  if (!normalized)
@@ -275,6 +318,7 @@ function installDomTools$1(registerTool, input = {}) {
275
318
  platform: "web",
276
319
  source: options.source,
277
320
  adapter: "@ansight/capacitor",
321
+ coordinateSpace: createDomCoordinateSpace(),
278
322
  capturedAtUtc: new Date().toISOString(),
279
323
  types: typeRegistry.types,
280
324
  truncated: limits.count >= limits.maxNodes,
@@ -309,6 +353,7 @@ function installDomTools$1(registerTool, input = {}) {
309
353
  platform: "web",
310
354
  source: options.source,
311
355
  adapter: "@ansight/capacitor",
356
+ coordinateSpace: createDomCoordinateSpace(),
312
357
  capturedAtUtc: new Date().toISOString(),
313
358
  types: typeRegistry.types,
314
359
  node,
@@ -347,7 +392,7 @@ function installDomTools$1(registerTool, input = {}) {
347
392
  registrations.push(registerTool({
348
393
  id: "dom.invoke_action",
349
394
  name: "Invoke DOM action",
350
- description: "Clicks, focuses, blurs, or changes a DOM node.",
395
+ description: "Taps, focuses, blurs, or enters text in a DOM node.",
351
396
  category: "UI",
352
397
  scope: "write",
353
398
  argumentsSchema: {
@@ -355,7 +400,10 @@ function installDomTools$1(registerTool, input = {}) {
355
400
  required: ["nodeId", "action"],
356
401
  properties: {
357
402
  nodeId: { type: "string" },
358
- action: { enum: ["click", "focus", "blur", "setValue"] },
403
+ action: {
404
+ type: "string",
405
+ enum: ["tap", "typeText", "click", "focus", "blur", "setValue"],
406
+ },
359
407
  value: { type: "string" },
360
408
  },
361
409
  },
@@ -372,17 +420,24 @@ function installDomTools$1(registerTool, input = {}) {
372
420
  errorCode: "dom_node_not_found",
373
421
  };
374
422
  }
375
- if (action === "click")
423
+ const normalizedAction = normalizeDomAction(action);
424
+ if (normalizedAction === "click")
376
425
  element.click();
377
- else if (action === "focus")
426
+ else if (normalizedAction === "focus")
378
427
  element.focus();
379
- else if (action === "blur")
428
+ else if (normalizedAction === "blur")
380
429
  element.blur();
381
- else if (action === "setValue" &&
430
+ else if (normalizedAction === "setValue" &&
382
431
  (element instanceof HTMLInputElement ||
383
432
  element instanceof HTMLTextAreaElement ||
384
- element instanceof HTMLSelectElement)) {
385
- element.value = value ?? "";
433
+ element instanceof HTMLSelectElement ||
434
+ element.isContentEditable)) {
435
+ if (element.isContentEditable) {
436
+ element.textContent = value ?? "";
437
+ }
438
+ else {
439
+ element.value = value ?? "";
440
+ }
386
441
  element.dispatchEvent(new Event("input", { bubbles: true }));
387
442
  element.dispatchEvent(new Event("change", { bubbles: true }));
388
443
  }
@@ -393,7 +448,7 @@ function installDomTools$1(registerTool, input = {}) {
393
448
  errorCode: "dom_action_unsupported",
394
449
  };
395
450
  }
396
- return successful({ nodeId: id, action }, `DOM action '${action}' invoked.`);
451
+ return successful({ nodeId: id, action, performedAction: normalizedAction }, `DOM action '${action}' invoked.`);
397
452
  }));
398
453
  }
399
454
  return {
@@ -565,6 +620,50 @@ class AnsightOptionsBuilder {
565
620
  };
566
621
  return this;
567
622
  }
623
+ withNetworkCapture(options = {}) {
624
+ this.options.networkCapture = { ...options };
625
+ return this;
626
+ }
627
+ withNetworkRequestBodies(maximumBodyBytes) {
628
+ if (typeof this.options.networkCapture !== "object")
629
+ return this;
630
+ const current = this.options.networkCapture;
631
+ this.options.networkCapture = {
632
+ ...current,
633
+ captureRequestBody: true,
634
+ ...(maximumBodyBytes == null ? {} : { maximumBodyBytes }),
635
+ };
636
+ return this;
637
+ }
638
+ withoutNetworkRequestBodies() {
639
+ if (typeof this.options.networkCapture !== "object")
640
+ return this;
641
+ const current = this.options.networkCapture;
642
+ this.options.networkCapture = { ...current, captureRequestBody: false };
643
+ return this;
644
+ }
645
+ withNetworkResponseBodies(maximumBodyBytes) {
646
+ if (typeof this.options.networkCapture !== "object")
647
+ return this;
648
+ const current = this.options.networkCapture;
649
+ this.options.networkCapture = {
650
+ ...current,
651
+ captureResponseBody: true,
652
+ ...(maximumBodyBytes == null ? {} : { maximumBodyBytes }),
653
+ };
654
+ return this;
655
+ }
656
+ withoutNetworkResponseBodies() {
657
+ if (typeof this.options.networkCapture !== "object")
658
+ return this;
659
+ const current = this.options.networkCapture;
660
+ this.options.networkCapture = { ...current, captureResponseBody: false };
661
+ return this;
662
+ }
663
+ withoutNetworkCapture() {
664
+ this.options.networkCapture = false;
665
+ return this;
666
+ }
568
667
  withToolGuard(toolGuard) {
569
668
  this.options.toolGuard = toolGuard;
570
669
  return this;
@@ -732,6 +831,871 @@ function createOptionsBuilder(options = {}) {
732
831
  return new AnsightOptionsBuilder(options);
733
832
  }
734
833
 
834
+ const networkRequestSchema = "ansight.network-request.v1";
835
+ const redactedNetworkValue = "<redacted>";
836
+ const maximumHeaderCount = 128;
837
+ const maximumHeaderValueLength = 4096;
838
+ const maximumErrorMessageLength = 4096;
839
+ const maximumUrlLength = 16384;
840
+ const defaultMaximumBodyBytes = 64 * 1024;
841
+ const sensitiveHeaderNames = new Set([
842
+ "authorization",
843
+ "cookie",
844
+ "proxy-authorization",
845
+ "set-cookie",
846
+ "x-api-key",
847
+ "x-auth-token",
848
+ ]);
849
+ const sensitiveQueryNames = new Set([
850
+ "access_token",
851
+ "accesskey",
852
+ "access_key",
853
+ "api_key",
854
+ "apikey",
855
+ "auth",
856
+ "authorization",
857
+ "client_secret",
858
+ "code",
859
+ "credential",
860
+ "credentials",
861
+ "id_token",
862
+ "jwt",
863
+ "key",
864
+ "password",
865
+ "passwd",
866
+ "refresh_token",
867
+ "sas",
868
+ "sastoken",
869
+ "secret",
870
+ "secret_key",
871
+ "security_token",
872
+ "session_token",
873
+ "sig",
874
+ "signature",
875
+ "token",
876
+ ]);
877
+ const azureSasFingerprintNames = new Set([
878
+ "se",
879
+ "skoid",
880
+ "sp",
881
+ "sr",
882
+ "srt",
883
+ "ss",
884
+ "sv",
885
+ ]);
886
+ const azureSasQueryNames = new Set([
887
+ "epk",
888
+ "erk",
889
+ "rscc",
890
+ "rscd",
891
+ "rsce",
892
+ "rscl",
893
+ "rsct",
894
+ "saoid",
895
+ "scid",
896
+ "se",
897
+ "sig",
898
+ "si",
899
+ "sip",
900
+ "ske",
901
+ "skoid",
902
+ "sks",
903
+ "skt",
904
+ "sktid",
905
+ "skv",
906
+ "snapshot",
907
+ "sp",
908
+ "spk",
909
+ "spr",
910
+ "sr",
911
+ "srk",
912
+ "srt",
913
+ "ss",
914
+ "st",
915
+ "suoid",
916
+ "tn",
917
+ "versionid",
918
+ "sv",
919
+ ]);
920
+ function truncate(value, maximumLength) {
921
+ const text = String(value);
922
+ return text.length <= maximumLength
923
+ ? text
924
+ : `${text.slice(0, maximumLength)}…`;
925
+ }
926
+ function normalizeRequired(value, fallback, maximumLength) {
927
+ const normalized = value == null ? "" : String(value).trim();
928
+ return truncate(normalized || fallback, maximumLength);
929
+ }
930
+ function normalizeOptional(value, maximumLength) {
931
+ if (value == null)
932
+ return undefined;
933
+ const normalized = String(value).trim();
934
+ return normalized ? truncate(normalized, maximumLength) : undefined;
935
+ }
936
+ function lowercaseSet(values) {
937
+ return new Set((values ?? []).map((value) => value.toLowerCase()));
938
+ }
939
+ function isSensitiveHeader(name, options) {
940
+ const lowered = name.toLowerCase();
941
+ if (sensitiveHeaderNames.has(lowered) ||
942
+ lowercaseSet(options.additionalSensitiveHeaderNames).has(lowered)) {
943
+ return true;
944
+ }
945
+ const compact = lowered.replaceAll("-", "");
946
+ return (compact.includes("token") ||
947
+ compact.includes("secret") ||
948
+ compact.includes("apikey"));
949
+ }
950
+ function headerEntries(headers) {
951
+ if (!headers)
952
+ return [];
953
+ if (Array.isArray(headers)) {
954
+ return headers.flatMap((header) => {
955
+ if (Array.isArray(header))
956
+ return [[header[0], header[1]]];
957
+ if (header && typeof header === "object") {
958
+ const value = header;
959
+ return [[value.name, value.value]];
960
+ }
961
+ return [];
962
+ });
963
+ }
964
+ if (typeof headers.forEach === "function") {
965
+ const entries = [];
966
+ headers.forEach((value, name) => entries.push([name, value]));
967
+ return entries;
968
+ }
969
+ return typeof headers === "object" ? Object.entries(headers) : [];
970
+ }
971
+ function sanitizeHeaders(headers, options) {
972
+ return headerEntries(headers)
973
+ .filter(([name]) => name != null && String(name).trim())
974
+ .slice(0, maximumHeaderCount)
975
+ .map(([rawName, rawValue]) => {
976
+ const name = normalizeRequired(rawName, "Header", 256);
977
+ return {
978
+ name,
979
+ value: isSensitiveHeader(name, options)
980
+ ? redactedNetworkValue
981
+ : normalizeRequired(rawValue, "", maximumHeaderValueLength),
982
+ };
983
+ });
984
+ }
985
+ function sanitizeQuery(query, options) {
986
+ const appSensitive = lowercaseSet(options.additionalSensitiveQueryParameterNames);
987
+ const pairs = query.split("&");
988
+ const decodedNames = new Set(pairs.map((pair) => decodeQueryName(pair).toLowerCase()));
989
+ const hasAzureSas = decodedNames.has("sig") &&
990
+ [...azureSasFingerprintNames].some((name) => decodedNames.has(name));
991
+ const hasAwsSignature = decodedNames.has("x-amz-signature");
992
+ const hasGoogleSignature = decodedNames.has("x-goog-signature");
993
+ const hasCloudFrontSignature = decodedNames.has("signature") &&
994
+ ["key-pair-id", "policy", "expires"].some((name) => decodedNames.has(name));
995
+ const hasLegacyGoogleSignature = decodedNames.has("signature") && decodedNames.has("googleaccessid");
996
+ const hasAlibabaSignature = (decodedNames.has("signature") && decodedNames.has("ossaccesskeyid")) ||
997
+ decodedNames.has("x-oss-signature");
998
+ return pairs
999
+ .map((pair) => {
1000
+ const equalsIndex = pair.indexOf("=");
1001
+ const encodedName = equalsIndex < 0 ? pair : pair.slice(0, equalsIndex);
1002
+ const decodedName = decodeQueryName(pair);
1003
+ const lowered = decodedName.toLowerCase();
1004
+ const providerSensitive = (hasAzureSas && azureSasQueryNames.has(lowered)) ||
1005
+ (hasAwsSignature && lowered.startsWith("x-amz-")) ||
1006
+ (hasGoogleSignature && lowered.startsWith("x-goog-")) ||
1007
+ (hasCloudFrontSignature &&
1008
+ [
1009
+ "signature",
1010
+ "key-pair-id",
1011
+ "policy",
1012
+ "expires",
1013
+ "hash-algorithm",
1014
+ ].includes(lowered)) ||
1015
+ (hasLegacyGoogleSignature &&
1016
+ ["signature", "googleaccessid", "expires"].includes(lowered)) ||
1017
+ (hasAlibabaSignature &&
1018
+ (lowered.startsWith("x-oss-") ||
1019
+ ["signature", "ossaccesskeyid", "security-token"].includes(lowered)));
1020
+ return providerSensitive ||
1021
+ sensitiveQueryNames.has(lowered) ||
1022
+ appSensitive.has(lowered)
1023
+ ? `${encodedName}=${encodeURIComponent(redactedNetworkValue)}`
1024
+ : pair;
1025
+ })
1026
+ .join("&");
1027
+ }
1028
+ function decodeQueryName(pair) {
1029
+ const equalsIndex = pair.indexOf("=");
1030
+ const encodedName = equalsIndex < 0 ? pair : pair.slice(0, equalsIndex);
1031
+ try {
1032
+ return decodeURIComponent(encodedName.replaceAll("+", " "));
1033
+ }
1034
+ catch {
1035
+ return encodedName;
1036
+ }
1037
+ }
1038
+ function sanitizeUrl(value, options) {
1039
+ let normalized = normalizeRequired(value, "<unknown>", maximumUrlLength);
1040
+ normalized = normalized.replace(/^(https?:\/\/)[^/@]+@/i, `$1${redactedNetworkValue}@`);
1041
+ const queryIndex = normalized.indexOf("?");
1042
+ if (queryIndex < 0)
1043
+ return truncate(normalized, maximumUrlLength);
1044
+ const fragmentIndex = normalized.indexOf("#", queryIndex);
1045
+ if (options.includeQueryString === false) {
1046
+ return truncate(normalized.slice(0, queryIndex) +
1047
+ (fragmentIndex < 0 ? "" : normalized.slice(fragmentIndex)), maximumUrlLength);
1048
+ }
1049
+ const queryEnd = fragmentIndex < 0 ? normalized.length : fragmentIndex;
1050
+ return truncate(normalized.slice(0, queryIndex + 1) +
1051
+ sanitizeQuery(normalized.slice(queryIndex + 1, queryEnd), options) +
1052
+ (fragmentIndex < 0 ? "" : normalized.slice(fragmentIndex)), maximumUrlLength);
1053
+ }
1054
+ function sanitizeErrorMessage(value, options) {
1055
+ const normalized = normalizeOptional(value, maximumErrorMessageLength);
1056
+ if (!normalized)
1057
+ return undefined;
1058
+ return truncate(normalized
1059
+ .replace(/(access_token|api_key|apikey|auth|authorization|code|key|password|passwd|secret|signature|token)(\s*=\s*)([^&\s,;]+)/gi, `$1$2${redactedNetworkValue}`)
1060
+ .replace(/https?:\/\/[^\s"'<>]+/gi, (url) => sanitizeUrl(url, options)), maximumErrorMessageLength);
1061
+ }
1062
+ function normalizeTimestamp(value, fallback) {
1063
+ const date = new Date(value == null ? fallback : String(value));
1064
+ return Number.isFinite(date.valueOf()) ? date.toISOString() : fallback;
1065
+ }
1066
+ function generateId(globalObject) {
1067
+ if (typeof globalObject.crypto?.randomUUID === "function") {
1068
+ return globalObject.crypto.randomUUID().replaceAll("-", "");
1069
+ }
1070
+ return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
1071
+ }
1072
+ function normalizeSize(value) {
1073
+ const number = Number(value);
1074
+ return Number.isFinite(number) && number >= 0
1075
+ ? Math.round(number)
1076
+ : undefined;
1077
+ }
1078
+ function maximumBodyBytes(options) {
1079
+ const configured = Number(options.maximumBodyBytes);
1080
+ const value = Number.isFinite(configured)
1081
+ ? Math.round(configured)
1082
+ : defaultMaximumBodyBytes;
1083
+ return Math.max(0, value);
1084
+ }
1085
+ function sanitizeSensitiveText(value, options) {
1086
+ return value
1087
+ .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}`)
1088
+ .replace(/https?:\/\/[^\s"'<>]+/gi, (url) => sanitizeUrl(url, options));
1089
+ }
1090
+ function truncateUtf8(bytes, maximum) {
1091
+ let length = Math.min(bytes.length, maximum);
1092
+ const decoder = new TextDecoder("utf-8", { fatal: true });
1093
+ while (length > 0) {
1094
+ try {
1095
+ decoder.decode(bytes.slice(0, length));
1096
+ return bytes.slice(0, length);
1097
+ }
1098
+ catch {
1099
+ length -= 1;
1100
+ }
1101
+ }
1102
+ return new Uint8Array();
1103
+ }
1104
+ function bytesToBase64$1(bytes) {
1105
+ let binary = "";
1106
+ for (const byte of bytes)
1107
+ binary += String.fromCharCode(byte);
1108
+ return btoa(binary);
1109
+ }
1110
+ function base64ToBytes(value) {
1111
+ const binary = atob(value);
1112
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
1113
+ }
1114
+ function normalizeBody(body, options) {
1115
+ const maximum = maximumBodyBytes(options);
1116
+ if (!body || maximum <= 0)
1117
+ return undefined;
1118
+ const encoding = body.encoding?.toLowerCase();
1119
+ let bytes;
1120
+ try {
1121
+ if (encoding === "utf8") {
1122
+ bytes = new TextEncoder().encode(sanitizeSensitiveText(body.data, options));
1123
+ }
1124
+ else if (encoding === "base64" && options.captureBinaryBodies === true) {
1125
+ bytes = base64ToBytes(body.data);
1126
+ }
1127
+ else {
1128
+ return undefined;
1129
+ }
1130
+ }
1131
+ catch {
1132
+ return undefined;
1133
+ }
1134
+ const originalLength = bytes.length;
1135
+ const captured = encoding === "utf8"
1136
+ ? truncateUtf8(bytes, maximum)
1137
+ : bytes.slice(0, maximum);
1138
+ const totalBytes = normalizeSize(body.totalBytes);
1139
+ return {
1140
+ contentType: normalizeOptional(body.contentType, 512),
1141
+ encoding,
1142
+ data: encoding === "base64"
1143
+ ? bytesToBase64$1(captured)
1144
+ : new TextDecoder().decode(captured),
1145
+ capturedBytes: captured.length,
1146
+ totalBytes,
1147
+ truncated: body.truncated ||
1148
+ originalLength > captured.length ||
1149
+ (totalBytes != null && totalBytes > captured.length),
1150
+ };
1151
+ }
1152
+ function normalizeRecord(input, options, globalObject) {
1153
+ const now = new Date().toISOString();
1154
+ const startedAtUtc = normalizeTimestamp(input.startedAtUtc, now);
1155
+ const completedAtUtc = normalizeTimestamp(input.completedAtUtc, startedAtUtc);
1156
+ const duration = Number(input.durationMilliseconds);
1157
+ return {
1158
+ schema: networkRequestSchema,
1159
+ id: normalizeRequired(input.id, generateId(globalObject), 128),
1160
+ source: normalizeRequired(input.source, "unknown", 128),
1161
+ startedAtUtc,
1162
+ completedAtUtc: completedAtUtc < startedAtUtc ? startedAtUtc : completedAtUtc,
1163
+ durationMilliseconds: Number.isFinite(duration) && duration >= 0 ? duration : 0,
1164
+ method: normalizeRequired(input.method, "GET", 32).toUpperCase(),
1165
+ url: sanitizeUrl(input.url, options),
1166
+ protocol: normalizeOptional(input.protocol, 64),
1167
+ requestHeaders: options.includeRequestHeaders === false
1168
+ ? []
1169
+ : sanitizeHeaders(input.requestHeaders, options),
1170
+ requestBodySizeBytes: options.includeBodySizes === false
1171
+ ? undefined
1172
+ : normalizeSize(input.requestBodySizeBytes),
1173
+ requestBody: options.captureRequestBody !== false
1174
+ ? normalizeBody(input.requestBody, options)
1175
+ : undefined,
1176
+ statusCode: Number.isInteger(Number(input.statusCode)) &&
1177
+ Number(input.statusCode) >= 100 &&
1178
+ Number(input.statusCode) <= 999
1179
+ ? Number(input.statusCode)
1180
+ : undefined,
1181
+ reasonPhrase: normalizeOptional(input.reasonPhrase, 512),
1182
+ responseHeaders: options.includeResponseHeaders === false
1183
+ ? []
1184
+ : sanitizeHeaders(input.responseHeaders, options),
1185
+ responseBodySizeBytes: options.includeBodySizes === false
1186
+ ? undefined
1187
+ : normalizeSize(input.responseBodySizeBytes),
1188
+ responseBody: options.captureResponseBody !== false
1189
+ ? normalizeBody(input.responseBody, options)
1190
+ : undefined,
1191
+ errorType: normalizeOptional(input.errorType, 512),
1192
+ errorMessage: sanitizeErrorMessage(input.errorMessage, options),
1193
+ };
1194
+ }
1195
+ function sanitizeNetworkRequest(input, options = {}, globalObject = globalThis) {
1196
+ try {
1197
+ let normalized = normalizeRecord(input, options, globalObject);
1198
+ if (options.urlSanitizer) {
1199
+ normalized = normalizeRecord({ ...normalized, url: options.urlSanitizer(normalized.url) }, options, globalObject);
1200
+ }
1201
+ if (options.requestSanitizer) {
1202
+ const transformed = options.requestSanitizer(normalized);
1203
+ if (transformed == null)
1204
+ return null;
1205
+ normalized = normalizeRecord(transformed, options, globalObject);
1206
+ }
1207
+ return normalized;
1208
+ }
1209
+ catch {
1210
+ return null;
1211
+ }
1212
+ }
1213
+ function parseContentLength(headers) {
1214
+ const entry = headerEntries(headers).find(([name]) => String(name).toLowerCase() === "content-length");
1215
+ return entry ? normalizeSize(entry[1]) : undefined;
1216
+ }
1217
+ function headerValue(headers, wantedName) {
1218
+ const entry = headerEntries(headers).find(([name]) => String(name).toLowerCase() === wantedName);
1219
+ return entry == null ? undefined : String(entry[1]);
1220
+ }
1221
+ function isTextContentType(contentType) {
1222
+ if (!contentType)
1223
+ return true;
1224
+ const mediaType = contentType.split(";", 1)[0].trim().toLowerCase();
1225
+ return (mediaType.startsWith("text/") ||
1226
+ mediaType.endsWith("+json") ||
1227
+ mediaType.endsWith("+xml") ||
1228
+ [
1229
+ "application/json",
1230
+ "application/xml",
1231
+ "application/graphql",
1232
+ "application/javascript",
1233
+ "application/x-www-form-urlencoded",
1234
+ ].includes(mediaType));
1235
+ }
1236
+ function bodyFromBytes(bytes, totalBytes, contentType, options) {
1237
+ const binary = !isTextContentType(contentType);
1238
+ if (binary && options.captureBinaryBodies !== true)
1239
+ return undefined;
1240
+ const maximum = maximumBodyBytes(options);
1241
+ if (maximum <= 0)
1242
+ return undefined;
1243
+ const captured = binary
1244
+ ? bytes.slice(0, maximum)
1245
+ : truncateUtf8(bytes, maximum);
1246
+ return {
1247
+ contentType,
1248
+ encoding: binary ? "base64" : "utf8",
1249
+ data: binary ? bytesToBase64$1(captured) : new TextDecoder().decode(captured),
1250
+ capturedBytes: captured.length,
1251
+ totalBytes,
1252
+ truncated: bytes.length > captured.length ||
1253
+ (totalBytes != null && totalBytes > captured.length),
1254
+ };
1255
+ }
1256
+ function bodyFromValue(value, headers, options) {
1257
+ if (value == null || options.captureRequestBody === false)
1258
+ return undefined;
1259
+ const contentType = headerValue(headers, "content-type");
1260
+ if (typeof value === "string" || value instanceof URLSearchParams) {
1261
+ const bytes = new TextEncoder().encode(String(value));
1262
+ return bodyFromBytes(bytes, bytes.length, contentType ||
1263
+ (value instanceof URLSearchParams
1264
+ ? "application/x-www-form-urlencoded"
1265
+ : undefined), options);
1266
+ }
1267
+ if (value instanceof ArrayBuffer) {
1268
+ const bytes = new Uint8Array(value);
1269
+ return bodyFromBytes(bytes, bytes.length, contentType, options);
1270
+ }
1271
+ if (ArrayBuffer.isView(value)) {
1272
+ const bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
1273
+ return bodyFromBytes(bytes, bytes.length, contentType, options);
1274
+ }
1275
+ return undefined;
1276
+ }
1277
+ async function bodyFromFetchResponse(response, headers, options, shouldContinue = () => true) {
1278
+ if (!shouldContinue() || options.captureResponseBody === false)
1279
+ return undefined;
1280
+ const contentType = headerValue(headers, "content-type");
1281
+ if (!isTextContentType(contentType) && options.captureBinaryBodies !== true) {
1282
+ return undefined;
1283
+ }
1284
+ const totalBytes = parseContentLength(headers);
1285
+ const maximum = maximumBodyBytes(options);
1286
+ if (maximum <= 0)
1287
+ return undefined;
1288
+ const clone = response.clone();
1289
+ if (clone.body) {
1290
+ const reader = clone.body.getReader();
1291
+ const chunks = [];
1292
+ let capturedLength = 0;
1293
+ let observedLength = 0;
1294
+ try {
1295
+ while (capturedLength <= maximum) {
1296
+ if (!shouldContinue()) {
1297
+ await reader.cancel().catch(() => undefined);
1298
+ return undefined;
1299
+ }
1300
+ const result = await reader.read();
1301
+ if (!shouldContinue()) {
1302
+ await reader.cancel().catch(() => undefined);
1303
+ return undefined;
1304
+ }
1305
+ if (result.done)
1306
+ break;
1307
+ const chunk = result.value;
1308
+ observedLength += chunk.length;
1309
+ const remaining = maximum - capturedLength;
1310
+ if (remaining > 0) {
1311
+ const kept = chunk.slice(0, remaining);
1312
+ chunks.push(kept);
1313
+ capturedLength += kept.length;
1314
+ }
1315
+ if (observedLength > maximum) {
1316
+ await reader.cancel().catch(() => undefined);
1317
+ break;
1318
+ }
1319
+ }
1320
+ }
1321
+ finally {
1322
+ reader.releaseLock();
1323
+ }
1324
+ const joined = new Uint8Array(capturedLength);
1325
+ let offset = 0;
1326
+ for (const chunk of chunks) {
1327
+ joined.set(chunk, offset);
1328
+ offset += chunk.length;
1329
+ }
1330
+ return bodyFromBytes(joined, totalBytes ?? observedLength, contentType, options);
1331
+ }
1332
+ if (totalBytes == null || totalBytes > maximum)
1333
+ return undefined;
1334
+ const bytes = new Uint8Array(await clone.arrayBuffer());
1335
+ if (!shouldContinue())
1336
+ return undefined;
1337
+ return bodyFromBytes(bytes, totalBytes, contentType, options);
1338
+ }
1339
+ function parseXhrResponseHeaders(value) {
1340
+ return value
1341
+ .trim()
1342
+ .split(/[\r\n]+/)
1343
+ .flatMap((line) => {
1344
+ const separator = line.indexOf(":");
1345
+ return separator < 0
1346
+ ? []
1347
+ : [
1348
+ {
1349
+ name: line.slice(0, separator),
1350
+ value: line.slice(separator + 1),
1351
+ },
1352
+ ];
1353
+ });
1354
+ }
1355
+ function monotonicNow(globalObject) {
1356
+ return typeof globalObject.performance?.now === "function"
1357
+ ? globalObject.performance.now()
1358
+ : Date.now();
1359
+ }
1360
+ function installBrowserNetworkCapture(capture, options = {}, sourcePrefix = "capacitor", globalObject = globalThis) {
1361
+ const cleanups = [];
1362
+ let active = true;
1363
+ let fetchInvocationDepth = 0;
1364
+ if (options.captureFetch !== false &&
1365
+ typeof globalObject.fetch === "function") {
1366
+ const originalFetch = globalObject.fetch;
1367
+ const wrappedFetch = function (input, init) {
1368
+ const startedAtUtc = new Date().toISOString();
1369
+ const started = monotonicNow(globalObject);
1370
+ const inputRequest = typeof input === "object" && "headers" in input && "method" in input
1371
+ ? input
1372
+ : undefined;
1373
+ const requestHeaders = headerEntries(inputRequest?.headers).concat(headerEntries(init?.headers));
1374
+ const requestBody = bodyFromValue(init?.body, requestHeaders, options);
1375
+ const method = init?.method || inputRequest?.method || "GET";
1376
+ const url = typeof input === "string" ? input : inputRequest?.url || String(input);
1377
+ let promise;
1378
+ fetchInvocationDepth += 1;
1379
+ try {
1380
+ promise = originalFetch(input, init);
1381
+ }
1382
+ catch (error) {
1383
+ fetchInvocationDepth -= 1;
1384
+ const request = sanitizeNetworkRequest({
1385
+ source: `${sourcePrefix}.fetch`,
1386
+ startedAtUtc,
1387
+ completedAtUtc: new Date().toISOString(),
1388
+ durationMilliseconds: monotonicNow(globalObject) - started,
1389
+ method,
1390
+ url,
1391
+ requestHeaders: sanitizeHeaders(requestHeaders, {}),
1392
+ requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
1393
+ requestBody,
1394
+ errorType: error instanceof Error ? error.name : "Error",
1395
+ errorMessage: error instanceof Error ? error.message : String(error),
1396
+ }, options, globalObject);
1397
+ if (active && request)
1398
+ Promise.resolve(capture(request)).catch(() => undefined);
1399
+ throw error;
1400
+ }
1401
+ fetchInvocationDepth -= 1;
1402
+ return promise.then((response) => {
1403
+ if (!active)
1404
+ return response;
1405
+ const responseHeaders = headerEntries(response.headers);
1406
+ const responseRecord = {
1407
+ source: `${sourcePrefix}.fetch`,
1408
+ startedAtUtc,
1409
+ completedAtUtc: new Date().toISOString(),
1410
+ durationMilliseconds: monotonicNow(globalObject) - started,
1411
+ method,
1412
+ url: response.url || url,
1413
+ requestHeaders: sanitizeHeaders(requestHeaders, {}),
1414
+ requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
1415
+ requestBody,
1416
+ statusCode: response.status,
1417
+ reasonPhrase: response.statusText,
1418
+ responseHeaders: sanitizeHeaders(responseHeaders, {}),
1419
+ responseBodySizeBytes: parseContentLength(responseHeaders),
1420
+ };
1421
+ return bodyFromFetchResponse(response, responseHeaders, options, () => active).then((responseBody) => {
1422
+ const request = sanitizeNetworkRequest({
1423
+ ...responseRecord,
1424
+ responseBody,
1425
+ responseBodySizeBytes: responseRecord.responseBodySizeBytes ??
1426
+ responseBody?.totalBytes,
1427
+ }, options, globalObject);
1428
+ if (active && request)
1429
+ void Promise.resolve(capture(request)).catch(() => undefined);
1430
+ return response;
1431
+ }, () => {
1432
+ const request = sanitizeNetworkRequest(responseRecord, options, globalObject);
1433
+ if (active && request)
1434
+ void Promise.resolve(capture(request)).catch(() => undefined);
1435
+ return response;
1436
+ });
1437
+ }, (error) => {
1438
+ const request = sanitizeNetworkRequest({
1439
+ source: `${sourcePrefix}.fetch`,
1440
+ startedAtUtc,
1441
+ completedAtUtc: new Date().toISOString(),
1442
+ durationMilliseconds: monotonicNow(globalObject) - started,
1443
+ method,
1444
+ url,
1445
+ requestHeaders: sanitizeHeaders(requestHeaders, {}),
1446
+ requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
1447
+ requestBody,
1448
+ errorType: error instanceof Error ? error.name : "Error",
1449
+ errorMessage: error instanceof Error ? error.message : String(error),
1450
+ }, options, globalObject);
1451
+ if (active && request)
1452
+ Promise.resolve(capture(request)).catch(() => undefined);
1453
+ throw error;
1454
+ });
1455
+ };
1456
+ globalObject.fetch = wrappedFetch;
1457
+ cleanups.push(() => {
1458
+ if (globalObject.fetch === wrappedFetch)
1459
+ globalObject.fetch = originalFetch;
1460
+ });
1461
+ }
1462
+ const Xhr = globalObject.XMLHttpRequest;
1463
+ if (options.captureXmlHttpRequest !== false && Xhr?.prototype) {
1464
+ const states = new WeakMap();
1465
+ const prototype = Xhr.prototype;
1466
+ const originalOpen = prototype.open;
1467
+ const originalSend = prototype.send;
1468
+ const originalSetRequestHeader = prototype.setRequestHeader;
1469
+ const wrappedOpen = function (method, url, ...rest) {
1470
+ states.set(this, {
1471
+ method,
1472
+ url: String(url),
1473
+ requestHeaders: [],
1474
+ suppressed: fetchInvocationDepth > 0,
1475
+ });
1476
+ Reflect.apply(originalOpen, this, [method, url, ...rest]);
1477
+ };
1478
+ const wrappedSetRequestHeader = function (name, value) {
1479
+ states.get(this)?.requestHeaders.push({ name, value });
1480
+ Reflect.apply(originalSetRequestHeader, this, [name, value]);
1481
+ };
1482
+ const wrappedSend = function (body) {
1483
+ const state = states.get(this);
1484
+ if (!state || state.suppressed) {
1485
+ Reflect.apply(originalSend, this, [body]);
1486
+ return;
1487
+ }
1488
+ state.startedAtUtc = new Date().toISOString();
1489
+ state.started = monotonicNow(globalObject);
1490
+ state.requestBody = bodyFromValue(body, state.requestHeaders, options);
1491
+ let failure;
1492
+ const markFailure = (event) => {
1493
+ failure = event.type;
1494
+ };
1495
+ const complete = () => {
1496
+ if (!active)
1497
+ return;
1498
+ let responseHeaders = [];
1499
+ try {
1500
+ responseHeaders = parseXhrResponseHeaders(this.getAllResponseHeaders());
1501
+ }
1502
+ catch {
1503
+ // Some WebViews throw before response headers exist.
1504
+ }
1505
+ let responseBody;
1506
+ try {
1507
+ const responseType = this.responseType || "text";
1508
+ const responseOptions = {
1509
+ ...options,
1510
+ captureRequestBody: options.captureResponseBody,
1511
+ };
1512
+ if (responseType === "text") {
1513
+ responseBody = bodyFromValue(this.responseText, responseHeaders, responseOptions);
1514
+ }
1515
+ else if (responseType === "arraybuffer") {
1516
+ responseBody = bodyFromValue(this.response, responseHeaders, responseOptions);
1517
+ }
1518
+ }
1519
+ catch {
1520
+ // Response data is not readable for every XHR response type.
1521
+ }
1522
+ const request = sanitizeNetworkRequest({
1523
+ source: `${sourcePrefix}.xhr`,
1524
+ startedAtUtc: state.startedAtUtc,
1525
+ completedAtUtc: new Date().toISOString(),
1526
+ durationMilliseconds: monotonicNow(globalObject) - (state.started ?? 0),
1527
+ method: state.method,
1528
+ url: this.responseURL || state.url,
1529
+ requestHeaders: state.requestHeaders,
1530
+ requestBodySizeBytes: parseContentLength(state.requestHeaders) ??
1531
+ state.requestBody?.totalBytes,
1532
+ requestBody: state.requestBody,
1533
+ statusCode: this.status || undefined,
1534
+ reasonPhrase: this.statusText,
1535
+ responseHeaders,
1536
+ responseBodySizeBytes: parseContentLength(responseHeaders) ?? responseBody?.totalBytes,
1537
+ responseBody,
1538
+ errorType: failure,
1539
+ errorMessage: failure ? `XMLHttpRequest ${failure}` : undefined,
1540
+ }, options, globalObject);
1541
+ if (request)
1542
+ Promise.resolve(capture(request)).catch(() => undefined);
1543
+ };
1544
+ this.addEventListener("error", markFailure);
1545
+ this.addEventListener("abort", markFailure);
1546
+ this.addEventListener("timeout", markFailure);
1547
+ this.addEventListener("loadend", complete, { once: true });
1548
+ Reflect.apply(originalSend, this, [body]);
1549
+ };
1550
+ prototype.open = wrappedOpen;
1551
+ prototype.setRequestHeader = wrappedSetRequestHeader;
1552
+ prototype.send = wrappedSend;
1553
+ cleanups.push(() => {
1554
+ if (prototype.open === wrappedOpen)
1555
+ prototype.open = originalOpen;
1556
+ if (prototype.send === wrappedSend)
1557
+ prototype.send = originalSend;
1558
+ if (prototype.setRequestHeader === wrappedSetRequestHeader) {
1559
+ prototype.setRequestHeader = originalSetRequestHeader;
1560
+ }
1561
+ });
1562
+ }
1563
+ let removed = false;
1564
+ return {
1565
+ remove() {
1566
+ if (removed)
1567
+ return;
1568
+ removed = true;
1569
+ active = false;
1570
+ for (const cleanup of cleanups.reverse())
1571
+ cleanup();
1572
+ },
1573
+ };
1574
+ }
1575
+
1576
+ const ANSIGHT_CAPACITOR_SDK_VERSION = "1.3.0-preview.12";
1577
+ const COMPILED_CAPACITOR_CORE_VERSION = "8.4.2";
1578
+ const CAPACITOR_GROUP = "capacitor";
1579
+ const LOCALIZATION_GROUP = "localization";
1580
+ function normalized(value) {
1581
+ if (value == null)
1582
+ return undefined;
1583
+ const result = String(value).trim();
1584
+ return result || undefined;
1585
+ }
1586
+ function canonicalizeLocale(value) {
1587
+ const locale = normalized(value)?.replace(/_/g, "-");
1588
+ if (!locale)
1589
+ return undefined;
1590
+ try {
1591
+ return Intl.getCanonicalLocales(locale)[0] ?? locale;
1592
+ }
1593
+ catch {
1594
+ return locale;
1595
+ }
1596
+ }
1597
+ function parseLocale(locale) {
1598
+ const parts = (locale ?? "").split("-").filter(Boolean);
1599
+ const language = parts[0]?.toLowerCase();
1600
+ const region = parts.find((part, index) => index > 0 && (/^[A-Za-z]{2}$/.test(part) || /^\d{3}$/.test(part)));
1601
+ return { language, region: region?.toUpperCase() };
1602
+ }
1603
+ function webViewDetails(platform, nativePlatform, userAgent) {
1604
+ const agent = userAgent ?? "";
1605
+ if (nativePlatform && platform === "ios") {
1606
+ return {
1607
+ engine: "wkWebView",
1608
+ version: /AppleWebKit\/([^\s]+)/.exec(agent)?.[1],
1609
+ };
1610
+ }
1611
+ if (nativePlatform && platform === "android") {
1612
+ return {
1613
+ engine: "chromiumWebView",
1614
+ version: /(?:Chrome|Chromium)\/([^\s]+)/.exec(agent)?.[1],
1615
+ };
1616
+ }
1617
+ if (/Firefox\/([^\s]+)/.test(agent)) {
1618
+ return { engine: "gecko", version: /Firefox\/([^\s]+)/.exec(agent)?.[1] };
1619
+ }
1620
+ if (/(?:Chrome|Chromium)\/([^\s]+)/.test(agent)) {
1621
+ return {
1622
+ engine: "chromium",
1623
+ version: /(?:Chrome|Chromium)\/([^\s]+)/.exec(agent)?.[1],
1624
+ };
1625
+ }
1626
+ if (/AppleWebKit\/([^\s]+)/.test(agent)) {
1627
+ return {
1628
+ engine: "webkit",
1629
+ version: /AppleWebKit\/([^\s]+)/.exec(agent)?.[1],
1630
+ };
1631
+ }
1632
+ return { engine: "unknown" };
1633
+ }
1634
+ function currentCapacitorSessionEnvironment(platform, nativePlatform) {
1635
+ let resolved;
1636
+ try {
1637
+ resolved = Intl.DateTimeFormat().resolvedOptions();
1638
+ }
1639
+ catch {
1640
+ resolved = undefined;
1641
+ }
1642
+ return {
1643
+ platform,
1644
+ nativePlatform,
1645
+ userAgent: typeof navigator === "undefined" ? undefined : navigator.userAgent,
1646
+ locale: resolved?.locale ??
1647
+ (typeof navigator === "undefined" ? undefined : navigator.language),
1648
+ timeZone: resolved?.timeZone,
1649
+ utcOffsetMinutes: -new Date().getTimezoneOffset(),
1650
+ };
1651
+ }
1652
+ function createAutomaticSessionProperties(environment) {
1653
+ const platform = normalized(environment.platform) ?? "unknown";
1654
+ const userAgent = normalized(environment.userAgent);
1655
+ const webView = webViewDetails(platform, environment.nativePlatform, userAgent);
1656
+ const locale = canonicalizeLocale(environment.locale);
1657
+ const parsedLocale = parseLocale(locale);
1658
+ const capacitor = {
1659
+ sdkVersion: ANSIGHT_CAPACITOR_SDK_VERSION,
1660
+ capacitorVersion: "8.x",
1661
+ compiledCapacitorVersion: COMPILED_CAPACITOR_CORE_VERSION,
1662
+ platform,
1663
+ runtimeLanguage: "javascript",
1664
+ executionMode: environment.nativePlatform ? "native" : "web",
1665
+ webViewEngine: webView.engine,
1666
+ };
1667
+ if (webView.version)
1668
+ capacitor.webViewEngineVersion = webView.version;
1669
+ if (userAgent)
1670
+ capacitor.userAgent = userAgent;
1671
+ const localization = {
1672
+ utcOffsetMinutes: String(environment.utcOffsetMinutes ?? -new Date().getTimezoneOffset()),
1673
+ };
1674
+ if (locale)
1675
+ localization.locale = locale;
1676
+ if (parsedLocale.language)
1677
+ localization.language = parsedLocale.language;
1678
+ if (parsedLocale.region)
1679
+ localization.region = parsedLocale.region;
1680
+ if (normalized(environment.timeZone)) {
1681
+ localization.timeZone = String(environment.timeZone).trim();
1682
+ }
1683
+ return {
1684
+ [CAPACITOR_GROUP]: capacitor,
1685
+ [LOCALIZATION_GROUP]: localization,
1686
+ };
1687
+ }
1688
+ function mergeSessionProperties(automaticProperties, customProperties) {
1689
+ const merged = Object.fromEntries(Object.entries(automaticProperties).map(([group, properties]) => [
1690
+ group,
1691
+ { ...properties },
1692
+ ]));
1693
+ for (const [group, properties] of Object.entries(customProperties ?? {})) {
1694
+ merged[group] = { ...(merged[group] ?? {}), ...properties };
1695
+ }
1696
+ return merged;
1697
+ }
1698
+
735
1699
  const AnsightNative = core.registerPlugin("Ansight");
736
1700
  const toolHandlers = new Map();
737
1701
  const artifactProviders = new Map();
@@ -742,6 +1706,9 @@ let logListener;
742
1706
  let lifecycleCleanup;
743
1707
  let artifactToolRegistrations = [];
744
1708
  let domToolRegistration;
1709
+ let networkCaptureSubscription;
1710
+ let networkCaptureRegistration;
1711
+ let networkConnectionListener;
745
1712
  function normalizePairingPayload(payload) {
746
1713
  if (payload == null)
747
1714
  return payload;
@@ -749,11 +1716,19 @@ function normalizePairingPayload(payload) {
749
1716
  }
750
1717
  function normalizeOptions(input) {
751
1718
  const options = JSON.parse(JSON.stringify(input));
1719
+ options.customProperties = mergeSessionProperties(automaticSessionProperties(), options.customProperties);
752
1720
  delete options.domTools;
753
1721
  delete options.errorCapture;
754
1722
  delete options.lifecycle;
1723
+ delete options.networkCapture;
755
1724
  return options;
756
1725
  }
1726
+ function automaticSessionProperties() {
1727
+ return createAutomaticSessionProperties(currentCapacitorSessionEnvironment(core.Capacitor.getPlatform(), core.Capacitor.isNativePlatform()));
1728
+ }
1729
+ function automaticSessionPropertyValue(group, key) {
1730
+ return automaticSessionProperties()[group]?.[key];
1731
+ }
757
1732
  function normalizeToolResult(value) {
758
1733
  if (value && typeof value === "object" && "success" in value) {
759
1734
  return value;
@@ -803,11 +1778,13 @@ async function emitHostConnectionStatus() {
803
1778
  }
804
1779
  async function afterConnectionChange(operation) {
805
1780
  const result = await operation();
1781
+ await refreshNetworkCaptureConnection();
806
1782
  await emitHostConnectionStatus();
807
1783
  return result;
808
1784
  }
809
1785
  async function initialize(options = {}) {
810
1786
  const result = await AnsightNative.initialize(normalizeOptions(options));
1787
+ await configureNetworkCapture(options.networkCapture);
811
1788
  if (options.lifecycle !== false)
812
1789
  startLifecycleTracking();
813
1790
  if (options.errorCapture) {
@@ -821,6 +1798,7 @@ async function initialize(options = {}) {
821
1798
  }
822
1799
  async function initializeAndActivate(options = {}) {
823
1800
  const result = await AnsightNative.initializeAndActivate(normalizeOptions(options));
1801
+ await configureNetworkCapture(options.networkCapture);
824
1802
  if (options.lifecycle !== false)
825
1803
  startLifecycleTracking();
826
1804
  if (options.errorCapture) {
@@ -845,6 +1823,80 @@ function event(input) {
845
1823
  return AnsightNative.recordEvent(typeof input === "string" ? { label: input } : input);
846
1824
  }
847
1825
  const recordEvent = event;
1826
+ async function recordNetworkRequest(input, sanitizationOptions = {}) {
1827
+ const request = sanitizeNetworkRequest(input, sanitizationOptions);
1828
+ if (!request) {
1829
+ return {
1830
+ success: false,
1831
+ message: "Network request capture was suppressed by the sanitizer.",
1832
+ };
1833
+ }
1834
+ return AnsightNative.recordNetworkRequest(request);
1835
+ }
1836
+ function installNetworkCapture(options = {}) {
1837
+ uninstallNetworkCapture();
1838
+ const registration = { options };
1839
+ networkCaptureRegistration = registration;
1840
+ ensureNetworkConnectionListener();
1841
+ void refreshNetworkCaptureConnection();
1842
+ return {
1843
+ remove() {
1844
+ if (networkCaptureRegistration === registration) {
1845
+ uninstallNetworkCapture();
1846
+ }
1847
+ },
1848
+ };
1849
+ }
1850
+ function uninstallNetworkCapture() {
1851
+ networkCaptureRegistration = undefined;
1852
+ const listener = networkConnectionListener;
1853
+ networkConnectionListener = undefined;
1854
+ if (listener)
1855
+ void listener.then((value) => value.remove());
1856
+ detachNetworkCapture();
1857
+ }
1858
+ function detachNetworkCapture() {
1859
+ networkCaptureSubscription?.remove();
1860
+ networkCaptureSubscription = undefined;
1861
+ }
1862
+ async function refreshNetworkCaptureConnection() {
1863
+ const registration = networkCaptureRegistration;
1864
+ if (!registration) {
1865
+ detachNetworkCapture();
1866
+ return;
1867
+ }
1868
+ try {
1869
+ const status = await AnsightNative.hostConnectionStatus();
1870
+ if (networkCaptureRegistration !== registration)
1871
+ return;
1872
+ applyNetworkConnectionStatus(status);
1873
+ }
1874
+ catch {
1875
+ if (networkCaptureRegistration === registration)
1876
+ detachNetworkCapture();
1877
+ }
1878
+ }
1879
+ function ensureNetworkConnectionListener() {
1880
+ networkConnectionListener ??= AnsightNative.addListener("ansightHostConnectionStatus", applyNetworkConnectionStatus);
1881
+ }
1882
+ function applyNetworkConnectionStatus(status) {
1883
+ const registration = networkCaptureRegistration;
1884
+ if (!registration || status.isConnected !== true) {
1885
+ detachNetworkCapture();
1886
+ return;
1887
+ }
1888
+ networkCaptureSubscription ??= installBrowserNetworkCapture((request) => AnsightNative.recordNetworkRequest(request), registration.options);
1889
+ }
1890
+ async function configureNetworkCapture(value) {
1891
+ uninstallNetworkCapture();
1892
+ if (!value)
1893
+ return;
1894
+ networkCaptureRegistration = {
1895
+ options: typeof value === "object" ? value : {},
1896
+ };
1897
+ ensureNetworkConnectionListener();
1898
+ await refreshNetworkCaptureConnection();
1899
+ }
848
1900
  const recordCrashCandidate = (input) => AnsightNative.recordCrashCandidate(input);
849
1901
  const screenViewed = (name, details = {}) => AnsightNative.screenViewed({ name, details });
850
1902
  const trackRoute = screenViewed;
@@ -900,12 +1952,25 @@ const disableFramesPerSecond = () => AnsightNative.disableFramesPerSecond();
900
1952
  const captureScreenFrame = (options = {}) => AnsightNative.captureScreenFrame(options);
901
1953
  const enableTouchCapture = () => AnsightNative.enableTouchCapture();
902
1954
  const disableTouchCapture = () => AnsightNative.disableTouchCapture();
903
- const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({ properties });
1955
+ const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({
1956
+ properties: mergeSessionProperties(automaticSessionProperties(), properties),
1957
+ });
904
1958
  const updateCustomProperties = updateSessionProperties;
905
- const clearSessionProperties = () => AnsightNative.clearSessionProperties();
1959
+ const clearSessionProperties = () => AnsightNative.updateSessionProperties({
1960
+ properties: automaticSessionProperties(),
1961
+ });
906
1962
  const clearCustomProperties = clearSessionProperties;
907
1963
  const registerCustomProperty = (group, key, value) => AnsightNative.registerCustomProperty({ group, key, value });
908
- const removeCustomProperty = (group, key) => AnsightNative.removeCustomProperty({ group, key });
1964
+ const removeCustomProperty = (group, key) => {
1965
+ const automaticValue = automaticSessionPropertyValue(group, key);
1966
+ return automaticValue == null
1967
+ ? AnsightNative.removeCustomProperty({ group, key })
1968
+ : AnsightNative.registerCustomProperty({
1969
+ group,
1970
+ key,
1971
+ value: automaticValue,
1972
+ });
1973
+ };
909
1974
  function addHostConnectionStatusListener(listener, options = {}) {
910
1975
  hostConnectionListeners.add(listener);
911
1976
  if (options.emitCurrent !== false)
@@ -1256,6 +2321,10 @@ const Ansight = {
1256
2321
  recordMetric,
1257
2322
  event,
1258
2323
  recordEvent,
2324
+ recordNetworkRequest,
2325
+ installNetworkCapture,
2326
+ uninstallNetworkCapture,
2327
+ sanitizeNetworkRequest,
1259
2328
  screenViewed,
1260
2329
  trackRoute,
1261
2330
  setAppLifecycleState,
@@ -1353,6 +2422,7 @@ exports.initialize = initialize;
1353
2422
  exports.initializeAndActivate = initializeAndActivate;
1354
2423
  exports.installDomTools = installDomTools;
1355
2424
  exports.installErrorHandlers = installErrorHandlers;
2425
+ exports.installNetworkCapture = installNetworkCapture;
1356
2426
  exports.isFramesPerSecondEnabled = isFramesPerSecondEnabled;
1357
2427
  exports.listRegisteredArtifactProviders = listRegisteredArtifactProviders;
1358
2428
  exports.listRegisteredTools = listRegisteredTools;
@@ -1362,6 +2432,7 @@ exports.openSession = openSession;
1362
2432
  exports.recordCrashCandidate = recordCrashCandidate;
1363
2433
  exports.recordEvent = recordEvent;
1364
2434
  exports.recordMetric = recordMetric;
2435
+ exports.recordNetworkRequest = recordNetworkRequest;
1365
2436
  exports.recordedEvents = recordedEvents;
1366
2437
  exports.recordedMetrics = recordedMetrics;
1367
2438
  exports.registerArtifactProvider = registerArtifactProvider;
@@ -1370,6 +2441,7 @@ exports.registerCustomProperty = registerCustomProperty;
1370
2441
  exports.registerMetricChannel = registerMetricChannel;
1371
2442
  exports.registerTool = registerTool;
1372
2443
  exports.removeCustomProperty = removeCustomProperty;
2444
+ exports.sanitizeNetworkRequest = sanitizeNetworkRequest;
1373
2445
  exports.savePairingConfig = savePairingConfig;
1374
2446
  exports.scanPairingQrCode = scanPairingQrCode;
1375
2447
  exports.screenViewed = screenViewed;
@@ -1383,6 +2455,7 @@ exports.stopAppStateTracking = stopAppStateTracking;
1383
2455
  exports.stopLifecycleTracking = stopLifecycleTracking;
1384
2456
  exports.trackRoute = trackRoute;
1385
2457
  exports.uninstallDomTools = uninstallDomTools;
2458
+ exports.uninstallNetworkCapture = uninstallNetworkCapture;
1386
2459
  exports.unregisterArtifactProvider = unregisterArtifactProvider;
1387
2460
  exports.unregisterTool = unregisterTool;
1388
2461
  exports.updateCustomProperties = updateCustomProperties;