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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1203,6 +1203,50 @@
1203
1203
  };
1204
1204
  return this;
1205
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
+ }
1206
1250
  withToolGuard(toolGuard) {
1207
1251
  this.options.toolGuard = toolGuard;
1208
1252
  return this;
@@ -1370,6 +1414,871 @@
1370
1414
  return new AnsightOptionsBuilder(options);
1371
1415
  }
1372
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
+
1373
2282
  const AnsightNative = registerPlugin("Ansight");
1374
2283
  const toolHandlers = new Map();
1375
2284
  const artifactProviders = new Map();
@@ -1380,6 +2289,9 @@
1380
2289
  let lifecycleCleanup;
1381
2290
  let artifactToolRegistrations = [];
1382
2291
  let domToolRegistration;
2292
+ let networkCaptureSubscription;
2293
+ let networkCaptureRegistration;
2294
+ let networkConnectionListener;
1383
2295
  function normalizePairingPayload(payload) {
1384
2296
  if (payload == null)
1385
2297
  return payload;
@@ -1387,11 +2299,19 @@
1387
2299
  }
1388
2300
  function normalizeOptions(input) {
1389
2301
  const options = JSON.parse(JSON.stringify(input));
2302
+ options.customProperties = mergeSessionProperties(automaticSessionProperties(), options.customProperties);
1390
2303
  delete options.domTools;
1391
2304
  delete options.errorCapture;
1392
2305
  delete options.lifecycle;
2306
+ delete options.networkCapture;
1393
2307
  return options;
1394
2308
  }
2309
+ function automaticSessionProperties() {
2310
+ return createAutomaticSessionProperties(currentCapacitorSessionEnvironment(Capacitor.getPlatform(), Capacitor.isNativePlatform()));
2311
+ }
2312
+ function automaticSessionPropertyValue(group, key) {
2313
+ return automaticSessionProperties()[group]?.[key];
2314
+ }
1395
2315
  function normalizeToolResult(value) {
1396
2316
  if (value && typeof value === "object" && "success" in value) {
1397
2317
  return value;
@@ -1441,11 +2361,13 @@
1441
2361
  }
1442
2362
  async function afterConnectionChange(operation) {
1443
2363
  const result = await operation();
2364
+ await refreshNetworkCaptureConnection();
1444
2365
  await emitHostConnectionStatus();
1445
2366
  return result;
1446
2367
  }
1447
2368
  async function initialize(options = {}) {
1448
2369
  const result = await AnsightNative.initialize(normalizeOptions(options));
2370
+ await configureNetworkCapture(options.networkCapture);
1449
2371
  if (options.lifecycle !== false)
1450
2372
  startLifecycleTracking();
1451
2373
  if (options.errorCapture) {
@@ -1459,6 +2381,7 @@
1459
2381
  }
1460
2382
  async function initializeAndActivate(options = {}) {
1461
2383
  const result = await AnsightNative.initializeAndActivate(normalizeOptions(options));
2384
+ await configureNetworkCapture(options.networkCapture);
1462
2385
  if (options.lifecycle !== false)
1463
2386
  startLifecycleTracking();
1464
2387
  if (options.errorCapture) {
@@ -1483,6 +2406,80 @@
1483
2406
  return AnsightNative.recordEvent(typeof input === "string" ? { label: input } : input);
1484
2407
  }
1485
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
+ }
1486
2483
  const recordCrashCandidate = (input) => AnsightNative.recordCrashCandidate(input);
1487
2484
  const screenViewed = (name, details = {}) => AnsightNative.screenViewed({ name, details });
1488
2485
  const trackRoute = screenViewed;
@@ -1538,12 +2535,25 @@
1538
2535
  const captureScreenFrame = (options = {}) => AnsightNative.captureScreenFrame(options);
1539
2536
  const enableTouchCapture = () => AnsightNative.enableTouchCapture();
1540
2537
  const disableTouchCapture = () => AnsightNative.disableTouchCapture();
1541
- const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({ properties });
2538
+ const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({
2539
+ properties: mergeSessionProperties(automaticSessionProperties(), properties),
2540
+ });
1542
2541
  const updateCustomProperties = updateSessionProperties;
1543
- const clearSessionProperties = () => AnsightNative.clearSessionProperties();
2542
+ const clearSessionProperties = () => AnsightNative.updateSessionProperties({
2543
+ properties: automaticSessionProperties(),
2544
+ });
1544
2545
  const clearCustomProperties = clearSessionProperties;
1545
2546
  const registerCustomProperty = (group, key, value) => AnsightNative.registerCustomProperty({ group, key, value });
1546
- 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
+ };
1547
2557
  function addHostConnectionStatusListener(listener, options = {}) {
1548
2558
  hostConnectionListeners.add(listener);
1549
2559
  if (options.emitCurrent !== false)
@@ -1894,6 +2904,10 @@
1894
2904
  recordMetric,
1895
2905
  event,
1896
2906
  recordEvent,
2907
+ recordNetworkRequest,
2908
+ installNetworkCapture,
2909
+ uninstallNetworkCapture,
2910
+ sanitizeNetworkRequest,
1897
2911
  screenViewed,
1898
2912
  trackRoute,
1899
2913
  setAppLifecycleState,