@trustchex/react-native-sdk 1.493.0 → 1.493.2

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.
@@ -1,19 +1,20 @@
1
1
  "use strict";
2
2
 
3
- import React, { useState } from 'react';
4
- import { Alert } from 'react-native';
3
+ import React, { useRef, useState } from 'react';
4
+ import { ActivityIndicator, Alert, Modal, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import StyledButton from "./StyledButton.js";
7
7
  import { sendDiagnosticReport } from "../Libs/sendDiagnosticReport.js";
8
8
  import { SDK_VERSION } from "../../version.js";
9
- import { jsx as _jsx } from "react/jsx-runtime";
9
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
10
10
  /**
11
11
  * "Report a scanning issue" button for the result screen.
12
12
  *
13
- * Shows a brief notice describing what will be shared (acting as the user's
14
- * consent step), then uploads the diagnostic bundle (technical signals +
15
- * scanned data + captured images) to the backend, encrypted in transit and at
16
- * rest. Operators view and download the reports from the dashboard.
13
+ * Uploads the diagnostic bundle to the backend (proof-of-work authenticated,
14
+ * encrypted in transit + at rest). The PoW solve is the only slow step, so while
15
+ * sending we show a NON-DISMISSIBLE modal overlay with progress the user can't
16
+ * dismiss it accidentally (tap-outside / Android-back do nothing), but can
17
+ * explicitly Cancel, which aborts the solve. A thank-you shows on success.
17
18
  *
18
19
  * Hidden when there's no session/baseUrl to upload to (e.g. before init).
19
20
  */
@@ -27,9 +28,36 @@ const DiagnosticReportButton = ({
27
28
  t
28
29
  } = useTranslation();
29
30
  const [sending, setSending] = useState(false);
31
+ const [progress, setProgress] = useState(0);
32
+ // Ref so the running solve sees cancellation immediately (no stale closure).
33
+ const cancelledRef = useRef(false);
30
34
  if (!baseUrl || !sessionId) {
31
35
  return null;
32
36
  }
37
+ const doSend = async () => {
38
+ cancelledRef.current = false;
39
+ setProgress(0);
40
+ setSending(true);
41
+ try {
42
+ const {
43
+ uploaded,
44
+ cancelled
45
+ } = await sendDiagnosticReport({
46
+ baseUrl,
47
+ sessionId,
48
+ scan,
49
+ sdkVersion: SDK_VERSION,
50
+ images,
51
+ onPowProgress: setProgress,
52
+ shouldCancel: () => cancelledRef.current
53
+ });
54
+ setSending(false);
55
+ if (cancelled) return; // user cancelled — no alert
56
+ Alert.alert(uploaded ? t('diagnosticReport.sentTitle') : t('diagnosticReport.failedTitle'), uploaded ? t('diagnosticReport.sentBody') : t('diagnosticReport.failedBody'));
57
+ } catch {
58
+ setSending(false);
59
+ }
60
+ };
33
61
  const onPress = () => {
34
62
  if (sending) return;
35
63
  Alert.alert(t('diagnosticReport.noticeTitle'), t('diagnosticReport.noticeBody'), [{
@@ -37,30 +65,91 @@ const DiagnosticReportButton = ({
37
65
  style: 'cancel'
38
66
  }, {
39
67
  text: t('diagnosticReport.continue'),
40
- onPress: async () => {
41
- setSending(true);
42
- try {
43
- const {
44
- uploaded
45
- } = await sendDiagnosticReport({
46
- baseUrl,
47
- sessionId,
48
- scan,
49
- sdkVersion: SDK_VERSION,
50
- images
51
- });
52
- Alert.alert(uploaded ? t('diagnosticReport.sentTitle') : t('diagnosticReport.failedTitle'), uploaded ? t('diagnosticReport.sentBody') : t('diagnosticReport.failedBody'));
53
- } finally {
54
- setSending(false);
55
- }
56
- }
68
+ onPress: doSend
57
69
  }]);
58
70
  };
59
- return /*#__PURE__*/_jsx(StyledButton, {
60
- mode: "outlined",
61
- onPress: onPress,
62
- disabled: sending,
63
- children: t('diagnosticReport.button')
71
+ const pct = Math.round(progress * 100);
72
+ return /*#__PURE__*/_jsxs(_Fragment, {
73
+ children: [/*#__PURE__*/_jsx(StyledButton, {
74
+ mode: "outlined",
75
+ onPress: onPress,
76
+ children: t('diagnosticReport.button')
77
+ }), /*#__PURE__*/_jsx(Modal, {
78
+ visible: sending,
79
+ transparent: true,
80
+ animationType: "fade"
81
+ // Block accidental dismissal: Android hardware-back does nothing while
82
+ // sending. There is no tap-outside-to-close (we render our own backdrop).
83
+ ,
84
+ onRequestClose: () => {},
85
+ children: /*#__PURE__*/_jsx(View, {
86
+ style: styles.backdrop,
87
+ children: /*#__PURE__*/_jsxs(View, {
88
+ style: styles.card,
89
+ children: [/*#__PURE__*/_jsx(ActivityIndicator, {
90
+ size: "large"
91
+ }), /*#__PURE__*/_jsx(Text, {
92
+ style: styles.title,
93
+ children: t('diagnosticReport.sending')
94
+ }), /*#__PURE__*/_jsx(Text, {
95
+ style: styles.progress,
96
+ children: pct > 0 ? t('diagnosticReport.sendingProgress', {
97
+ pct
98
+ }) : t('diagnosticReport.sendingPreparing')
99
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
100
+ accessibilityRole: "button",
101
+ style: styles.cancelBtn,
102
+ onPress: () => {
103
+ cancelledRef.current = true;
104
+ },
105
+ children: /*#__PURE__*/_jsx(Text, {
106
+ style: styles.cancelText,
107
+ children: t('diagnosticReport.cancel')
108
+ })
109
+ })]
110
+ })
111
+ })
112
+ })]
64
113
  });
65
114
  };
115
+ const styles = StyleSheet.create({
116
+ backdrop: {
117
+ flex: 1,
118
+ backgroundColor: 'rgba(0,0,0,0.45)',
119
+ alignItems: 'center',
120
+ justifyContent: 'center',
121
+ padding: 24
122
+ },
123
+ card: {
124
+ width: '100%',
125
+ maxWidth: 320,
126
+ backgroundColor: 'white',
127
+ borderRadius: 16,
128
+ paddingVertical: 24,
129
+ paddingHorizontal: 20,
130
+ alignItems: 'center',
131
+ gap: 12
132
+ },
133
+ title: {
134
+ fontSize: 16,
135
+ fontWeight: '600',
136
+ color: '#111827',
137
+ textAlign: 'center'
138
+ },
139
+ progress: {
140
+ fontSize: 14,
141
+ color: '#6B7280',
142
+ textAlign: 'center'
143
+ },
144
+ cancelBtn: {
145
+ marginTop: 8,
146
+ paddingVertical: 10,
147
+ paddingHorizontal: 24
148
+ },
149
+ cancelText: {
150
+ fontSize: 15,
151
+ fontWeight: '600',
152
+ color: '#DC2626'
153
+ }
154
+ });
66
155
  export default DiagnosticReportButton;
@@ -29,9 +29,11 @@ const leadingZeroBits = bytes => {
29
29
  };
30
30
 
31
31
  /**
32
- * Solve the PoW for a challenge. Returns the solution nonce as a string.
33
- * Bounded by `maxIterations` so a pathological difficulty can't hang the app;
34
- * returns null if unsolved within the bound.
32
+ * Solve the PoW for a challenge (synchronous). Returns the solution nonce as a
33
+ * string. Bounded by `maxIterations` so a pathological difficulty can't hang the
34
+ * app; returns null if unsolved within the bound.
35
+ *
36
+ * Prefer `solvePowAsync` on the UI thread — this blocks until solved.
35
37
  */
36
38
  export const solvePow = (challenge, difficulty, maxIterations = 8_000_000) => {
37
39
  for (let i = 0; i < maxIterations; i++) {
@@ -42,4 +44,55 @@ export const solvePow = (challenge, difficulty, maxIterations = 8_000_000) => {
42
44
  }
43
45
  }
44
46
  return null;
47
+ };
48
+
49
+ /** Thrown by solvePowAsync / sendDiagnosticReport when the user cancels. */
50
+ export class PowCancelledError extends Error {
51
+ constructor() {
52
+ super('Proof-of-work cancelled');
53
+ this.name = 'PowCancelledError';
54
+ }
55
+ }
56
+ /**
57
+ * Async, UI-friendly PoW solver. Hashes in small chunks and yields to the event
58
+ * loop between chunks so the JS thread (and the UI) stays responsive instead of
59
+ * freezing during the solve. Reports coarse progress (0..1, best-effort against
60
+ * the expected work for the difficulty) via `onProgress`, and aborts (throwing
61
+ * PowCancelledError) when `shouldCancel` returns true.
62
+ *
63
+ * Returns the solution nonce, or null if unsolved within `maxIterations`.
64
+ */
65
+ export const solvePowAsync = async (challenge, difficulty, options = {}) => {
66
+ const {
67
+ onProgress,
68
+ shouldCancel,
69
+ maxIterations = 8_000_000,
70
+ // Hash a large chunk between yields: the per-yield setTimeout(0) round-trip
71
+ // on Hermes costs ~ms, so yielding every 2k hashes dominated wall-clock.
72
+ // 25k keeps the UI responsive + cancel snappy (a chunk is a few tens of ms)
73
+ // while spending almost all time hashing, not bouncing through the event loop.
74
+ chunkSize = 25_000
75
+ } = options;
76
+ // Expected hashes ≈ 2^difficulty; used only to render a smooth-ish bar.
77
+ const expected = Math.max(1, Math.pow(2, difficulty));
78
+ // Encode the fixed "challenge." prefix once; per iteration we only encode the
79
+ // small varying nonce and concat, instead of re-UTF-8-encoding the whole
80
+ // string (the challenge is 32+ chars) on every hash.
81
+ const prefix = Buffer.from(`${challenge}.`, 'utf-8');
82
+ for (let i = 0; i < maxIterations; i++) {
83
+ const digest = sha256(Buffer.concat([prefix, Buffer.from(i.toString(), 'utf-8')]));
84
+ if (leadingZeroBits(digest) >= difficulty) {
85
+ onProgress?.(1);
86
+ return i.toString();
87
+ }
88
+ if (i % chunkSize === chunkSize - 1) {
89
+ if (shouldCancel?.()) {
90
+ throw new PowCancelledError();
91
+ }
92
+ onProgress?.(Math.min(0.99, i / expected));
93
+ // Yield so React can paint the progress and the UI stays responsive.
94
+ await new Promise(resolve => setTimeout(resolve, 0));
95
+ }
96
+ }
97
+ return null;
45
98
  };
@@ -22,7 +22,7 @@
22
22
  import { diagnostics } from "./diagnostics.js";
23
23
  import { gatherDeviceDetails } from "./native-device-info.utils.js";
24
24
  import { buildDiagnosticReport } from "./diagnosticReport.js";
25
- import { solvePow } from "./diagnosticPow.js";
25
+ import { solvePowAsync, PowCancelledError } from "./diagnosticPow.js";
26
26
  import { logError } from "./debug.utils.js";
27
27
  const mimeToType = mime => mime === 'png' ? 'image/png' : 'image/jpeg';
28
28
 
@@ -37,7 +37,9 @@ export const sendDiagnosticReport = async params => {
37
37
  sessionId,
38
38
  scan,
39
39
  sdkVersion,
40
- images = []
40
+ images = [],
41
+ onPowProgress,
42
+ shouldCancel
41
43
  } = params;
42
44
  if (!baseUrl) {
43
45
  return {
@@ -95,12 +97,21 @@ export const sendDiagnosticReport = async params => {
95
97
  };
96
98
  }
97
99
  const challenge = await challengeRes.json();
98
- const solution = solvePow(challenge.challenge, challenge.difficulty);
100
+ const solution = await solvePowAsync(challenge.challenge, challenge.difficulty, {
101
+ onProgress: onPowProgress,
102
+ shouldCancel
103
+ });
99
104
  if (!solution) {
100
105
  return {
101
106
  uploaded: false
102
107
  };
103
108
  }
109
+ if (shouldCancel?.()) {
110
+ return {
111
+ uploaded: false,
112
+ cancelled: true
113
+ };
114
+ }
104
115
  const response = await fetch(`${baseUrl}/api/app/mobile/diagnostics`, {
105
116
  method: 'POST',
106
117
  headers: {
@@ -114,6 +125,12 @@ export const sendDiagnosticReport = async params => {
114
125
  uploaded: response.ok
115
126
  };
116
127
  } catch (e) {
128
+ if (e instanceof PowCancelledError) {
129
+ return {
130
+ uploaded: false,
131
+ cancelled: true
132
+ };
133
+ }
117
134
  logError('Diagnostics', 'Failed to upload diagnostic report', e instanceof Error ? e.message : String(e));
118
135
  return {
119
136
  uploaded: false
@@ -50,8 +50,11 @@ export default {
50
50
  'diagnosticReport.noticeBody': 'This sends your scan data, document images and technical details to support so they can help resolve a scanning issue.',
51
51
  'diagnosticReport.continue': 'Continue',
52
52
  'diagnosticReport.cancel': 'Cancel',
53
- 'diagnosticReport.sentTitle': 'Report sent',
54
- 'diagnosticReport.sentBody': 'Your diagnostic report was sent to support. Thank you.',
53
+ 'diagnosticReport.sending': 'Sending diagnostic report',
54
+ 'diagnosticReport.sendingPreparing': 'Preparing…',
55
+ 'diagnosticReport.sendingProgress': 'Preparing… {{pct}}%',
56
+ 'diagnosticReport.sentTitle': 'Thank you!',
57
+ 'diagnosticReport.sentBody': 'Thanks for sending your diagnostic data — it helps us improve scanning. Our team will look into the issue.',
55
58
  'diagnosticReport.failedTitle': "Couldn't send the report",
56
59
  'diagnosticReport.failedBody': 'Please try again later.',
57
60
  'livenessDetectionScreen.guideHeader': 'Face Verification',
@@ -50,8 +50,11 @@ export default {
50
50
  'diagnosticReport.noticeBody': 'Bu işlem; tarama verilerinizi, belge görüntülerinizi ve teknik ayrıntıları destek ekibine gönderir; böylece tarama sorununu çözebilirler.',
51
51
  'diagnosticReport.continue': 'Devam et',
52
52
  'diagnosticReport.cancel': 'İptal',
53
- 'diagnosticReport.sentTitle': 'Rapor gönderildi',
54
- 'diagnosticReport.sentBody': 'Tanılama raporunuz destek ekibine gönderildi. Teşekkürler.',
53
+ 'diagnosticReport.sending': 'Tanılama raporu gönderiliyor',
54
+ 'diagnosticReport.sendingPreparing': 'Hazırlanıyor…',
55
+ 'diagnosticReport.sendingProgress': 'Hazırlanıyor… %{{pct}}',
56
+ 'diagnosticReport.sentTitle': 'Teşekkürler!',
57
+ 'diagnosticReport.sentBody': 'Tanılama verilerinizi gönderdiğiniz için teşekkürler — taramayı iyileştirmemize yardımcı oluyor. Ekibimiz sorunu inceleyecek.',
55
58
  'diagnosticReport.failedTitle': 'Rapor gönderilemedi',
56
59
  'diagnosticReport.failedBody': 'Lütfen daha sonra tekrar deneyin.',
57
60
  'livenessDetectionScreen.guideHeader': 'Yüz Doğrulaması',
@@ -2,4 +2,4 @@
2
2
 
3
3
  // This file is auto-generated. Do not edit manually.
4
4
  // Version is synced from package.json during build.
5
- export const SDK_VERSION = '1.493.0';
5
+ export const SDK_VERSION = '1.493.2';
@@ -11,10 +11,11 @@ export interface DiagnosticReportButtonProps {
11
11
  /**
12
12
  * "Report a scanning issue" button for the result screen.
13
13
  *
14
- * Shows a brief notice describing what will be shared (acting as the user's
15
- * consent step), then uploads the diagnostic bundle (technical signals +
16
- * scanned data + captured images) to the backend, encrypted in transit and at
17
- * rest. Operators view and download the reports from the dashboard.
14
+ * Uploads the diagnostic bundle to the backend (proof-of-work authenticated,
15
+ * encrypted in transit + at rest). The PoW solve is the only slow step, so while
16
+ * sending we show a NON-DISMISSIBLE modal overlay with progress the user can't
17
+ * dismiss it accidentally (tap-outside / Android-back do nothing), but can
18
+ * explicitly Cancel, which aborts the solve. A thank-you shows on success.
18
19
  *
19
20
  * Hidden when there's no session/baseUrl to upload to (e.g. before init).
20
21
  */
@@ -1 +1 @@
1
- {"version":3,"file":"DiagnosticReportButton.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/DiagnosticReportButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAIxC,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,MAAM,WAAW,2BAA2B;IAC1C,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,QAAA,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAsDjE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"DiagnosticReportButton.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/DiagnosticReportButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2B,MAAM,OAAO,CAAC;AAYhD,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,MAAM,WAAW,2BAA2B;IAC1C,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED;;;;;;;;;;GAUG;AACH,QAAA,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAkGjE,CAAC;AA2CF,eAAe,sBAAsB,CAAC"}
@@ -14,9 +14,32 @@ export interface PowChallenge {
14
14
  expiresAt: number;
15
15
  }
16
16
  /**
17
- * Solve the PoW for a challenge. Returns the solution nonce as a string.
18
- * Bounded by `maxIterations` so a pathological difficulty can't hang the app;
19
- * returns null if unsolved within the bound.
17
+ * Solve the PoW for a challenge (synchronous). Returns the solution nonce as a
18
+ * string. Bounded by `maxIterations` so a pathological difficulty can't hang the
19
+ * app; returns null if unsolved within the bound.
20
+ *
21
+ * Prefer `solvePowAsync` on the UI thread — this blocks until solved.
20
22
  */
21
23
  export declare const solvePow: (challenge: string, difficulty: number, maxIterations?: number) => string | null;
24
+ /** Thrown by solvePowAsync / sendDiagnosticReport when the user cancels. */
25
+ export declare class PowCancelledError extends Error {
26
+ constructor();
27
+ }
28
+ export interface SolvePowAsyncOptions {
29
+ onProgress?: (fraction: number) => void;
30
+ /** Polled between chunks; return true to abort. Throws PowCancelledError. */
31
+ shouldCancel?: () => boolean;
32
+ maxIterations?: number;
33
+ chunkSize?: number;
34
+ }
35
+ /**
36
+ * Async, UI-friendly PoW solver. Hashes in small chunks and yields to the event
37
+ * loop between chunks so the JS thread (and the UI) stays responsive instead of
38
+ * freezing during the solve. Reports coarse progress (0..1, best-effort against
39
+ * the expected work for the difficulty) via `onProgress`, and aborts (throwing
40
+ * PowCancelledError) when `shouldCancel` returns true.
41
+ *
42
+ * Returns the solution nonce, or null if unsolved within `maxIterations`.
43
+ */
44
+ export declare const solvePowAsync: (challenge: string, difficulty: number, options?: SolvePowAsyncOptions) => Promise<string | null>;
22
45
  //# sourceMappingURL=diagnosticPow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"diagnosticPow.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/diagnosticPow.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAiBD;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GACnB,WAAW,MAAM,EACjB,YAAY,MAAM,EAClB,sBAAyB,KACxB,MAAM,GAAG,IASX,CAAC"}
1
+ {"version":3,"file":"diagnosticPow.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/diagnosticPow.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAiBD;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,GACnB,WAAW,MAAM,EACjB,YAAY,MAAM,EAClB,sBAAyB,KACxB,MAAM,GAAG,IASX,CAAC;AAEF,4EAA4E;AAC5E,qBAAa,iBAAkB,SAAQ,KAAK;;CAK3C;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,GACxB,WAAW,MAAM,EACjB,YAAY,MAAM,EAClB,UAAS,oBAAyB,KACjC,OAAO,CAAC,MAAM,GAAG,IAAI,CAmCvB,CAAC"}
@@ -14,10 +14,19 @@ export interface SendDiagnosticParams {
14
14
  sdkVersion: string;
15
15
  /** base64 images to attach (MRZ side / front / face / NFC chip face). */
16
16
  images?: DiagnosticImage[];
17
+ /**
18
+ * Progress callback for the proof-of-work step (0..1). The PoW solve is the
19
+ * only slow part; the UI uses this to show a progress bar instead of freezing.
20
+ */
21
+ onPowProgress?: (fraction: number) => void;
22
+ /** Polled during the solve; return true to cancel. */
23
+ shouldCancel?: () => boolean;
17
24
  }
18
25
  export interface SendResult {
19
26
  /** True when the report was uploaded and stored. */
20
27
  uploaded: boolean;
28
+ /** True when the user cancelled before completion. */
29
+ cancelled?: boolean;
21
30
  }
22
31
  /**
23
32
  * Build the report and upload it (over HTTPS) to the demo-diagnostics endpoint.
@@ -1 +1 @@
1
- {"version":3,"file":"sendDiagnosticReport.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/sendDiagnosticReport.ts"],"names":[],"mappings":"AAqBA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,iBAAiB,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAKD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,oBAAoB,KAC3B,OAAO,CAAC,UAAU,CAsFpB,CAAC"}
1
+ {"version":3,"file":"sendDiagnosticReport.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/sendDiagnosticReport.ts"],"names":[],"mappings":"AAqBA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAQ5B,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,iBAAiB,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,QAAQ,EAAE,OAAO,CAAC;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAKD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,oBAAoB,KAC3B,OAAO,CAAC,UAAU,CA2GpB,CAAC"}
@@ -48,6 +48,9 @@ declare const _default: {
48
48
  'diagnosticReport.noticeBody': string;
49
49
  'diagnosticReport.continue': string;
50
50
  'diagnosticReport.cancel': string;
51
+ 'diagnosticReport.sending': string;
52
+ 'diagnosticReport.sendingPreparing': string;
53
+ 'diagnosticReport.sendingProgress': string;
51
54
  'diagnosticReport.sentTitle': string;
52
55
  'diagnosticReport.sentBody': string;
53
56
  'diagnosticReport.failedTitle': string;
@@ -1 +1 @@
1
- {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/en.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAgRE"}
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/en.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAoRE"}
@@ -48,6 +48,9 @@ declare const _default: {
48
48
  'diagnosticReport.noticeBody': string;
49
49
  'diagnosticReport.continue': string;
50
50
  'diagnosticReport.cancel': string;
51
+ 'diagnosticReport.sending': string;
52
+ 'diagnosticReport.sendingPreparing': string;
53
+ 'diagnosticReport.sendingProgress': string;
51
54
  'diagnosticReport.sentTitle': string;
52
55
  'diagnosticReport.sentBody': string;
53
56
  'diagnosticReport.failedTitle': string;
@@ -1 +1 @@
1
- {"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/tr.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAmRE"}
1
+ {"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../../../../src/Translation/Resources/tr.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAuRE"}
@@ -1,2 +1,2 @@
1
- export declare const SDK_VERSION = "1.493.0";
1
+ export declare const SDK_VERSION = "1.493.2";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustchex/react-native-sdk",
3
- "version": "1.493.0",
3
+ "version": "1.493.2",
4
4
  "description": "Trustchex mobile app react native SDK for android or ios devices",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,5 +1,13 @@
1
- import React, { useState } from 'react';
2
- import { Alert } from 'react-native';
1
+ import React, { useRef, useState } from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ Alert,
5
+ Modal,
6
+ StyleSheet,
7
+ Text,
8
+ TouchableOpacity,
9
+ View,
10
+ } from 'react-native';
3
11
  import { useTranslation } from 'react-i18next';
4
12
  import StyledButton from './StyledButton';
5
13
  import {
@@ -20,10 +28,11 @@ export interface DiagnosticReportButtonProps {
20
28
  /**
21
29
  * "Report a scanning issue" button for the result screen.
22
30
  *
23
- * Shows a brief notice describing what will be shared (acting as the user's
24
- * consent step), then uploads the diagnostic bundle (technical signals +
25
- * scanned data + captured images) to the backend, encrypted in transit and at
26
- * rest. Operators view and download the reports from the dashboard.
31
+ * Uploads the diagnostic bundle to the backend (proof-of-work authenticated,
32
+ * encrypted in transit + at rest). The PoW solve is the only slow step, so while
33
+ * sending we show a NON-DISMISSIBLE modal overlay with progress the user can't
34
+ * dismiss it accidentally (tap-outside / Android-back do nothing), but can
35
+ * explicitly Cancel, which aborts the solve. A thank-you shows on success.
27
36
  *
28
37
  * Hidden when there's no session/baseUrl to upload to (e.g. before init).
29
38
  */
@@ -35,11 +44,43 @@ const DiagnosticReportButton: React.FC<DiagnosticReportButtonProps> = ({
35
44
  }) => {
36
45
  const { t } = useTranslation();
37
46
  const [sending, setSending] = useState(false);
47
+ const [progress, setProgress] = useState(0);
48
+ // Ref so the running solve sees cancellation immediately (no stale closure).
49
+ const cancelledRef = useRef(false);
38
50
 
39
51
  if (!baseUrl || !sessionId) {
40
52
  return null;
41
53
  }
42
54
 
55
+ const doSend = async () => {
56
+ cancelledRef.current = false;
57
+ setProgress(0);
58
+ setSending(true);
59
+ try {
60
+ const { uploaded, cancelled } = await sendDiagnosticReport({
61
+ baseUrl,
62
+ sessionId,
63
+ scan,
64
+ sdkVersion: SDK_VERSION,
65
+ images,
66
+ onPowProgress: setProgress,
67
+ shouldCancel: () => cancelledRef.current,
68
+ });
69
+ setSending(false);
70
+ if (cancelled) return; // user cancelled — no alert
71
+ Alert.alert(
72
+ uploaded
73
+ ? t('diagnosticReport.sentTitle')
74
+ : t('diagnosticReport.failedTitle'),
75
+ uploaded
76
+ ? t('diagnosticReport.sentBody')
77
+ : t('diagnosticReport.failedBody')
78
+ );
79
+ } catch {
80
+ setSending(false);
81
+ }
82
+ };
83
+
43
84
  const onPress = () => {
44
85
  if (sending) return;
45
86
  Alert.alert(
@@ -47,40 +88,93 @@ const DiagnosticReportButton: React.FC<DiagnosticReportButtonProps> = ({
47
88
  t('diagnosticReport.noticeBody'),
48
89
  [
49
90
  { text: t('diagnosticReport.cancel'), style: 'cancel' },
50
- {
51
- text: t('diagnosticReport.continue'),
52
- onPress: async () => {
53
- setSending(true);
54
- try {
55
- const { uploaded } = await sendDiagnosticReport({
56
- baseUrl,
57
- sessionId,
58
- scan,
59
- sdkVersion: SDK_VERSION,
60
- images,
61
- });
62
- Alert.alert(
63
- uploaded
64
- ? t('diagnosticReport.sentTitle')
65
- : t('diagnosticReport.failedTitle'),
66
- uploaded
67
- ? t('diagnosticReport.sentBody')
68
- : t('diagnosticReport.failedBody')
69
- );
70
- } finally {
71
- setSending(false);
72
- }
73
- },
74
- },
91
+ { text: t('diagnosticReport.continue'), onPress: doSend },
75
92
  ]
76
93
  );
77
94
  };
78
95
 
96
+ const pct = Math.round(progress * 100);
97
+
79
98
  return (
80
- <StyledButton mode="outlined" onPress={onPress} disabled={sending}>
81
- {t('diagnosticReport.button')}
82
- </StyledButton>
99
+ <>
100
+ <StyledButton mode="outlined" onPress={onPress}>
101
+ {t('diagnosticReport.button')}
102
+ </StyledButton>
103
+
104
+ <Modal
105
+ visible={sending}
106
+ transparent
107
+ animationType="fade"
108
+ // Block accidental dismissal: Android hardware-back does nothing while
109
+ // sending. There is no tap-outside-to-close (we render our own backdrop).
110
+ onRequestClose={() => {}}
111
+ >
112
+ <View style={styles.backdrop}>
113
+ <View style={styles.card}>
114
+ <ActivityIndicator size="large" />
115
+ <Text style={styles.title}>{t('diagnosticReport.sending')}</Text>
116
+ <Text style={styles.progress}>
117
+ {pct > 0
118
+ ? t('diagnosticReport.sendingProgress', { pct })
119
+ : t('diagnosticReport.sendingPreparing')}
120
+ </Text>
121
+ <TouchableOpacity
122
+ accessibilityRole="button"
123
+ style={styles.cancelBtn}
124
+ onPress={() => {
125
+ cancelledRef.current = true;
126
+ }}
127
+ >
128
+ <Text style={styles.cancelText}>
129
+ {t('diagnosticReport.cancel')}
130
+ </Text>
131
+ </TouchableOpacity>
132
+ </View>
133
+ </View>
134
+ </Modal>
135
+ </>
83
136
  );
84
137
  };
85
138
 
139
+ const styles = StyleSheet.create({
140
+ backdrop: {
141
+ flex: 1,
142
+ backgroundColor: 'rgba(0,0,0,0.45)',
143
+ alignItems: 'center',
144
+ justifyContent: 'center',
145
+ padding: 24,
146
+ },
147
+ card: {
148
+ width: '100%',
149
+ maxWidth: 320,
150
+ backgroundColor: 'white',
151
+ borderRadius: 16,
152
+ paddingVertical: 24,
153
+ paddingHorizontal: 20,
154
+ alignItems: 'center',
155
+ gap: 12,
156
+ },
157
+ title: {
158
+ fontSize: 16,
159
+ fontWeight: '600',
160
+ color: '#111827',
161
+ textAlign: 'center',
162
+ },
163
+ progress: {
164
+ fontSize: 14,
165
+ color: '#6B7280',
166
+ textAlign: 'center',
167
+ },
168
+ cancelBtn: {
169
+ marginTop: 8,
170
+ paddingVertical: 10,
171
+ paddingHorizontal: 24,
172
+ },
173
+ cancelText: {
174
+ fontSize: 15,
175
+ fontWeight: '600',
176
+ color: '#DC2626',
177
+ },
178
+ });
179
+
86
180
  export default DiagnosticReportButton;
@@ -34,9 +34,11 @@ const leadingZeroBits = (bytes: Uint8Array): number => {
34
34
  };
35
35
 
36
36
  /**
37
- * Solve the PoW for a challenge. Returns the solution nonce as a string.
38
- * Bounded by `maxIterations` so a pathological difficulty can't hang the app;
39
- * returns null if unsolved within the bound.
37
+ * Solve the PoW for a challenge (synchronous). Returns the solution nonce as a
38
+ * string. Bounded by `maxIterations` so a pathological difficulty can't hang the
39
+ * app; returns null if unsolved within the bound.
40
+ *
41
+ * Prefer `solvePowAsync` on the UI thread — this blocks until solved.
40
42
  */
41
43
  export const solvePow = (
42
44
  challenge: string,
@@ -52,3 +54,69 @@ export const solvePow = (
52
54
  }
53
55
  return null;
54
56
  };
57
+
58
+ /** Thrown by solvePowAsync / sendDiagnosticReport when the user cancels. */
59
+ export class PowCancelledError extends Error {
60
+ constructor() {
61
+ super('Proof-of-work cancelled');
62
+ this.name = 'PowCancelledError';
63
+ }
64
+ }
65
+
66
+ export interface SolvePowAsyncOptions {
67
+ onProgress?: (fraction: number) => void;
68
+ /** Polled between chunks; return true to abort. Throws PowCancelledError. */
69
+ shouldCancel?: () => boolean;
70
+ maxIterations?: number;
71
+ chunkSize?: number;
72
+ }
73
+
74
+ /**
75
+ * Async, UI-friendly PoW solver. Hashes in small chunks and yields to the event
76
+ * loop between chunks so the JS thread (and the UI) stays responsive instead of
77
+ * freezing during the solve. Reports coarse progress (0..1, best-effort against
78
+ * the expected work for the difficulty) via `onProgress`, and aborts (throwing
79
+ * PowCancelledError) when `shouldCancel` returns true.
80
+ *
81
+ * Returns the solution nonce, or null if unsolved within `maxIterations`.
82
+ */
83
+ export const solvePowAsync = async (
84
+ challenge: string,
85
+ difficulty: number,
86
+ options: SolvePowAsyncOptions = {}
87
+ ): Promise<string | null> => {
88
+ const {
89
+ onProgress,
90
+ shouldCancel,
91
+ maxIterations = 8_000_000,
92
+ // Hash a large chunk between yields: the per-yield setTimeout(0) round-trip
93
+ // on Hermes costs ~ms, so yielding every 2k hashes dominated wall-clock.
94
+ // 25k keeps the UI responsive + cancel snappy (a chunk is a few tens of ms)
95
+ // while spending almost all time hashing, not bouncing through the event loop.
96
+ chunkSize = 25_000,
97
+ } = options;
98
+ // Expected hashes ≈ 2^difficulty; used only to render a smooth-ish bar.
99
+ const expected = Math.max(1, Math.pow(2, difficulty));
100
+ // Encode the fixed "challenge." prefix once; per iteration we only encode the
101
+ // small varying nonce and concat, instead of re-UTF-8-encoding the whole
102
+ // string (the challenge is 32+ chars) on every hash.
103
+ const prefix = Buffer.from(`${challenge}.`, 'utf-8');
104
+ for (let i = 0; i < maxIterations; i++) {
105
+ const digest = sha256(
106
+ Buffer.concat([prefix, Buffer.from(i.toString(), 'utf-8')])
107
+ );
108
+ if (leadingZeroBits(digest) >= difficulty) {
109
+ onProgress?.(1);
110
+ return i.toString();
111
+ }
112
+ if (i % chunkSize === chunkSize - 1) {
113
+ if (shouldCancel?.()) {
114
+ throw new PowCancelledError();
115
+ }
116
+ onProgress?.(Math.min(0.99, i / expected));
117
+ // Yield so React can paint the progress and the UI stays responsive.
118
+ await new Promise<void>((resolve) => setTimeout(resolve, 0));
119
+ }
120
+ }
121
+ return null;
122
+ };
@@ -23,7 +23,11 @@ import {
23
23
  buildDiagnosticReport,
24
24
  type ScanDataForReport,
25
25
  } from './diagnosticReport';
26
- import { solvePow, type PowChallenge } from './diagnosticPow';
26
+ import {
27
+ solvePowAsync,
28
+ PowCancelledError,
29
+ type PowChallenge,
30
+ } from './diagnosticPow';
27
31
  import { logError } from './debug.utils';
28
32
 
29
33
  export interface DiagnosticImage {
@@ -42,11 +46,20 @@ export interface SendDiagnosticParams {
42
46
  sdkVersion: string;
43
47
  /** base64 images to attach (MRZ side / front / face / NFC chip face). */
44
48
  images?: DiagnosticImage[];
49
+ /**
50
+ * Progress callback for the proof-of-work step (0..1). The PoW solve is the
51
+ * only slow part; the UI uses this to show a progress bar instead of freezing.
52
+ */
53
+ onPowProgress?: (fraction: number) => void;
54
+ /** Polled during the solve; return true to cancel. */
55
+ shouldCancel?: () => boolean;
45
56
  }
46
57
 
47
58
  export interface SendResult {
48
59
  /** True when the report was uploaded and stored. */
49
60
  uploaded: boolean;
61
+ /** True when the user cancelled before completion. */
62
+ cancelled?: boolean;
50
63
  }
51
64
 
52
65
  const mimeToType = (mime: 'jpg' | 'png'): string =>
@@ -60,7 +73,15 @@ const mimeToType = (mime: 'jpg' | 'png'): string =>
60
73
  export const sendDiagnosticReport = async (
61
74
  params: SendDiagnosticParams
62
75
  ): Promise<SendResult> => {
63
- const { baseUrl, sessionId, scan, sdkVersion, images = [] } = params;
76
+ const {
77
+ baseUrl,
78
+ sessionId,
79
+ scan,
80
+ sdkVersion,
81
+ images = [],
82
+ onPowProgress,
83
+ shouldCancel,
84
+ } = params;
64
85
  if (!baseUrl) {
65
86
  return { uploaded: false };
66
87
  }
@@ -121,10 +142,20 @@ export const sendDiagnosticReport = async (
121
142
  return { uploaded: false };
122
143
  }
123
144
  const challenge = (await challengeRes.json()) as PowChallenge;
124
- const solution = solvePow(challenge.challenge, challenge.difficulty);
145
+ const solution = await solvePowAsync(
146
+ challenge.challenge,
147
+ challenge.difficulty,
148
+ {
149
+ onProgress: onPowProgress,
150
+ shouldCancel,
151
+ }
152
+ );
125
153
  if (!solution) {
126
154
  return { uploaded: false };
127
155
  }
156
+ if (shouldCancel?.()) {
157
+ return { uploaded: false, cancelled: true };
158
+ }
128
159
 
129
160
  const response = await fetch(`${baseUrl}/api/app/mobile/diagnostics`, {
130
161
  method: 'POST',
@@ -138,6 +169,9 @@ export const sendDiagnosticReport = async (
138
169
 
139
170
  return { uploaded: response.ok };
140
171
  } catch (e) {
172
+ if (e instanceof PowCancelledError) {
173
+ return { uploaded: false, cancelled: true };
174
+ }
141
175
  logError(
142
176
  'Diagnostics',
143
177
  'Failed to upload diagnostic report',
@@ -59,8 +59,12 @@ export default {
59
59
  'This sends your scan data, document images and technical details to support so they can help resolve a scanning issue.',
60
60
  'diagnosticReport.continue': 'Continue',
61
61
  'diagnosticReport.cancel': 'Cancel',
62
- 'diagnosticReport.sentTitle': 'Report sent',
63
- 'diagnosticReport.sentBody': 'Your diagnostic report was sent to support. Thank you.',
62
+ 'diagnosticReport.sending': 'Sending diagnostic report',
63
+ 'diagnosticReport.sendingPreparing': 'Preparing…',
64
+ 'diagnosticReport.sendingProgress': 'Preparing… {{pct}}%',
65
+ 'diagnosticReport.sentTitle': 'Thank you!',
66
+ 'diagnosticReport.sentBody':
67
+ 'Thanks for sending your diagnostic data — it helps us improve scanning. Our team will look into the issue.',
64
68
  'diagnosticReport.failedTitle': "Couldn't send the report",
65
69
  'diagnosticReport.failedBody': 'Please try again later.',
66
70
  'livenessDetectionScreen.guideHeader': 'Face Verification',
@@ -59,8 +59,12 @@ export default {
59
59
  'Bu işlem; tarama verilerinizi, belge görüntülerinizi ve teknik ayrıntıları destek ekibine gönderir; böylece tarama sorununu çözebilirler.',
60
60
  'diagnosticReport.continue': 'Devam et',
61
61
  'diagnosticReport.cancel': 'İptal',
62
- 'diagnosticReport.sentTitle': 'Rapor gönderildi',
63
- 'diagnosticReport.sentBody': 'Tanılama raporunuz destek ekibine gönderildi. Teşekkürler.',
62
+ 'diagnosticReport.sending': 'Tanılama raporu gönderiliyor',
63
+ 'diagnosticReport.sendingPreparing': 'Hazırlanıyor…',
64
+ 'diagnosticReport.sendingProgress': 'Hazırlanıyor… %{{pct}}',
65
+ 'diagnosticReport.sentTitle': 'Teşekkürler!',
66
+ 'diagnosticReport.sentBody':
67
+ 'Tanılama verilerinizi gönderdiğiniz için teşekkürler — taramayı iyileştirmemize yardımcı oluyor. Ekibimiz sorunu inceleyecek.',
64
68
  'diagnosticReport.failedTitle': 'Rapor gönderilemedi',
65
69
  'diagnosticReport.failedBody': 'Lütfen daha sonra tekrar deneyin.',
66
70
  'livenessDetectionScreen.guideHeader': 'Yüz Doğrulaması',
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
2
  // Version is synced from package.json during build.
3
- export const SDK_VERSION = '1.493.0';
3
+ export const SDK_VERSION = '1.493.2';