@ansight/capacitor 1.3.0-preview.1 → 1.3.0-preview.11

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