@ansight/capacitor 1.3.0-preview.9 → 1.4.0-preview.1

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