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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,18 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var core = require('@capacitor/core');
6
6
 
7
+ function createTypeRegistry() {
8
+ return { idsByTypeName: new Map(), types: [] };
9
+ }
10
+ function registerType(registry, typeName) {
11
+ const existingTypeId = registry.idsByTypeName.get(typeName);
12
+ if (existingTypeId !== undefined)
13
+ return existingTypeId;
14
+ const typeId = registry.types.length;
15
+ registry.types.push(typeName);
16
+ registry.idsByTypeName.set(typeName, typeId);
17
+ return typeId;
18
+ }
7
19
  const nodeIds = new WeakMap();
8
20
  let nextNodeId = 1;
9
21
  function nodeId(element) {
@@ -50,6 +62,55 @@ function automationId(element) {
50
62
  element.id ??
51
63
  undefined)?.trim() || undefined);
52
64
  }
65
+ function semanticRole(element) {
66
+ const declared = element.getAttribute("role")?.trim().toLowerCase();
67
+ if (declared)
68
+ return declared;
69
+ if (element instanceof HTMLButtonElement)
70
+ return "button";
71
+ if (element instanceof HTMLInputElement) {
72
+ if (element.type === "checkbox")
73
+ return "checkbox";
74
+ if (element.type === "radio")
75
+ return "radio";
76
+ if (element.type === "range")
77
+ return "slider";
78
+ if (["button", "submit", "reset"].includes(element.type))
79
+ return "button";
80
+ return "textbox";
81
+ }
82
+ if (element instanceof HTMLTextAreaElement)
83
+ return "textbox";
84
+ if (element instanceof HTMLSelectElement)
85
+ return "combobox";
86
+ if (element.tagName === "A")
87
+ return "link";
88
+ if (/^H[1-6]$/.test(element.tagName))
89
+ return "heading";
90
+ return "view";
91
+ }
92
+ function supportedActions(element, allowActions) {
93
+ if (!allowActions)
94
+ return [];
95
+ const actions = [];
96
+ if (["A", "BUTTON", "SUMMARY"].includes(element.tagName) ||
97
+ element instanceof HTMLInputElement) {
98
+ actions.push("tap");
99
+ }
100
+ if (element instanceof HTMLInputElement ||
101
+ element instanceof HTMLTextAreaElement ||
102
+ element instanceof HTMLSelectElement) {
103
+ actions.push("typeText", "focus");
104
+ }
105
+ else if (element.tabIndex >= 0) {
106
+ actions.push("focus");
107
+ }
108
+ if (element.scrollHeight >
109
+ element.clientHeight) {
110
+ actions.push("scroll", "swipe");
111
+ }
112
+ return [...new Set(actions)];
113
+ }
53
114
  function computedColorToArgbHex(value) {
54
115
  const normalized = value.trim().toLowerCase();
55
116
  if (!normalized)
@@ -115,7 +176,7 @@ function displayedValue(element) {
115
176
  const normalized = value?.replace(/\s+/g, " ").trim();
116
177
  return normalized ? normalized.slice(0, 240) : undefined;
117
178
  }
118
- function captureNode(element, options, depth, limits) {
179
+ function captureNode(element, options, depth, limits, typeRegistry) {
119
180
  if (limits.count >= limits.maxNodes || depth > limits.maxDepth)
120
181
  return null;
121
182
  const style = getComputedStyle(element);
@@ -125,17 +186,23 @@ function captureNode(element, options, depth, limits) {
125
186
  return null;
126
187
  limits.count += 1;
127
188
  const children = Array.from(element.children)
128
- .map((child) => captureNode(child, options, depth + 1, limits))
189
+ .map((child) => captureNode(child, options, depth + 1, limits, typeRegistry))
129
190
  .filter((child) => child !== null);
130
191
  const htmlElement = element;
131
192
  const disabled = element.hasAttribute("disabled") ||
132
193
  element.getAttribute("aria-disabled") === "true";
133
194
  const parsedOpacity = Number.parseFloat(style.opacity);
134
- return {
195
+ const parsedZIndex = Number.parseFloat(style.zIndex);
196
+ const actions = supportedActions(element, options.allowActions);
197
+ const label = accessibleLabel(element, options.includeText);
198
+ const node = {
135
199
  id: nodeId(element),
136
- type: element.tagName.toLowerCase(),
200
+ typeId: registerType(typeRegistry, element.tagName.toLowerCase()),
137
201
  automationId: automationId(element),
138
- label: accessibleLabel(element, options.includeText),
202
+ label,
203
+ role: semanticRole(element),
204
+ supportedActions: actions,
205
+ interactable: visible && !disabled && actions.length > 0,
139
206
  visible,
140
207
  enabled: !disabled,
141
208
  focusable: htmlElement.tabIndex >= 0 ||
@@ -160,12 +227,15 @@ function captureNode(element, options, depth, limits) {
160
227
  id: element.id || undefined,
161
228
  role: element.getAttribute("role") ?? undefined,
162
229
  className: element.getAttribute("class") ?? undefined,
163
- value: options.includeText ? displayedValue(element) : undefined,
164
230
  checked: element instanceof HTMLInputElement ? element.checked : undefined,
165
231
  attributes: options.includeAttributes ? attributes(element) : undefined,
166
232
  },
167
233
  children,
168
234
  };
235
+ if (Number.isFinite(parsedZIndex) && parsedZIndex !== 0) {
236
+ node.z = parsedZIndex;
237
+ }
238
+ return node;
169
239
  }
170
240
  function findElement(id) {
171
241
  return Array.from(document.querySelectorAll("*")).find((element) => nodeId(element) === id);
@@ -198,12 +268,15 @@ function installDomTools$1(registerTool, input = {}) {
198
268
  maxNodes: options.maxNodes,
199
269
  count: 0,
200
270
  };
201
- const tree = captureNode(root, options, 0, limits);
271
+ const typeRegistry = createTypeRegistry();
272
+ const tree = captureNode(root, options, 0, limits, typeRegistry);
202
273
  return successful({
274
+ format: "ansight.dom.visual-tree.compact.v2",
203
275
  platform: "web",
204
276
  source: options.source,
205
277
  adapter: "@ansight/capacitor",
206
278
  capturedAtUtc: new Date().toISOString(),
279
+ types: typeRegistry.types,
207
280
  truncated: limits.count >= limits.maxNodes,
208
281
  root: tree,
209
282
  }, "DOM document captured.");
@@ -229,7 +302,17 @@ function installDomTools$1(registerTool, input = {}) {
229
302
  };
230
303
  }
231
304
  const limits = { maxDepth: 0, maxNodes: 1, count: 0 };
232
- return successful(captureNode(element, options, 0, limits), "DOM node captured.");
305
+ const typeRegistry = createTypeRegistry();
306
+ const node = captureNode(element, options, 0, limits, typeRegistry);
307
+ return successful({
308
+ format: "ansight.dom.visual-tree.compact.v2",
309
+ platform: "web",
310
+ source: options.source,
311
+ adapter: "@ansight/capacitor",
312
+ capturedAtUtc: new Date().toISOString(),
313
+ types: typeRegistry.types,
314
+ node,
315
+ }, "DOM node captured.");
233
316
  }),
234
317
  registerTool({
235
318
  id: "dom.query_selector",
@@ -336,6 +419,8 @@ class AnsightOptionsBuilder {
336
419
  retentionPeriodSeconds: 120,
337
420
  enableFramesPerSecond: true,
338
421
  enableBatteryLevel: false,
422
+ enableOpenFileHandleTracking: false,
423
+ enableJniReferenceCountTracking: false,
339
424
  sessionJpegCapture: {
340
425
  intervalMilliseconds: 2000,
341
426
  quality: 60,
@@ -378,6 +463,22 @@ class AnsightOptionsBuilder {
378
463
  this.options.enableBatteryLevel = false;
379
464
  return this;
380
465
  }
466
+ withOpenFileHandleTracking() {
467
+ this.options.enableOpenFileHandleTracking = true;
468
+ return this;
469
+ }
470
+ withoutOpenFileHandleTracking() {
471
+ this.options.enableOpenFileHandleTracking = false;
472
+ return this;
473
+ }
474
+ withJniReferenceCountTracking() {
475
+ this.options.enableJniReferenceCountTracking = true;
476
+ return this;
477
+ }
478
+ withoutJniReferenceCountTracking() {
479
+ this.options.enableJniReferenceCountTracking = false;
480
+ return this;
481
+ }
381
482
  withRetentionPeriodSeconds(value) {
382
483
  this.options.retentionPeriodSeconds = value;
383
484
  return this;
@@ -416,7 +517,7 @@ class AnsightOptionsBuilder {
416
517
  this.options.defaultMemoryChannels = current;
417
518
  return this;
418
519
  }
419
- withSessionJpegCapture(optionsOrIntervalMilliseconds = {}, quality = 60, maxWidth = 480, captureGpuBackedSurfaces = true, mode = "screenshotOnly") {
520
+ withSessionJpegCapture(optionsOrIntervalMilliseconds = {}, quality = 60, maxWidth = 480, captureGpuBackedSurfaces = true, mode = "screenshotOnly", captureKeyboardPresence = false) {
420
521
  this.options.sessionJpegCapture =
421
522
  typeof optionsOrIntervalMilliseconds === "number"
422
523
  ? {
@@ -425,11 +526,13 @@ class AnsightOptionsBuilder {
425
526
  maxWidth,
426
527
  captureGpuBackedSurfaces,
427
528
  mode,
529
+ captureKeyboardPresence,
428
530
  }
429
531
  : {
430
532
  intervalMilliseconds: 2000,
431
533
  quality: 60,
432
534
  maxWidth: 480,
535
+ captureKeyboardPresence: false,
433
536
  mode: "screenshotOnly",
434
537
  ...optionsOrIntervalMilliseconds,
435
538
  };
@@ -447,6 +550,14 @@ class AnsightOptionsBuilder {
447
550
  this.options.touchCapture = false;
448
551
  return this;
449
552
  }
553
+ withCrashCapture(options = {}) {
554
+ this.options.crashCapture = { ...options, enabled: true };
555
+ return this;
556
+ }
557
+ withoutCrashCapture() {
558
+ this.options.crashCapture = false;
559
+ return this;
560
+ }
450
561
  withLifecycleCapture(options = {}) {
451
562
  this.options.lifecycleCapture = {
452
563
  ...options,
@@ -454,6 +565,50 @@ class AnsightOptionsBuilder {
454
565
  };
455
566
  return this;
456
567
  }
568
+ withNetworkCapture(options = {}) {
569
+ this.options.networkCapture = { ...options };
570
+ return this;
571
+ }
572
+ withNetworkRequestBodies(maximumBodyBytes) {
573
+ if (typeof this.options.networkCapture !== "object")
574
+ return this;
575
+ const current = this.options.networkCapture;
576
+ this.options.networkCapture = {
577
+ ...current,
578
+ captureRequestBody: true,
579
+ ...(maximumBodyBytes == null ? {} : { maximumBodyBytes }),
580
+ };
581
+ return this;
582
+ }
583
+ withoutNetworkRequestBodies() {
584
+ if (typeof this.options.networkCapture !== "object")
585
+ return this;
586
+ const current = this.options.networkCapture;
587
+ this.options.networkCapture = { ...current, captureRequestBody: false };
588
+ return this;
589
+ }
590
+ withNetworkResponseBodies(maximumBodyBytes) {
591
+ if (typeof this.options.networkCapture !== "object")
592
+ return this;
593
+ const current = this.options.networkCapture;
594
+ this.options.networkCapture = {
595
+ ...current,
596
+ captureResponseBody: true,
597
+ ...(maximumBodyBytes == null ? {} : { maximumBodyBytes }),
598
+ };
599
+ return this;
600
+ }
601
+ withoutNetworkResponseBodies() {
602
+ if (typeof this.options.networkCapture !== "object")
603
+ return this;
604
+ const current = this.options.networkCapture;
605
+ this.options.networkCapture = { ...current, captureResponseBody: false };
606
+ return this;
607
+ }
608
+ withoutNetworkCapture() {
609
+ this.options.networkCapture = false;
610
+ return this;
611
+ }
457
612
  withToolGuard(toolGuard) {
458
613
  this.options.toolGuard = toolGuard;
459
614
  return this;
@@ -621,6 +776,871 @@ function createOptionsBuilder(options = {}) {
621
776
  return new AnsightOptionsBuilder(options);
622
777
  }
623
778
 
779
+ const networkRequestSchema = "ansight.network-request.v1";
780
+ const redactedNetworkValue = "<redacted>";
781
+ const maximumHeaderCount = 128;
782
+ const maximumHeaderValueLength = 4096;
783
+ const maximumErrorMessageLength = 4096;
784
+ const maximumUrlLength = 16384;
785
+ const defaultMaximumBodyBytes = 64 * 1024;
786
+ const sensitiveHeaderNames = new Set([
787
+ "authorization",
788
+ "cookie",
789
+ "proxy-authorization",
790
+ "set-cookie",
791
+ "x-api-key",
792
+ "x-auth-token",
793
+ ]);
794
+ const sensitiveQueryNames = new Set([
795
+ "access_token",
796
+ "accesskey",
797
+ "access_key",
798
+ "api_key",
799
+ "apikey",
800
+ "auth",
801
+ "authorization",
802
+ "client_secret",
803
+ "code",
804
+ "credential",
805
+ "credentials",
806
+ "id_token",
807
+ "jwt",
808
+ "key",
809
+ "password",
810
+ "passwd",
811
+ "refresh_token",
812
+ "sas",
813
+ "sastoken",
814
+ "secret",
815
+ "secret_key",
816
+ "security_token",
817
+ "session_token",
818
+ "sig",
819
+ "signature",
820
+ "token",
821
+ ]);
822
+ const azureSasFingerprintNames = new Set([
823
+ "se",
824
+ "skoid",
825
+ "sp",
826
+ "sr",
827
+ "srt",
828
+ "ss",
829
+ "sv",
830
+ ]);
831
+ const azureSasQueryNames = new Set([
832
+ "epk",
833
+ "erk",
834
+ "rscc",
835
+ "rscd",
836
+ "rsce",
837
+ "rscl",
838
+ "rsct",
839
+ "saoid",
840
+ "scid",
841
+ "se",
842
+ "sig",
843
+ "si",
844
+ "sip",
845
+ "ske",
846
+ "skoid",
847
+ "sks",
848
+ "skt",
849
+ "sktid",
850
+ "skv",
851
+ "snapshot",
852
+ "sp",
853
+ "spk",
854
+ "spr",
855
+ "sr",
856
+ "srk",
857
+ "srt",
858
+ "ss",
859
+ "st",
860
+ "suoid",
861
+ "tn",
862
+ "versionid",
863
+ "sv",
864
+ ]);
865
+ function truncate(value, maximumLength) {
866
+ const text = String(value);
867
+ return text.length <= maximumLength
868
+ ? text
869
+ : `${text.slice(0, maximumLength)}…`;
870
+ }
871
+ function normalizeRequired(value, fallback, maximumLength) {
872
+ const normalized = value == null ? "" : String(value).trim();
873
+ return truncate(normalized || fallback, maximumLength);
874
+ }
875
+ function normalizeOptional(value, maximumLength) {
876
+ if (value == null)
877
+ return undefined;
878
+ const normalized = String(value).trim();
879
+ return normalized ? truncate(normalized, maximumLength) : undefined;
880
+ }
881
+ function lowercaseSet(values) {
882
+ return new Set((values ?? []).map((value) => value.toLowerCase()));
883
+ }
884
+ function isSensitiveHeader(name, options) {
885
+ const lowered = name.toLowerCase();
886
+ if (sensitiveHeaderNames.has(lowered) ||
887
+ lowercaseSet(options.additionalSensitiveHeaderNames).has(lowered)) {
888
+ return true;
889
+ }
890
+ const compact = lowered.replaceAll("-", "");
891
+ return (compact.includes("token") ||
892
+ compact.includes("secret") ||
893
+ compact.includes("apikey"));
894
+ }
895
+ function headerEntries(headers) {
896
+ if (!headers)
897
+ return [];
898
+ if (Array.isArray(headers)) {
899
+ return headers.flatMap((header) => {
900
+ if (Array.isArray(header))
901
+ return [[header[0], header[1]]];
902
+ if (header && typeof header === "object") {
903
+ const value = header;
904
+ return [[value.name, value.value]];
905
+ }
906
+ return [];
907
+ });
908
+ }
909
+ if (typeof headers.forEach === "function") {
910
+ const entries = [];
911
+ headers.forEach((value, name) => entries.push([name, value]));
912
+ return entries;
913
+ }
914
+ return typeof headers === "object" ? Object.entries(headers) : [];
915
+ }
916
+ function sanitizeHeaders(headers, options) {
917
+ return headerEntries(headers)
918
+ .filter(([name]) => name != null && String(name).trim())
919
+ .slice(0, maximumHeaderCount)
920
+ .map(([rawName, rawValue]) => {
921
+ const name = normalizeRequired(rawName, "Header", 256);
922
+ return {
923
+ name,
924
+ value: isSensitiveHeader(name, options)
925
+ ? redactedNetworkValue
926
+ : normalizeRequired(rawValue, "", maximumHeaderValueLength),
927
+ };
928
+ });
929
+ }
930
+ function sanitizeQuery(query, options) {
931
+ const appSensitive = lowercaseSet(options.additionalSensitiveQueryParameterNames);
932
+ const pairs = query.split("&");
933
+ const decodedNames = new Set(pairs.map((pair) => decodeQueryName(pair).toLowerCase()));
934
+ const hasAzureSas = decodedNames.has("sig") &&
935
+ [...azureSasFingerprintNames].some((name) => decodedNames.has(name));
936
+ const hasAwsSignature = decodedNames.has("x-amz-signature");
937
+ const hasGoogleSignature = decodedNames.has("x-goog-signature");
938
+ const hasCloudFrontSignature = decodedNames.has("signature") &&
939
+ ["key-pair-id", "policy", "expires"].some((name) => decodedNames.has(name));
940
+ const hasLegacyGoogleSignature = decodedNames.has("signature") && decodedNames.has("googleaccessid");
941
+ const hasAlibabaSignature = (decodedNames.has("signature") && decodedNames.has("ossaccesskeyid")) ||
942
+ decodedNames.has("x-oss-signature");
943
+ return pairs
944
+ .map((pair) => {
945
+ const equalsIndex = pair.indexOf("=");
946
+ const encodedName = equalsIndex < 0 ? pair : pair.slice(0, equalsIndex);
947
+ const decodedName = decodeQueryName(pair);
948
+ const lowered = decodedName.toLowerCase();
949
+ const providerSensitive = (hasAzureSas && azureSasQueryNames.has(lowered)) ||
950
+ (hasAwsSignature && lowered.startsWith("x-amz-")) ||
951
+ (hasGoogleSignature && lowered.startsWith("x-goog-")) ||
952
+ (hasCloudFrontSignature &&
953
+ [
954
+ "signature",
955
+ "key-pair-id",
956
+ "policy",
957
+ "expires",
958
+ "hash-algorithm",
959
+ ].includes(lowered)) ||
960
+ (hasLegacyGoogleSignature &&
961
+ ["signature", "googleaccessid", "expires"].includes(lowered)) ||
962
+ (hasAlibabaSignature &&
963
+ (lowered.startsWith("x-oss-") ||
964
+ ["signature", "ossaccesskeyid", "security-token"].includes(lowered)));
965
+ return providerSensitive ||
966
+ sensitiveQueryNames.has(lowered) ||
967
+ appSensitive.has(lowered)
968
+ ? `${encodedName}=${encodeURIComponent(redactedNetworkValue)}`
969
+ : pair;
970
+ })
971
+ .join("&");
972
+ }
973
+ function decodeQueryName(pair) {
974
+ const equalsIndex = pair.indexOf("=");
975
+ const encodedName = equalsIndex < 0 ? pair : pair.slice(0, equalsIndex);
976
+ try {
977
+ return decodeURIComponent(encodedName.replaceAll("+", " "));
978
+ }
979
+ catch {
980
+ return encodedName;
981
+ }
982
+ }
983
+ function sanitizeUrl(value, options) {
984
+ let normalized = normalizeRequired(value, "<unknown>", maximumUrlLength);
985
+ normalized = normalized.replace(/^(https?:\/\/)[^/@]+@/i, `$1${redactedNetworkValue}@`);
986
+ const queryIndex = normalized.indexOf("?");
987
+ if (queryIndex < 0)
988
+ return truncate(normalized, maximumUrlLength);
989
+ const fragmentIndex = normalized.indexOf("#", queryIndex);
990
+ if (options.includeQueryString === false) {
991
+ return truncate(normalized.slice(0, queryIndex) +
992
+ (fragmentIndex < 0 ? "" : normalized.slice(fragmentIndex)), maximumUrlLength);
993
+ }
994
+ const queryEnd = fragmentIndex < 0 ? normalized.length : fragmentIndex;
995
+ return truncate(normalized.slice(0, queryIndex + 1) +
996
+ sanitizeQuery(normalized.slice(queryIndex + 1, queryEnd), options) +
997
+ (fragmentIndex < 0 ? "" : normalized.slice(fragmentIndex)), maximumUrlLength);
998
+ }
999
+ function sanitizeErrorMessage(value, options) {
1000
+ const normalized = normalizeOptional(value, maximumErrorMessageLength);
1001
+ if (!normalized)
1002
+ return undefined;
1003
+ return truncate(normalized
1004
+ .replace(/(access_token|api_key|apikey|auth|authorization|code|key|password|passwd|secret|signature|token)(\s*=\s*)([^&\s,;]+)/gi, `$1$2${redactedNetworkValue}`)
1005
+ .replace(/https?:\/\/[^\s"'<>]+/gi, (url) => sanitizeUrl(url, options)), maximumErrorMessageLength);
1006
+ }
1007
+ function normalizeTimestamp(value, fallback) {
1008
+ const date = new Date(value == null ? fallback : String(value));
1009
+ return Number.isFinite(date.valueOf()) ? date.toISOString() : fallback;
1010
+ }
1011
+ function generateId(globalObject) {
1012
+ if (typeof globalObject.crypto?.randomUUID === "function") {
1013
+ return globalObject.crypto.randomUUID().replaceAll("-", "");
1014
+ }
1015
+ return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
1016
+ }
1017
+ function normalizeSize(value) {
1018
+ const number = Number(value);
1019
+ return Number.isFinite(number) && number >= 0
1020
+ ? Math.round(number)
1021
+ : undefined;
1022
+ }
1023
+ function maximumBodyBytes(options) {
1024
+ const configured = Number(options.maximumBodyBytes);
1025
+ const value = Number.isFinite(configured)
1026
+ ? Math.round(configured)
1027
+ : defaultMaximumBodyBytes;
1028
+ return Math.max(0, value);
1029
+ }
1030
+ function sanitizeSensitiveText(value, options) {
1031
+ return value
1032
+ .replace(/(access_token|accesskey|access_key|api_key|apikey|auth|authorization|client_secret|code|credential|credentials|id_token|jwt|key|password|passwd|refresh_token|sas|sastoken|secret|secret_key|security_token|session_token|sig|signature|token)(["']?\s*[:=]\s*["']?)([^&\s,;}"']+)/gi, `$1$2${redactedNetworkValue}`)
1033
+ .replace(/https?:\/\/[^\s"'<>]+/gi, (url) => sanitizeUrl(url, options));
1034
+ }
1035
+ function truncateUtf8(bytes, maximum) {
1036
+ let length = Math.min(bytes.length, maximum);
1037
+ const decoder = new TextDecoder("utf-8", { fatal: true });
1038
+ while (length > 0) {
1039
+ try {
1040
+ decoder.decode(bytes.slice(0, length));
1041
+ return bytes.slice(0, length);
1042
+ }
1043
+ catch {
1044
+ length -= 1;
1045
+ }
1046
+ }
1047
+ return new Uint8Array();
1048
+ }
1049
+ function bytesToBase64$1(bytes) {
1050
+ let binary = "";
1051
+ for (const byte of bytes)
1052
+ binary += String.fromCharCode(byte);
1053
+ return btoa(binary);
1054
+ }
1055
+ function base64ToBytes(value) {
1056
+ const binary = atob(value);
1057
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
1058
+ }
1059
+ function normalizeBody(body, options) {
1060
+ const maximum = maximumBodyBytes(options);
1061
+ if (!body || maximum <= 0)
1062
+ return undefined;
1063
+ const encoding = body.encoding?.toLowerCase();
1064
+ let bytes;
1065
+ try {
1066
+ if (encoding === "utf8") {
1067
+ bytes = new TextEncoder().encode(sanitizeSensitiveText(body.data, options));
1068
+ }
1069
+ else if (encoding === "base64" && options.captureBinaryBodies === true) {
1070
+ bytes = base64ToBytes(body.data);
1071
+ }
1072
+ else {
1073
+ return undefined;
1074
+ }
1075
+ }
1076
+ catch {
1077
+ return undefined;
1078
+ }
1079
+ const originalLength = bytes.length;
1080
+ const captured = encoding === "utf8"
1081
+ ? truncateUtf8(bytes, maximum)
1082
+ : bytes.slice(0, maximum);
1083
+ const totalBytes = normalizeSize(body.totalBytes);
1084
+ return {
1085
+ contentType: normalizeOptional(body.contentType, 512),
1086
+ encoding,
1087
+ data: encoding === "base64"
1088
+ ? bytesToBase64$1(captured)
1089
+ : new TextDecoder().decode(captured),
1090
+ capturedBytes: captured.length,
1091
+ totalBytes,
1092
+ truncated: body.truncated ||
1093
+ originalLength > captured.length ||
1094
+ (totalBytes != null && totalBytes > captured.length),
1095
+ };
1096
+ }
1097
+ function normalizeRecord(input, options, globalObject) {
1098
+ const now = new Date().toISOString();
1099
+ const startedAtUtc = normalizeTimestamp(input.startedAtUtc, now);
1100
+ const completedAtUtc = normalizeTimestamp(input.completedAtUtc, startedAtUtc);
1101
+ const duration = Number(input.durationMilliseconds);
1102
+ return {
1103
+ schema: networkRequestSchema,
1104
+ id: normalizeRequired(input.id, generateId(globalObject), 128),
1105
+ source: normalizeRequired(input.source, "unknown", 128),
1106
+ startedAtUtc,
1107
+ completedAtUtc: completedAtUtc < startedAtUtc ? startedAtUtc : completedAtUtc,
1108
+ durationMilliseconds: Number.isFinite(duration) && duration >= 0 ? duration : 0,
1109
+ method: normalizeRequired(input.method, "GET", 32).toUpperCase(),
1110
+ url: sanitizeUrl(input.url, options),
1111
+ protocol: normalizeOptional(input.protocol, 64),
1112
+ requestHeaders: options.includeRequestHeaders === false
1113
+ ? []
1114
+ : sanitizeHeaders(input.requestHeaders, options),
1115
+ requestBodySizeBytes: options.includeBodySizes === false
1116
+ ? undefined
1117
+ : normalizeSize(input.requestBodySizeBytes),
1118
+ requestBody: options.captureRequestBody !== false
1119
+ ? normalizeBody(input.requestBody, options)
1120
+ : undefined,
1121
+ statusCode: Number.isInteger(Number(input.statusCode)) &&
1122
+ Number(input.statusCode) >= 100 &&
1123
+ Number(input.statusCode) <= 999
1124
+ ? Number(input.statusCode)
1125
+ : undefined,
1126
+ reasonPhrase: normalizeOptional(input.reasonPhrase, 512),
1127
+ responseHeaders: options.includeResponseHeaders === false
1128
+ ? []
1129
+ : sanitizeHeaders(input.responseHeaders, options),
1130
+ responseBodySizeBytes: options.includeBodySizes === false
1131
+ ? undefined
1132
+ : normalizeSize(input.responseBodySizeBytes),
1133
+ responseBody: options.captureResponseBody !== false
1134
+ ? normalizeBody(input.responseBody, options)
1135
+ : undefined,
1136
+ errorType: normalizeOptional(input.errorType, 512),
1137
+ errorMessage: sanitizeErrorMessage(input.errorMessage, options),
1138
+ };
1139
+ }
1140
+ function sanitizeNetworkRequest(input, options = {}, globalObject = globalThis) {
1141
+ try {
1142
+ let normalized = normalizeRecord(input, options, globalObject);
1143
+ if (options.urlSanitizer) {
1144
+ normalized = normalizeRecord({ ...normalized, url: options.urlSanitizer(normalized.url) }, options, globalObject);
1145
+ }
1146
+ if (options.requestSanitizer) {
1147
+ const transformed = options.requestSanitizer(normalized);
1148
+ if (transformed == null)
1149
+ return null;
1150
+ normalized = normalizeRecord(transformed, options, globalObject);
1151
+ }
1152
+ return normalized;
1153
+ }
1154
+ catch {
1155
+ return null;
1156
+ }
1157
+ }
1158
+ function parseContentLength(headers) {
1159
+ const entry = headerEntries(headers).find(([name]) => String(name).toLowerCase() === "content-length");
1160
+ return entry ? normalizeSize(entry[1]) : undefined;
1161
+ }
1162
+ function headerValue(headers, wantedName) {
1163
+ const entry = headerEntries(headers).find(([name]) => String(name).toLowerCase() === wantedName);
1164
+ return entry == null ? undefined : String(entry[1]);
1165
+ }
1166
+ function isTextContentType(contentType) {
1167
+ if (!contentType)
1168
+ return true;
1169
+ const mediaType = contentType.split(";", 1)[0].trim().toLowerCase();
1170
+ return (mediaType.startsWith("text/") ||
1171
+ mediaType.endsWith("+json") ||
1172
+ mediaType.endsWith("+xml") ||
1173
+ [
1174
+ "application/json",
1175
+ "application/xml",
1176
+ "application/graphql",
1177
+ "application/javascript",
1178
+ "application/x-www-form-urlencoded",
1179
+ ].includes(mediaType));
1180
+ }
1181
+ function bodyFromBytes(bytes, totalBytes, contentType, options) {
1182
+ const binary = !isTextContentType(contentType);
1183
+ if (binary && options.captureBinaryBodies !== true)
1184
+ return undefined;
1185
+ const maximum = maximumBodyBytes(options);
1186
+ if (maximum <= 0)
1187
+ return undefined;
1188
+ const captured = binary
1189
+ ? bytes.slice(0, maximum)
1190
+ : truncateUtf8(bytes, maximum);
1191
+ return {
1192
+ contentType,
1193
+ encoding: binary ? "base64" : "utf8",
1194
+ data: binary ? bytesToBase64$1(captured) : new TextDecoder().decode(captured),
1195
+ capturedBytes: captured.length,
1196
+ totalBytes,
1197
+ truncated: bytes.length > captured.length ||
1198
+ (totalBytes != null && totalBytes > captured.length),
1199
+ };
1200
+ }
1201
+ function bodyFromValue(value, headers, options) {
1202
+ if (value == null || options.captureRequestBody === false)
1203
+ return undefined;
1204
+ const contentType = headerValue(headers, "content-type");
1205
+ if (typeof value === "string" || value instanceof URLSearchParams) {
1206
+ const bytes = new TextEncoder().encode(String(value));
1207
+ return bodyFromBytes(bytes, bytes.length, contentType ||
1208
+ (value instanceof URLSearchParams
1209
+ ? "application/x-www-form-urlencoded"
1210
+ : undefined), options);
1211
+ }
1212
+ if (value instanceof ArrayBuffer) {
1213
+ const bytes = new Uint8Array(value);
1214
+ return bodyFromBytes(bytes, bytes.length, contentType, options);
1215
+ }
1216
+ if (ArrayBuffer.isView(value)) {
1217
+ const bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
1218
+ return bodyFromBytes(bytes, bytes.length, contentType, options);
1219
+ }
1220
+ return undefined;
1221
+ }
1222
+ async function bodyFromFetchResponse(response, headers, options, shouldContinue = () => true) {
1223
+ if (!shouldContinue() || options.captureResponseBody === false)
1224
+ return undefined;
1225
+ const contentType = headerValue(headers, "content-type");
1226
+ if (!isTextContentType(contentType) && options.captureBinaryBodies !== true) {
1227
+ return undefined;
1228
+ }
1229
+ const totalBytes = parseContentLength(headers);
1230
+ const maximum = maximumBodyBytes(options);
1231
+ if (maximum <= 0)
1232
+ return undefined;
1233
+ const clone = response.clone();
1234
+ if (clone.body) {
1235
+ const reader = clone.body.getReader();
1236
+ const chunks = [];
1237
+ let capturedLength = 0;
1238
+ let observedLength = 0;
1239
+ try {
1240
+ while (capturedLength <= maximum) {
1241
+ if (!shouldContinue()) {
1242
+ await reader.cancel().catch(() => undefined);
1243
+ return undefined;
1244
+ }
1245
+ const result = await reader.read();
1246
+ if (!shouldContinue()) {
1247
+ await reader.cancel().catch(() => undefined);
1248
+ return undefined;
1249
+ }
1250
+ if (result.done)
1251
+ break;
1252
+ const chunk = result.value;
1253
+ observedLength += chunk.length;
1254
+ const remaining = maximum - capturedLength;
1255
+ if (remaining > 0) {
1256
+ const kept = chunk.slice(0, remaining);
1257
+ chunks.push(kept);
1258
+ capturedLength += kept.length;
1259
+ }
1260
+ if (observedLength > maximum) {
1261
+ await reader.cancel().catch(() => undefined);
1262
+ break;
1263
+ }
1264
+ }
1265
+ }
1266
+ finally {
1267
+ reader.releaseLock();
1268
+ }
1269
+ const joined = new Uint8Array(capturedLength);
1270
+ let offset = 0;
1271
+ for (const chunk of chunks) {
1272
+ joined.set(chunk, offset);
1273
+ offset += chunk.length;
1274
+ }
1275
+ return bodyFromBytes(joined, totalBytes ?? observedLength, contentType, options);
1276
+ }
1277
+ if (totalBytes == null || totalBytes > maximum)
1278
+ return undefined;
1279
+ const bytes = new Uint8Array(await clone.arrayBuffer());
1280
+ if (!shouldContinue())
1281
+ return undefined;
1282
+ return bodyFromBytes(bytes, totalBytes, contentType, options);
1283
+ }
1284
+ function parseXhrResponseHeaders(value) {
1285
+ return value
1286
+ .trim()
1287
+ .split(/[\r\n]+/)
1288
+ .flatMap((line) => {
1289
+ const separator = line.indexOf(":");
1290
+ return separator < 0
1291
+ ? []
1292
+ : [
1293
+ {
1294
+ name: line.slice(0, separator),
1295
+ value: line.slice(separator + 1),
1296
+ },
1297
+ ];
1298
+ });
1299
+ }
1300
+ function monotonicNow(globalObject) {
1301
+ return typeof globalObject.performance?.now === "function"
1302
+ ? globalObject.performance.now()
1303
+ : Date.now();
1304
+ }
1305
+ function installBrowserNetworkCapture(capture, options = {}, sourcePrefix = "capacitor", globalObject = globalThis) {
1306
+ const cleanups = [];
1307
+ let active = true;
1308
+ let fetchInvocationDepth = 0;
1309
+ if (options.captureFetch !== false &&
1310
+ typeof globalObject.fetch === "function") {
1311
+ const originalFetch = globalObject.fetch;
1312
+ const wrappedFetch = function (input, init) {
1313
+ const startedAtUtc = new Date().toISOString();
1314
+ const started = monotonicNow(globalObject);
1315
+ const inputRequest = typeof input === "object" && "headers" in input && "method" in input
1316
+ ? input
1317
+ : undefined;
1318
+ const requestHeaders = headerEntries(inputRequest?.headers).concat(headerEntries(init?.headers));
1319
+ const requestBody = bodyFromValue(init?.body, requestHeaders, options);
1320
+ const method = init?.method || inputRequest?.method || "GET";
1321
+ const url = typeof input === "string" ? input : inputRequest?.url || String(input);
1322
+ let promise;
1323
+ fetchInvocationDepth += 1;
1324
+ try {
1325
+ promise = originalFetch(input, init);
1326
+ }
1327
+ catch (error) {
1328
+ fetchInvocationDepth -= 1;
1329
+ const request = sanitizeNetworkRequest({
1330
+ source: `${sourcePrefix}.fetch`,
1331
+ startedAtUtc,
1332
+ completedAtUtc: new Date().toISOString(),
1333
+ durationMilliseconds: monotonicNow(globalObject) - started,
1334
+ method,
1335
+ url,
1336
+ requestHeaders: sanitizeHeaders(requestHeaders, {}),
1337
+ requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
1338
+ requestBody,
1339
+ errorType: error instanceof Error ? error.name : "Error",
1340
+ errorMessage: error instanceof Error ? error.message : String(error),
1341
+ }, options, globalObject);
1342
+ if (active && request)
1343
+ Promise.resolve(capture(request)).catch(() => undefined);
1344
+ throw error;
1345
+ }
1346
+ fetchInvocationDepth -= 1;
1347
+ return promise.then((response) => {
1348
+ if (!active)
1349
+ return response;
1350
+ const responseHeaders = headerEntries(response.headers);
1351
+ const responseRecord = {
1352
+ source: `${sourcePrefix}.fetch`,
1353
+ startedAtUtc,
1354
+ completedAtUtc: new Date().toISOString(),
1355
+ durationMilliseconds: monotonicNow(globalObject) - started,
1356
+ method,
1357
+ url: response.url || url,
1358
+ requestHeaders: sanitizeHeaders(requestHeaders, {}),
1359
+ requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
1360
+ requestBody,
1361
+ statusCode: response.status,
1362
+ reasonPhrase: response.statusText,
1363
+ responseHeaders: sanitizeHeaders(responseHeaders, {}),
1364
+ responseBodySizeBytes: parseContentLength(responseHeaders),
1365
+ };
1366
+ return bodyFromFetchResponse(response, responseHeaders, options, () => active).then((responseBody) => {
1367
+ const request = sanitizeNetworkRequest({
1368
+ ...responseRecord,
1369
+ responseBody,
1370
+ responseBodySizeBytes: responseRecord.responseBodySizeBytes ??
1371
+ responseBody?.totalBytes,
1372
+ }, options, globalObject);
1373
+ if (active && request)
1374
+ void Promise.resolve(capture(request)).catch(() => undefined);
1375
+ return response;
1376
+ }, () => {
1377
+ const request = sanitizeNetworkRequest(responseRecord, options, globalObject);
1378
+ if (active && request)
1379
+ void Promise.resolve(capture(request)).catch(() => undefined);
1380
+ return response;
1381
+ });
1382
+ }, (error) => {
1383
+ const request = sanitizeNetworkRequest({
1384
+ source: `${sourcePrefix}.fetch`,
1385
+ startedAtUtc,
1386
+ completedAtUtc: new Date().toISOString(),
1387
+ durationMilliseconds: monotonicNow(globalObject) - started,
1388
+ method,
1389
+ url,
1390
+ requestHeaders: sanitizeHeaders(requestHeaders, {}),
1391
+ requestBodySizeBytes: parseContentLength(requestHeaders) ?? requestBody?.totalBytes,
1392
+ requestBody,
1393
+ errorType: error instanceof Error ? error.name : "Error",
1394
+ errorMessage: error instanceof Error ? error.message : String(error),
1395
+ }, options, globalObject);
1396
+ if (active && request)
1397
+ Promise.resolve(capture(request)).catch(() => undefined);
1398
+ throw error;
1399
+ });
1400
+ };
1401
+ globalObject.fetch = wrappedFetch;
1402
+ cleanups.push(() => {
1403
+ if (globalObject.fetch === wrappedFetch)
1404
+ globalObject.fetch = originalFetch;
1405
+ });
1406
+ }
1407
+ const Xhr = globalObject.XMLHttpRequest;
1408
+ if (options.captureXmlHttpRequest !== false && Xhr?.prototype) {
1409
+ const states = new WeakMap();
1410
+ const prototype = Xhr.prototype;
1411
+ const originalOpen = prototype.open;
1412
+ const originalSend = prototype.send;
1413
+ const originalSetRequestHeader = prototype.setRequestHeader;
1414
+ const wrappedOpen = function (method, url, ...rest) {
1415
+ states.set(this, {
1416
+ method,
1417
+ url: String(url),
1418
+ requestHeaders: [],
1419
+ suppressed: fetchInvocationDepth > 0,
1420
+ });
1421
+ Reflect.apply(originalOpen, this, [method, url, ...rest]);
1422
+ };
1423
+ const wrappedSetRequestHeader = function (name, value) {
1424
+ states.get(this)?.requestHeaders.push({ name, value });
1425
+ Reflect.apply(originalSetRequestHeader, this, [name, value]);
1426
+ };
1427
+ const wrappedSend = function (body) {
1428
+ const state = states.get(this);
1429
+ if (!state || state.suppressed) {
1430
+ Reflect.apply(originalSend, this, [body]);
1431
+ return;
1432
+ }
1433
+ state.startedAtUtc = new Date().toISOString();
1434
+ state.started = monotonicNow(globalObject);
1435
+ state.requestBody = bodyFromValue(body, state.requestHeaders, options);
1436
+ let failure;
1437
+ const markFailure = (event) => {
1438
+ failure = event.type;
1439
+ };
1440
+ const complete = () => {
1441
+ if (!active)
1442
+ return;
1443
+ let responseHeaders = [];
1444
+ try {
1445
+ responseHeaders = parseXhrResponseHeaders(this.getAllResponseHeaders());
1446
+ }
1447
+ catch {
1448
+ // Some WebViews throw before response headers exist.
1449
+ }
1450
+ let responseBody;
1451
+ try {
1452
+ const responseType = this.responseType || "text";
1453
+ const responseOptions = {
1454
+ ...options,
1455
+ captureRequestBody: options.captureResponseBody,
1456
+ };
1457
+ if (responseType === "text") {
1458
+ responseBody = bodyFromValue(this.responseText, responseHeaders, responseOptions);
1459
+ }
1460
+ else if (responseType === "arraybuffer") {
1461
+ responseBody = bodyFromValue(this.response, responseHeaders, responseOptions);
1462
+ }
1463
+ }
1464
+ catch {
1465
+ // Response data is not readable for every XHR response type.
1466
+ }
1467
+ const request = sanitizeNetworkRequest({
1468
+ source: `${sourcePrefix}.xhr`,
1469
+ startedAtUtc: state.startedAtUtc,
1470
+ completedAtUtc: new Date().toISOString(),
1471
+ durationMilliseconds: monotonicNow(globalObject) - (state.started ?? 0),
1472
+ method: state.method,
1473
+ url: this.responseURL || state.url,
1474
+ requestHeaders: state.requestHeaders,
1475
+ requestBodySizeBytes: parseContentLength(state.requestHeaders) ??
1476
+ state.requestBody?.totalBytes,
1477
+ requestBody: state.requestBody,
1478
+ statusCode: this.status || undefined,
1479
+ reasonPhrase: this.statusText,
1480
+ responseHeaders,
1481
+ responseBodySizeBytes: parseContentLength(responseHeaders) ?? responseBody?.totalBytes,
1482
+ responseBody,
1483
+ errorType: failure,
1484
+ errorMessage: failure ? `XMLHttpRequest ${failure}` : undefined,
1485
+ }, options, globalObject);
1486
+ if (request)
1487
+ Promise.resolve(capture(request)).catch(() => undefined);
1488
+ };
1489
+ this.addEventListener("error", markFailure);
1490
+ this.addEventListener("abort", markFailure);
1491
+ this.addEventListener("timeout", markFailure);
1492
+ this.addEventListener("loadend", complete, { once: true });
1493
+ Reflect.apply(originalSend, this, [body]);
1494
+ };
1495
+ prototype.open = wrappedOpen;
1496
+ prototype.setRequestHeader = wrappedSetRequestHeader;
1497
+ prototype.send = wrappedSend;
1498
+ cleanups.push(() => {
1499
+ if (prototype.open === wrappedOpen)
1500
+ prototype.open = originalOpen;
1501
+ if (prototype.send === wrappedSend)
1502
+ prototype.send = originalSend;
1503
+ if (prototype.setRequestHeader === wrappedSetRequestHeader) {
1504
+ prototype.setRequestHeader = originalSetRequestHeader;
1505
+ }
1506
+ });
1507
+ }
1508
+ let removed = false;
1509
+ return {
1510
+ remove() {
1511
+ if (removed)
1512
+ return;
1513
+ removed = true;
1514
+ active = false;
1515
+ for (const cleanup of cleanups.reverse())
1516
+ cleanup();
1517
+ },
1518
+ };
1519
+ }
1520
+
1521
+ const ANSIGHT_CAPACITOR_SDK_VERSION = "1.3.0-preview.11";
1522
+ const COMPILED_CAPACITOR_CORE_VERSION = "8.4.2";
1523
+ const CAPACITOR_GROUP = "capacitor";
1524
+ const LOCALIZATION_GROUP = "localization";
1525
+ function normalized(value) {
1526
+ if (value == null)
1527
+ return undefined;
1528
+ const result = String(value).trim();
1529
+ return result || undefined;
1530
+ }
1531
+ function canonicalizeLocale(value) {
1532
+ const locale = normalized(value)?.replace(/_/g, "-");
1533
+ if (!locale)
1534
+ return undefined;
1535
+ try {
1536
+ return Intl.getCanonicalLocales(locale)[0] ?? locale;
1537
+ }
1538
+ catch {
1539
+ return locale;
1540
+ }
1541
+ }
1542
+ function parseLocale(locale) {
1543
+ const parts = (locale ?? "").split("-").filter(Boolean);
1544
+ const language = parts[0]?.toLowerCase();
1545
+ const region = parts.find((part, index) => index > 0 && (/^[A-Za-z]{2}$/.test(part) || /^\d{3}$/.test(part)));
1546
+ return { language, region: region?.toUpperCase() };
1547
+ }
1548
+ function webViewDetails(platform, nativePlatform, userAgent) {
1549
+ const agent = userAgent ?? "";
1550
+ if (nativePlatform && platform === "ios") {
1551
+ return {
1552
+ engine: "wkWebView",
1553
+ version: /AppleWebKit\/([^\s]+)/.exec(agent)?.[1],
1554
+ };
1555
+ }
1556
+ if (nativePlatform && platform === "android") {
1557
+ return {
1558
+ engine: "chromiumWebView",
1559
+ version: /(?:Chrome|Chromium)\/([^\s]+)/.exec(agent)?.[1],
1560
+ };
1561
+ }
1562
+ if (/Firefox\/([^\s]+)/.test(agent)) {
1563
+ return { engine: "gecko", version: /Firefox\/([^\s]+)/.exec(agent)?.[1] };
1564
+ }
1565
+ if (/(?:Chrome|Chromium)\/([^\s]+)/.test(agent)) {
1566
+ return {
1567
+ engine: "chromium",
1568
+ version: /(?:Chrome|Chromium)\/([^\s]+)/.exec(agent)?.[1],
1569
+ };
1570
+ }
1571
+ if (/AppleWebKit\/([^\s]+)/.test(agent)) {
1572
+ return {
1573
+ engine: "webkit",
1574
+ version: /AppleWebKit\/([^\s]+)/.exec(agent)?.[1],
1575
+ };
1576
+ }
1577
+ return { engine: "unknown" };
1578
+ }
1579
+ function currentCapacitorSessionEnvironment(platform, nativePlatform) {
1580
+ let resolved;
1581
+ try {
1582
+ resolved = Intl.DateTimeFormat().resolvedOptions();
1583
+ }
1584
+ catch {
1585
+ resolved = undefined;
1586
+ }
1587
+ return {
1588
+ platform,
1589
+ nativePlatform,
1590
+ userAgent: typeof navigator === "undefined" ? undefined : navigator.userAgent,
1591
+ locale: resolved?.locale ??
1592
+ (typeof navigator === "undefined" ? undefined : navigator.language),
1593
+ timeZone: resolved?.timeZone,
1594
+ utcOffsetMinutes: -new Date().getTimezoneOffset(),
1595
+ };
1596
+ }
1597
+ function createAutomaticSessionProperties(environment) {
1598
+ const platform = normalized(environment.platform) ?? "unknown";
1599
+ const userAgent = normalized(environment.userAgent);
1600
+ const webView = webViewDetails(platform, environment.nativePlatform, userAgent);
1601
+ const locale = canonicalizeLocale(environment.locale);
1602
+ const parsedLocale = parseLocale(locale);
1603
+ const capacitor = {
1604
+ sdkVersion: ANSIGHT_CAPACITOR_SDK_VERSION,
1605
+ capacitorVersion: "8.x",
1606
+ compiledCapacitorVersion: COMPILED_CAPACITOR_CORE_VERSION,
1607
+ platform,
1608
+ runtimeLanguage: "javascript",
1609
+ executionMode: environment.nativePlatform ? "native" : "web",
1610
+ webViewEngine: webView.engine,
1611
+ };
1612
+ if (webView.version)
1613
+ capacitor.webViewEngineVersion = webView.version;
1614
+ if (userAgent)
1615
+ capacitor.userAgent = userAgent;
1616
+ const localization = {
1617
+ utcOffsetMinutes: String(environment.utcOffsetMinutes ?? -new Date().getTimezoneOffset()),
1618
+ };
1619
+ if (locale)
1620
+ localization.locale = locale;
1621
+ if (parsedLocale.language)
1622
+ localization.language = parsedLocale.language;
1623
+ if (parsedLocale.region)
1624
+ localization.region = parsedLocale.region;
1625
+ if (normalized(environment.timeZone)) {
1626
+ localization.timeZone = String(environment.timeZone).trim();
1627
+ }
1628
+ return {
1629
+ [CAPACITOR_GROUP]: capacitor,
1630
+ [LOCALIZATION_GROUP]: localization,
1631
+ };
1632
+ }
1633
+ function mergeSessionProperties(automaticProperties, customProperties) {
1634
+ const merged = Object.fromEntries(Object.entries(automaticProperties).map(([group, properties]) => [
1635
+ group,
1636
+ { ...properties },
1637
+ ]));
1638
+ for (const [group, properties] of Object.entries(customProperties ?? {})) {
1639
+ merged[group] = { ...(merged[group] ?? {}), ...properties };
1640
+ }
1641
+ return merged;
1642
+ }
1643
+
624
1644
  const AnsightNative = core.registerPlugin("Ansight");
625
1645
  const toolHandlers = new Map();
626
1646
  const artifactProviders = new Map();
@@ -631,6 +1651,9 @@ let logListener;
631
1651
  let lifecycleCleanup;
632
1652
  let artifactToolRegistrations = [];
633
1653
  let domToolRegistration;
1654
+ let networkCaptureSubscription;
1655
+ let networkCaptureRegistration;
1656
+ let networkConnectionListener;
634
1657
  function normalizePairingPayload(payload) {
635
1658
  if (payload == null)
636
1659
  return payload;
@@ -638,11 +1661,19 @@ function normalizePairingPayload(payload) {
638
1661
  }
639
1662
  function normalizeOptions(input) {
640
1663
  const options = JSON.parse(JSON.stringify(input));
1664
+ options.customProperties = mergeSessionProperties(automaticSessionProperties(), options.customProperties);
641
1665
  delete options.domTools;
642
1666
  delete options.errorCapture;
643
1667
  delete options.lifecycle;
1668
+ delete options.networkCapture;
644
1669
  return options;
645
1670
  }
1671
+ function automaticSessionProperties() {
1672
+ return createAutomaticSessionProperties(currentCapacitorSessionEnvironment(core.Capacitor.getPlatform(), core.Capacitor.isNativePlatform()));
1673
+ }
1674
+ function automaticSessionPropertyValue(group, key) {
1675
+ return automaticSessionProperties()[group]?.[key];
1676
+ }
646
1677
  function normalizeToolResult(value) {
647
1678
  if (value && typeof value === "object" && "success" in value) {
648
1679
  return value;
@@ -692,11 +1723,13 @@ async function emitHostConnectionStatus() {
692
1723
  }
693
1724
  async function afterConnectionChange(operation) {
694
1725
  const result = await operation();
1726
+ await refreshNetworkCaptureConnection();
695
1727
  await emitHostConnectionStatus();
696
1728
  return result;
697
1729
  }
698
1730
  async function initialize(options = {}) {
699
1731
  const result = await AnsightNative.initialize(normalizeOptions(options));
1732
+ await configureNetworkCapture(options.networkCapture);
700
1733
  if (options.lifecycle !== false)
701
1734
  startLifecycleTracking();
702
1735
  if (options.errorCapture) {
@@ -710,6 +1743,7 @@ async function initialize(options = {}) {
710
1743
  }
711
1744
  async function initializeAndActivate(options = {}) {
712
1745
  const result = await AnsightNative.initializeAndActivate(normalizeOptions(options));
1746
+ await configureNetworkCapture(options.networkCapture);
713
1747
  if (options.lifecycle !== false)
714
1748
  startLifecycleTracking();
715
1749
  if (options.errorCapture) {
@@ -734,6 +1768,81 @@ function event(input) {
734
1768
  return AnsightNative.recordEvent(typeof input === "string" ? { label: input } : input);
735
1769
  }
736
1770
  const recordEvent = event;
1771
+ async function recordNetworkRequest(input, sanitizationOptions = {}) {
1772
+ const request = sanitizeNetworkRequest(input, sanitizationOptions);
1773
+ if (!request) {
1774
+ return {
1775
+ success: false,
1776
+ message: "Network request capture was suppressed by the sanitizer.",
1777
+ };
1778
+ }
1779
+ return AnsightNative.recordNetworkRequest(request);
1780
+ }
1781
+ function installNetworkCapture(options = {}) {
1782
+ uninstallNetworkCapture();
1783
+ const registration = { options };
1784
+ networkCaptureRegistration = registration;
1785
+ ensureNetworkConnectionListener();
1786
+ void refreshNetworkCaptureConnection();
1787
+ return {
1788
+ remove() {
1789
+ if (networkCaptureRegistration === registration) {
1790
+ uninstallNetworkCapture();
1791
+ }
1792
+ },
1793
+ };
1794
+ }
1795
+ function uninstallNetworkCapture() {
1796
+ networkCaptureRegistration = undefined;
1797
+ const listener = networkConnectionListener;
1798
+ networkConnectionListener = undefined;
1799
+ if (listener)
1800
+ void listener.then((value) => value.remove());
1801
+ detachNetworkCapture();
1802
+ }
1803
+ function detachNetworkCapture() {
1804
+ networkCaptureSubscription?.remove();
1805
+ networkCaptureSubscription = undefined;
1806
+ }
1807
+ async function refreshNetworkCaptureConnection() {
1808
+ const registration = networkCaptureRegistration;
1809
+ if (!registration) {
1810
+ detachNetworkCapture();
1811
+ return;
1812
+ }
1813
+ try {
1814
+ const status = await AnsightNative.hostConnectionStatus();
1815
+ if (networkCaptureRegistration !== registration)
1816
+ return;
1817
+ applyNetworkConnectionStatus(status);
1818
+ }
1819
+ catch {
1820
+ if (networkCaptureRegistration === registration)
1821
+ detachNetworkCapture();
1822
+ }
1823
+ }
1824
+ function ensureNetworkConnectionListener() {
1825
+ networkConnectionListener ??= AnsightNative.addListener("ansightHostConnectionStatus", applyNetworkConnectionStatus);
1826
+ }
1827
+ function applyNetworkConnectionStatus(status) {
1828
+ const registration = networkCaptureRegistration;
1829
+ if (!registration || status.isConnected !== true) {
1830
+ detachNetworkCapture();
1831
+ return;
1832
+ }
1833
+ networkCaptureSubscription ??= installBrowserNetworkCapture((request) => AnsightNative.recordNetworkRequest(request), registration.options);
1834
+ }
1835
+ async function configureNetworkCapture(value) {
1836
+ uninstallNetworkCapture();
1837
+ if (!value)
1838
+ return;
1839
+ networkCaptureRegistration = {
1840
+ options: typeof value === "object" ? value : {},
1841
+ };
1842
+ ensureNetworkConnectionListener();
1843
+ await refreshNetworkCaptureConnection();
1844
+ }
1845
+ const recordCrashCandidate = (input) => AnsightNative.recordCrashCandidate(input);
737
1846
  const screenViewed = (name, details = {}) => AnsightNative.screenViewed({ name, details });
738
1847
  const trackRoute = screenViewed;
739
1848
  const setAppLifecycleState = (state) => AnsightNative.setAppLifecycleState({ state });
@@ -788,12 +1897,25 @@ const disableFramesPerSecond = () => AnsightNative.disableFramesPerSecond();
788
1897
  const captureScreenFrame = (options = {}) => AnsightNative.captureScreenFrame(options);
789
1898
  const enableTouchCapture = () => AnsightNative.enableTouchCapture();
790
1899
  const disableTouchCapture = () => AnsightNative.disableTouchCapture();
791
- const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({ properties });
1900
+ const updateSessionProperties = (properties) => AnsightNative.updateSessionProperties({
1901
+ properties: mergeSessionProperties(automaticSessionProperties(), properties),
1902
+ });
792
1903
  const updateCustomProperties = updateSessionProperties;
793
- const clearSessionProperties = () => AnsightNative.clearSessionProperties();
1904
+ const clearSessionProperties = () => AnsightNative.updateSessionProperties({
1905
+ properties: automaticSessionProperties(),
1906
+ });
794
1907
  const clearCustomProperties = clearSessionProperties;
795
1908
  const registerCustomProperty = (group, key, value) => AnsightNative.registerCustomProperty({ group, key, value });
796
- const removeCustomProperty = (group, key) => AnsightNative.removeCustomProperty({ group, key });
1909
+ const removeCustomProperty = (group, key) => {
1910
+ const automaticValue = automaticSessionPropertyValue(group, key);
1911
+ return automaticValue == null
1912
+ ? AnsightNative.removeCustomProperty({ group, key })
1913
+ : AnsightNative.registerCustomProperty({
1914
+ group,
1915
+ key,
1916
+ value: automaticValue,
1917
+ });
1918
+ };
797
1919
  function addHostConnectionStatusListener(listener, options = {}) {
798
1920
  hostConnectionListeners.add(listener);
799
1921
  if (options.emitCurrent !== false)
@@ -1039,6 +2161,18 @@ function installErrorHandlers(options = {}) {
1039
2161
  const onError = (eventValue) => {
1040
2162
  if (!captureErrors)
1041
2163
  return;
2164
+ void recordCrashCandidate({
2165
+ runtime: "capacitor-javascript",
2166
+ kind: "unhandled_javascript_error",
2167
+ message: eventValue.message,
2168
+ stack: eventValue.error instanceof Error ? eventValue.error.stack : undefined,
2169
+ fatal: false,
2170
+ metadata: JSON.stringify({
2171
+ filename: eventValue.filename,
2172
+ line: eventValue.lineno,
2173
+ column: eventValue.colno,
2174
+ }),
2175
+ }).catch(() => undefined);
1042
2176
  void event({
1043
2177
  label: eventValue.message || "Unhandled JavaScript error",
1044
2178
  type: "Exception",
@@ -1056,6 +2190,13 @@ function installErrorHandlers(options = {}) {
1056
2190
  if (!captureRejections)
1057
2191
  return;
1058
2192
  const reason = eventValue.reason;
2193
+ void recordCrashCandidate({
2194
+ runtime: "capacitor-javascript",
2195
+ kind: "unhandled_promise_rejection",
2196
+ message: reason instanceof Error ? reason.message : String(reason),
2197
+ stack: reason instanceof Error ? reason.stack : undefined,
2198
+ fatal: false,
2199
+ }).catch(() => undefined);
1059
2200
  void event({
1060
2201
  label: reason instanceof Error
1061
2202
  ? reason.message
@@ -1125,6 +2266,10 @@ const Ansight = {
1125
2266
  recordMetric,
1126
2267
  event,
1127
2268
  recordEvent,
2269
+ recordNetworkRequest,
2270
+ installNetworkCapture,
2271
+ uninstallNetworkCapture,
2272
+ sanitizeNetworkRequest,
1128
2273
  screenViewed,
1129
2274
  trackRoute,
1130
2275
  setAppLifecycleState,
@@ -1222,14 +2367,17 @@ exports.initialize = initialize;
1222
2367
  exports.initializeAndActivate = initializeAndActivate;
1223
2368
  exports.installDomTools = installDomTools;
1224
2369
  exports.installErrorHandlers = installErrorHandlers;
2370
+ exports.installNetworkCapture = installNetworkCapture;
1225
2371
  exports.isFramesPerSecondEnabled = isFramesPerSecondEnabled;
1226
2372
  exports.listRegisteredArtifactProviders = listRegisteredArtifactProviders;
1227
2373
  exports.listRegisteredTools = listRegisteredTools;
1228
2374
  exports.metric = metric;
1229
2375
  exports.notifyHostConnectionConfigChanged = notifyHostConnectionConfigChanged;
1230
2376
  exports.openSession = openSession;
2377
+ exports.recordCrashCandidate = recordCrashCandidate;
1231
2378
  exports.recordEvent = recordEvent;
1232
2379
  exports.recordMetric = recordMetric;
2380
+ exports.recordNetworkRequest = recordNetworkRequest;
1233
2381
  exports.recordedEvents = recordedEvents;
1234
2382
  exports.recordedMetrics = recordedMetrics;
1235
2383
  exports.registerArtifactProvider = registerArtifactProvider;
@@ -1238,6 +2386,7 @@ exports.registerCustomProperty = registerCustomProperty;
1238
2386
  exports.registerMetricChannel = registerMetricChannel;
1239
2387
  exports.registerTool = registerTool;
1240
2388
  exports.removeCustomProperty = removeCustomProperty;
2389
+ exports.sanitizeNetworkRequest = sanitizeNetworkRequest;
1241
2390
  exports.savePairingConfig = savePairingConfig;
1242
2391
  exports.scanPairingQrCode = scanPairingQrCode;
1243
2392
  exports.screenViewed = screenViewed;
@@ -1251,6 +2400,7 @@ exports.stopAppStateTracking = stopAppStateTracking;
1251
2400
  exports.stopLifecycleTracking = stopLifecycleTracking;
1252
2401
  exports.trackRoute = trackRoute;
1253
2402
  exports.uninstallDomTools = uninstallDomTools;
2403
+ exports.uninstallNetworkCapture = uninstallNetworkCapture;
1254
2404
  exports.unregisterArtifactProvider = unregisterArtifactProvider;
1255
2405
  exports.unregisterTool = unregisterTool;
1256
2406
  exports.updateCustomProperties = updateCustomProperties;