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