@trustchex/react-native-sdk 1.475.1 → 1.481.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +76 -24
- package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +1 -0
- package/ios/Camera/TrustchexCameraView.swift +20 -15
- package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +3 -2
- package/lib/module/Screens/Static/QrCodeScanningScreen.js +12 -2
- package/lib/module/Screens/Static/ResultScreen.js +145 -113
- package/lib/module/Shared/Components/DebugOverlay.js +5 -2
- package/lib/module/Shared/Components/DiagnosticReportButton.js +64 -0
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +185 -30
- package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +8 -1
- package/lib/module/Shared/Components/NavigationManager.js +7 -9
- package/lib/module/Shared/EIDReader/eidReader.js +159 -38
- package/lib/module/Shared/Libs/MRZ_KNOWN_ISSUES.md +41 -0
- package/lib/module/Shared/Libs/SignalingClient.js +6 -5
- package/lib/module/Shared/Libs/crypto.utils.js +25 -3
- package/lib/module/Shared/Libs/deeplink.utils.js +37 -7
- package/lib/module/Shared/Libs/diagnosticReport.js +133 -0
- package/lib/module/Shared/Libs/diagnostics.js +171 -0
- package/lib/module/Shared/Libs/http-client.js +19 -6
- package/lib/module/Shared/Libs/mrz.utils.js +193 -33
- package/lib/module/Shared/Libs/mrzFrameAggregator.js +74 -3
- package/lib/module/Shared/Libs/native-device-info.utils.js +67 -0
- package/lib/module/Shared/Libs/promise.utils.js +4 -3
- package/lib/module/Shared/Libs/sendDiagnosticReport.js +130 -0
- package/lib/module/Shared/Libs/shuffle.utils.js +17 -0
- package/lib/module/Shared/Services/AnalyticsService.js +8 -2
- package/lib/module/Shared/Services/DataUploadService.js +114 -127
- package/lib/module/Shared/Services/VideoSessionService.js +4 -3
- package/lib/module/Translation/Resources/en.js +6 -0
- package/lib/module/Translation/Resources/tr.js +6 -0
- package/lib/module/Trustchex.js +20 -4
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DiagnosticReportButton.d.ts +20 -0
- package/lib/typescript/src/Shared/Components/DiagnosticReportButton.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts +10 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts +10 -0
- package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/crypto.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts +1 -0
- package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/diagnosticReport.d.ts +48 -0
- package/lib/typescript/src/Shared/Libs/diagnosticReport.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/diagnostics.d.ts +145 -0
- package/lib/typescript/src/Shared/Libs/diagnostics.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/http-client.d.ts +2 -0
- package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/mrzFrameAggregator.d.ts +55 -0
- package/lib/typescript/src/Shared/Libs/mrzFrameAggregator.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/native-device-info.utils.d.ts +33 -0
- package/lib/typescript/src/Shared/Libs/native-device-info.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/sendDiagnosticReport.d.ts +30 -0
- package/lib/typescript/src/Shared/Libs/sendDiagnosticReport.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/shuffle.utils.d.ts +3 -0
- package/lib/typescript/src/Shared/Libs/shuffle.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/AnalyticsService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +6 -0
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +6 -0
- package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
- package/lib/typescript/src/Trustchex.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +17 -3
- package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +4 -3
- package/src/Screens/Static/QrCodeScanningScreen.tsx +19 -2
- package/src/Screens/Static/ResultScreen.tsx +172 -162
- package/src/Shared/Components/DebugOverlay.tsx +5 -2
- package/src/Shared/Components/DiagnosticReportButton.tsx +77 -0
- package/src/Shared/Components/IdentityDocumentCamera.tsx +200 -29
- package/src/Shared/Components/IdentityDocumentCamera.types.ts +6 -0
- package/src/Shared/Components/IdentityDocumentCamera.utils.ts +9 -1
- package/src/Shared/Components/NavigationManager.tsx +7 -7
- package/src/Shared/Components/TrustchexCamera.tsx +6 -0
- package/src/Shared/EIDReader/eidReader.ts +181 -52
- package/src/Shared/Libs/MRZ_KNOWN_ISSUES.md +41 -0
- package/src/Shared/Libs/SignalingClient.ts +10 -5
- package/src/Shared/Libs/crypto.utils.ts +41 -4
- package/src/Shared/Libs/deeplink.utils.ts +39 -7
- package/src/Shared/Libs/diagnosticReport.ts +206 -0
- package/src/Shared/Libs/diagnostics.ts +251 -0
- package/src/Shared/Libs/http-client.ts +24 -8
- package/src/Shared/Libs/mrz.utils.ts +200 -34
- package/src/Shared/Libs/mrzFrameAggregator.ts +112 -2
- package/src/Shared/Libs/native-device-info.utils.ts +116 -0
- package/src/Shared/Libs/promise.utils.ts +11 -5
- package/src/Shared/Libs/sendDiagnosticReport.ts +165 -0
- package/src/Shared/Libs/shuffle.utils.ts +15 -0
- package/src/Shared/Services/AnalyticsService.ts +17 -1
- package/src/Shared/Services/DataUploadService.ts +155 -166
- package/src/Shared/Services/VideoSessionService.ts +15 -3
- package/src/Translation/Resources/en.ts +7 -0
- package/src/Translation/Resources/tr.ts +7 -0
- package/src/Trustchex.tsx +26 -3
- package/src/version.ts +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { debugLog } from './debug.utils';
|
|
1
2
|
const runWithRetry = async <T>(
|
|
2
3
|
fn: () => Promise<T>,
|
|
3
4
|
maxRetries: number = 3,
|
|
@@ -9,26 +10,31 @@ const runWithRetry = async <T>(
|
|
|
9
10
|
while (retries < maxRetries) {
|
|
10
11
|
try {
|
|
11
12
|
if (retries > 0) {
|
|
12
|
-
|
|
13
|
+
debugLog('Retry', `[Retry] Attempt ${retries + 1}/${maxRetries}...`);
|
|
13
14
|
}
|
|
14
15
|
result = await fn();
|
|
15
16
|
if (retries > 0) {
|
|
16
|
-
|
|
17
|
+
debugLog('Retry', `[Retry] ✓ Success on attempt ${retries + 1}`);
|
|
17
18
|
}
|
|
18
19
|
return result;
|
|
19
20
|
} catch (error) {
|
|
20
21
|
lastError = error;
|
|
21
22
|
retries++;
|
|
22
|
-
console.error(
|
|
23
|
+
console.error(
|
|
24
|
+
`[Retry] ✗ Attempt ${retries}/${maxRetries} failed:`,
|
|
25
|
+
error instanceof Error ? error.message : error
|
|
26
|
+
);
|
|
23
27
|
if (retries < maxRetries) {
|
|
24
28
|
const waitTime = delay * retries;
|
|
25
|
-
|
|
29
|
+
debugLog('Retry', `[Retry] Waiting ${waitTime}ms before retry...`);
|
|
26
30
|
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
}
|
|
30
34
|
console.error('[Retry] ✗ All retries exhausted. Last error:', lastError);
|
|
31
|
-
throw new Error(
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Max retries (${maxRetries}) exceeded. Last error: ${lastError instanceof Error ? lastError.message : String(lastError)}`
|
|
37
|
+
);
|
|
32
38
|
};
|
|
33
39
|
|
|
34
40
|
export { runWithRetry };
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assembles and shares the support diagnostic report.
|
|
3
|
+
*
|
|
4
|
+
* `react-native-share` is an OPTIONAL dependency: the host app opts into the
|
|
5
|
+
* diagnostic feature by installing it. When it's present we open the OS share
|
|
6
|
+
* sheet with the full report (the user picks email / WhatsApp / Drive / etc. and
|
|
7
|
+
* reviews before sending); when it's absent the feature is silently unavailable
|
|
8
|
+
* — `isDiagnosticSharingAvailable()` returns false and the result-screen button
|
|
9
|
+
* renders nothing, so apps that don't want it incur no dependency.
|
|
10
|
+
*
|
|
11
|
+
* Attached files (staged to a temp dir via react-native-fs, already an SDK dep):
|
|
12
|
+
* - `diagnostics.json` — technical signals (device, camera, MRZ, NFC steps),
|
|
13
|
+
* - `scan-data.json` — the scanned identity data (PII; user reviews it),
|
|
14
|
+
* - the captured document/face images.
|
|
15
|
+
*
|
|
16
|
+
* The readable summary rides as the share `message` so it's visible in the
|
|
17
|
+
* draft regardless of the chosen app.
|
|
18
|
+
*/
|
|
19
|
+
import RNFS from 'react-native-fs';
|
|
20
|
+
import { diagnostics } from './diagnostics';
|
|
21
|
+
import { gatherDeviceDetails } from './native-device-info.utils';
|
|
22
|
+
import {
|
|
23
|
+
buildDiagnosticReport,
|
|
24
|
+
type ScanDataForReport,
|
|
25
|
+
} from './diagnosticReport';
|
|
26
|
+
import { logError } from './debug.utils';
|
|
27
|
+
|
|
28
|
+
// `react-native-share` is OPTIONAL. Load it softly so the SDK bundles and runs
|
|
29
|
+
// without it; only host apps that installed it get the diagnostic feature. A
|
|
30
|
+
// static `import` would make Metro fail to resolve the module when it's absent,
|
|
31
|
+
// so we use a guarded require and keep the reference typed loosely.
|
|
32
|
+
type ShareModule = {
|
|
33
|
+
open: (options: Record<string, unknown>) => Promise<unknown>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let shareModule: ShareModule | null | undefined;
|
|
37
|
+
|
|
38
|
+
const loadShare = (): ShareModule | null => {
|
|
39
|
+
if (shareModule !== undefined) return shareModule;
|
|
40
|
+
try {
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
42
|
+
const mod = require('react-native-share');
|
|
43
|
+
shareModule = (mod?.default ?? mod) as ShareModule;
|
|
44
|
+
} catch {
|
|
45
|
+
shareModule = null; // not installed — feature unavailable
|
|
46
|
+
}
|
|
47
|
+
return shareModule;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Whether diagnostic sharing is available (i.e. `react-native-share` is
|
|
52
|
+
* installed in the host app). The result-screen button hides itself when false.
|
|
53
|
+
*/
|
|
54
|
+
export const isDiagnosticSharingAvailable = (): boolean => loadShare() != null;
|
|
55
|
+
|
|
56
|
+
export interface DiagnosticImage {
|
|
57
|
+
/** Stable file name without extension, e.g. "mrz-side". */
|
|
58
|
+
name: string;
|
|
59
|
+
base64: string;
|
|
60
|
+
mime: 'jpg' | 'png';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface SendDiagnosticParams {
|
|
64
|
+
scan: ScanDataForReport;
|
|
65
|
+
sdkVersion: string;
|
|
66
|
+
sessionId?: string;
|
|
67
|
+
/** base64 images to attach (MRZ side / front / face / NFC chip face). */
|
|
68
|
+
images?: DiagnosticImage[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
const writeTemp = async (
|
|
73
|
+
dir: string,
|
|
74
|
+
name: string,
|
|
75
|
+
data: string,
|
|
76
|
+
encoding: 'utf8' | 'base64'
|
|
77
|
+
): Promise<string> => {
|
|
78
|
+
const path = `${dir}/${name}`;
|
|
79
|
+
await RNFS.writeFile(path, data, encoding);
|
|
80
|
+
return path.startsWith('file://') ? path : `file://${path}`;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export interface SendResult {
|
|
84
|
+
shared: boolean;
|
|
85
|
+
/** True when the user dismissed the share sheet (not an error). */
|
|
86
|
+
dismissed?: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Build the report, stage attachments, and present the share sheet. Returns
|
|
91
|
+
* { shared } — false on error, with `dismissed` set when the user cancelled.
|
|
92
|
+
*/
|
|
93
|
+
export const sendDiagnosticReport = async (
|
|
94
|
+
params: SendDiagnosticParams
|
|
95
|
+
): Promise<SendResult> => {
|
|
96
|
+
const share = loadShare();
|
|
97
|
+
if (!share) {
|
|
98
|
+
// Optional dependency not installed — nothing to do.
|
|
99
|
+
return { shared: false };
|
|
100
|
+
}
|
|
101
|
+
const { scan, sdkVersion, sessionId, images = [] } = params;
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const iso = new Date(now).toISOString();
|
|
104
|
+
|
|
105
|
+
const device = await gatherDeviceDetails();
|
|
106
|
+
const report = buildDiagnosticReport({
|
|
107
|
+
diag: diagnostics.snapshot(now, iso),
|
|
108
|
+
device,
|
|
109
|
+
scan,
|
|
110
|
+
sdkVersion,
|
|
111
|
+
sessionId,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const dir = `${RNFS.TemporaryDirectoryPath}/trustchex-diagnostics-${now}`;
|
|
115
|
+
try {
|
|
116
|
+
await RNFS.mkdir(dir);
|
|
117
|
+
const urls: string[] = [];
|
|
118
|
+
urls.push(
|
|
119
|
+
await writeTemp(dir, 'diagnostics.json', report.diagnosticsJson, 'utf8')
|
|
120
|
+
);
|
|
121
|
+
urls.push(
|
|
122
|
+
await writeTemp(dir, 'scan-data.json', report.scanDataJson, 'utf8')
|
|
123
|
+
);
|
|
124
|
+
for (const img of images) {
|
|
125
|
+
if (!img.base64) continue;
|
|
126
|
+
urls.push(
|
|
127
|
+
await writeTemp(dir, `${img.name}.${img.mime}`, img.base64, 'base64')
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
await share.open({
|
|
133
|
+
title: report.subject,
|
|
134
|
+
subject: report.subject, // used by email targets
|
|
135
|
+
message: report.body,
|
|
136
|
+
urls,
|
|
137
|
+
// Per-file MIME types so targets render attachments correctly.
|
|
138
|
+
filenames: urls.map((u) => u.split('/').pop() ?? 'attachment'),
|
|
139
|
+
failOnCancel: false,
|
|
140
|
+
});
|
|
141
|
+
return { shared: true };
|
|
142
|
+
} catch (shareErr) {
|
|
143
|
+
// react-native-share throws on user cancel unless failOnCancel:false; treat
|
|
144
|
+
// a cancel as a non-error dismissal.
|
|
145
|
+
const msg =
|
|
146
|
+
shareErr instanceof Error ? shareErr.message : String(shareErr);
|
|
147
|
+
if (/cancel|dismiss|user did not share/i.test(msg)) {
|
|
148
|
+
return { shared: false, dismissed: true };
|
|
149
|
+
}
|
|
150
|
+
throw shareErr;
|
|
151
|
+
}
|
|
152
|
+
} catch (e) {
|
|
153
|
+
logError(
|
|
154
|
+
'Diagnostics',
|
|
155
|
+
'Failed to share diagnostic report',
|
|
156
|
+
e instanceof Error ? e.message : String(e)
|
|
157
|
+
);
|
|
158
|
+
return { shared: false };
|
|
159
|
+
} finally {
|
|
160
|
+
// Best-effort cleanup — temp files are short-lived.
|
|
161
|
+
setTimeout(() => {
|
|
162
|
+
RNFS.unlink(dir).catch(() => {});
|
|
163
|
+
}, 60_000);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import 'react-native-get-random-values';
|
|
2
|
+
|
|
3
|
+
// Unbiased Fisher–Yates shuffle backed by a CSPRNG. Used for the liveness
|
|
4
|
+
// instruction order, which acts as an anti-replay challenge and must not be
|
|
5
|
+
// predictable.
|
|
6
|
+
export const secureShuffle = <T>(items: T[]): T[] => {
|
|
7
|
+
const result = [...items];
|
|
8
|
+
const random = new Uint32Array(1);
|
|
9
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
10
|
+
crypto.getRandomValues(random);
|
|
11
|
+
const j = random[0] % (i + 1);
|
|
12
|
+
[result[i], result[j]] = [result[j], result[i]];
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
@@ -318,7 +318,13 @@ class AnalyticsService implements IAnalyticsService {
|
|
|
318
318
|
throw new Error('Rate limit exceeded');
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
//
|
|
321
|
+
// Auth failures are transient from the SDK's perspective (e.g. a token
|
|
322
|
+
// rollout); throw so the batch is retried instead of silently dropped.
|
|
323
|
+
if (response.status === 401 || response.status === 403) {
|
|
324
|
+
throw new Error(`Authentication error: ${response.status}`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// For other 4xx errors (Bad Request, etc.), do NOT retry.
|
|
322
328
|
// We return successfully so the bad events are removed from the queue.
|
|
323
329
|
if (response.status >= 400 && response.status < 500) {
|
|
324
330
|
if (__DEV__)
|
|
@@ -450,6 +456,16 @@ class AnalyticsService implements IAnalyticsService {
|
|
|
450
456
|
'password',
|
|
451
457
|
'apikey',
|
|
452
458
|
'accesstoken',
|
|
459
|
+
'documentnumber',
|
|
460
|
+
'personalnumber',
|
|
461
|
+
'nationalid',
|
|
462
|
+
'tckimlik',
|
|
463
|
+
'mrz',
|
|
464
|
+
'mrztext',
|
|
465
|
+
'birthdate',
|
|
466
|
+
'dateofbirth',
|
|
467
|
+
'sessionkey',
|
|
468
|
+
'sessiontoken',
|
|
453
469
|
];
|
|
454
470
|
|
|
455
471
|
for (const [key, value] of Object.entries(metadata)) {
|