@smileid/usesmileid 12.2.0 → 12.3.0
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/CHANGELOG.md +5 -1
- package/README.md +57 -18
- package/dist/camera/bounding_box_metering_region.d.ts +10 -0
- package/dist/camera/bounding_box_metering_region.js +1 -0
- package/dist/camera/camera_defaults.d.ts +0 -2
- package/dist/camera/camera_defaults.js +1 -1
- package/dist/camera/document_image_pipeline.d.ts +9 -0
- package/dist/camera/document_image_pipeline.js +1 -1
- package/dist/camera/imported_document_folder.d.ts +15 -0
- package/dist/camera/imported_document_folder.js +1 -0
- package/dist/camera/kamera_config_factory.d.ts +11 -1
- package/dist/camera/kamera_config_factory.js +1 -1
- package/dist/camera/use_smile_id_frame_stream.js +1 -1
- package/dist/camera/use_smile_id_kamera_camera_system.d.ts +46 -2
- package/dist/camera/use_smile_id_kamera_camera_system.js +1 -1
- package/dist/core/exception/use_smile_id_error_code.js +1 -1
- package/dist/core/exception/use_smile_id_network_exception.d.ts +10 -3
- package/dist/core/exception/use_smile_id_network_exception.js +1 -1
- package/dist/core/is_development_build.d.ts +6 -0
- package/dist/core/is_development_build.js +1 -0
- package/dist/core/models/config.d.ts +6 -0
- package/dist/core/models/ml_config.d.ts +1 -1
- package/dist/core/models/network_config.d.ts +4 -1
- package/dist/core/models/params.d.ts +8 -6
- package/dist/core/models/result.d.ts +11 -0
- package/dist/core/models/result.js +1 -1
- package/dist/core/perf/capture_perf_trace.d.ts +90 -0
- package/dist/core/perf/capture_perf_trace.js +1 -0
- package/dist/data/api/interceptors/logging_interceptor.js +1 -1
- package/dist/data/api/interceptors/smile_auth_interceptor.js +1 -1
- package/dist/data/api/request_capture.d.ts +16 -0
- package/dist/data/api/request_capture.js +1 -0
- package/dist/data/api/smile_id_api.d.ts +1 -1
- package/dist/data/api/smile_id_api.js +1 -1
- package/dist/data/factory/networking_factory.d.ts +3 -1
- package/dist/data/factory/networking_factory.js +1 -1
- package/dist/data/model/captured_job.d.ts +49 -0
- package/dist/data/model/captured_job.js +1 -0
- package/dist/data/model/job_request.d.ts +1 -1
- package/dist/data/model/sealed_job_request.d.ts +15 -0
- package/dist/data/model/sealed_job_request.js +1 -0
- package/dist/data/model/token_payload.d.ts +5 -7
- package/dist/data/model/token_payload.js +1 -1
- package/dist/data/model/user_details_payload.d.ts +6 -4
- package/dist/data/repository/use_smile_id_repository.d.ts +7 -0
- package/dist/data/repository/use_smile_id_repository_impl.d.ts +29 -2
- package/dist/data/repository/use_smile_id_repository_impl.js +1 -1
- package/dist/data/security/job_package_crypto.d.ts +41 -0
- package/dist/data/security/job_package_crypto.js +1 -0
- package/dist/data/security/job_package_sealer.d.ts +35 -0
- package/dist/data/security/job_package_sealer.js +1 -0
- package/dist/data/security/signing_crypto.d.ts +34 -0
- package/dist/data/security/signing_crypto.js +1 -1
- package/dist/data/util/retry_util.d.ts +9 -6
- package/dist/data/util/retry_util.js +1 -1
- package/dist/debug/selfie_preview_debug_geometry.d.ts +13 -13
- package/dist/debug/selfie_preview_debug_geometry.js +1 -1
- package/dist/debug/use_smile_id_debug.d.ts +25 -2
- package/dist/debug/use_smile_id_debug.js +1 -1
- package/dist/debug/use_smile_id_document_preview_debug_overlay.js +1 -1
- package/dist/debug/use_smile_id_selfie_preview_debug_overlay.d.ts +5 -1
- package/dist/debug/use_smile_id_selfie_preview_debug_overlay.js +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -1
- package/dist/l10n/strings_en.d.ts +3 -3
- package/dist/l10n/strings_en.js +2 -2
- package/dist/l10n/use_smile_id_localizations.d.ts +5 -7
- package/dist/l10n/use_smile_id_localizations.js +1 -1
- package/dist/metadata/metadata_configuration_mappers.d.ts +3 -5
- package/dist/metadata/metadata_configuration_mappers.js +1 -1
- package/dist/metadata/use_smile_id_metadata_factory.js +1 -1
- package/dist/presentation/assets/si_dark_lighting_animation.json +1 -927
- package/dist/presentation/assets/si_device_orientation_animation.json +1 -850
- package/dist/presentation/assets/si_document_green_book_hero_animation.json +1 -4740
- package/dist/presentation/assets/si_document_hero_animation.json +1 -4870
- package/dist/presentation/assets/si_document_hero_back_animation.json +1 -2122
- package/dist/presentation/assets/si_document_passport_hero_animation.json +1 -14540
- package/dist/presentation/assets/si_ess_all_directions_hero_animation.json +1 -5906
- package/dist/presentation/assets/si_ess_animation_all_directions.json +1 -5906
- package/dist/presentation/assets/si_selfie_hero_animation.json +1 -1468
- package/dist/presentation/assets/si_smile_to_capture_animation.json +1 -1412
- package/dist/presentation/components/helpers/captured_image_uri.d.ts +11 -0
- package/dist/presentation/components/helpers/captured_image_uri.js +1 -0
- package/dist/presentation/components/helpers/captured_oval_region.d.ts +40 -0
- package/dist/presentation/components/helpers/captured_oval_region.js +1 -0
- package/dist/presentation/components/helpers/document_interior_crop.d.ts +13 -2
- package/dist/presentation/components/helpers/document_interior_crop.js +1 -1
- package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts +35 -29
- package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js +1 -1
- package/dist/presentation/components/helpers/selfie_interior_crop.js +1 -1
- package/dist/presentation/components/use_smile_id_capture_content.d.ts +5 -2
- package/dist/presentation/components/use_smile_id_capture_content.js +1 -1
- package/dist/presentation/components/use_smile_id_captured_document_image.d.ts +8 -4
- package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -1
- package/dist/presentation/components/use_smile_id_captured_image_oval.d.ts +6 -0
- package/dist/presentation/components/use_smile_id_captured_image_oval.js +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +17 -7
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_direction_animation.d.ts +13 -14
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_direction_animation.js +1 -1
- package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_full_animation.js +1 -1
- package/dist/presentation/components/use_smile_id_face_oval_image.js +1 -1
- package/dist/presentation/flow/analytics/unsatisfied_reasons.d.ts +14 -0
- package/dist/presentation/flow/analytics/unsatisfied_reasons.js +1 -0
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event.d.ts +8 -7
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event.js +1 -1
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.d.ts +9 -0
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.js +1 -1
- package/dist/presentation/flow/config/flow_configuration_camera.d.ts +9 -0
- package/dist/presentation/flow/config/flow_configuration_camera.js +1 -0
- package/dist/presentation/flow/dsl/builder/config_builder.d.ts +6 -0
- package/dist/presentation/flow/dsl/builder/config_builder.js +1 -1
- package/dist/presentation/flow/dsl/builder/ml_builder.d.ts +1 -7
- package/dist/presentation/flow/dsl/builder/ml_builder.js +1 -1
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +0 -9
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +1 -1
- package/dist/presentation/flow/flow_navigation_manager.d.ts +39 -70
- package/dist/presentation/flow/flow_navigation_manager.js +1 -1
- package/dist/presentation/flow/flow_navigation_manager_analytics.d.ts +23 -0
- package/dist/presentation/flow/flow_navigation_manager_analytics.js +1 -0
- package/dist/presentation/flow/flow_navigation_manager_documents.d.ts +18 -0
- package/dist/presentation/flow/flow_navigation_manager_documents.js +1 -0
- package/dist/presentation/flow/flow_navigation_manager_job_request.d.ts +7 -0
- package/dist/presentation/flow/flow_navigation_manager_job_request.js +1 -0
- package/dist/presentation/flow/flow_navigation_manager_routing.d.ts +12 -0
- package/dist/presentation/flow/flow_navigation_manager_routing.js +1 -0
- package/dist/presentation/flow/flow_navigation_manager_submission.d.ts +17 -0
- package/dist/presentation/flow/flow_navigation_manager_submission.js +1 -0
- package/dist/presentation/flow/job_submission_state.d.ts +4 -0
- package/dist/presentation/flow/screen/helpers/face_session_config.d.ts +1 -1
- package/dist/presentation/flow/screen/helpers/failure_message.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_capture_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_preview_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_processing_screen.js +1 -1
- package/dist/presentation/flow/use_smile_id_flow.js +1 -1
- package/dist/presentation/flow/validation/flow_validator.d.ts +0 -19
- package/dist/presentation/flow/validation/flow_validator.js +1 -1
- package/dist/presentation/flow/validation/job_type_validator.js +1 -1
- package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts +0 -12
- package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -1
- package/dist/presentation/screen/helpers/capture_ui_state.d.ts +33 -6
- package/dist/presentation/screen/helpers/capture_ui_state.js +1 -1
- package/dist/presentation/screen/helpers/dropped_frames.d.ts +6 -0
- package/dist/presentation/screen/helpers/dropped_frames.js +1 -0
- package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -0
- package/dist/presentation/screen/helpers/ess_turn_haptics.js +1 -1
- package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts +5 -1
- package/dist/presentation/screen/helpers/instructions_screen_helpers.js +1 -1
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +23 -1
- package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +1 -1
- package/dist/presentation/screen/helpers/use_debounced_feedback_key.d.ts +1 -1
- package/dist/presentation/screen/helpers/use_debounced_feedback_key.js +1 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_consent_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +3 -0
- package/dist/presentation/screen/use_smile_id_preview_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +3 -0
- package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
- package/dist/presentation/utils/locale_utils.d.ts +3 -6
- package/dist/presentation/utils/locale_utils.js +1 -1
- package/dist/testing/scenario_expectations.d.ts +7 -2
- package/dist/testing/scenario_expectations.js +1 -1
- package/dist/testing/scripted_scan_results.d.ts +2 -1
- package/dist/testing/scripted_scan_results.js +1 -1
- package/package.json +7 -7
- package/dist/presentation/flow/validation/user_details_validator.d.ts +0 -8
- package/dist/presentation/flow/validation/user_details_validator.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
Full release notes: [Smile ID docs](https://docs.usesmileid.com/developer-resources/sdks/mobile/release-notes/react-native-expo).
|
|
2
2
|
|
|
3
|
+
## 12.3.0
|
|
4
|
+
|
|
5
|
+
See the repository CHANGELOG for the 12.3.0 release notes.
|
|
6
|
+
|
|
7
|
+
|
|
3
8
|
## 12.2.0
|
|
4
9
|
|
|
5
10
|
See the repository CHANGELOG for the 12.2.0 release notes.
|
|
6
11
|
|
|
7
|
-
|
|
8
12
|
## 12.1.1
|
|
9
13
|
|
|
10
14
|
See the repository CHANGELOG for the 12.1.1 release notes.
|
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Sizes are measured on every push to `main` and updated automatically by CI.
|
|
|
32
32
|
- Xcode (latest stable) — iOS 16.4 deployment target (set by Expo SDK 56's modules; the Smile ID iOS SDK itself supports iOS 15)
|
|
33
33
|
- Android Gradle Plugin 8.11.1+ — `compileSdk` 36 or newer (React Native's default; nothing to set) and `minSdk` 24, matching the native Smile ID SDK and the kamera engine. The ML Kit config plugins set the Kotlin and KSP versions the AARs were compiled with. The 12.0.0–12.0.2 AARs required compileSdk 37 and `minSdk` 26 instead; on those releases, install platform 37 before Gradle runs (on EAS, an `eas-build-pre-install` script: `yes | sdkmanager --licenses >/dev/null; sdkmanager "platforms;android-37.0"`).
|
|
34
34
|
|
|
35
|
-
`12.
|
|
35
|
+
`12.3.0` is built and verified against Expo SDK 57 and React Native 0.86. The floors above are the supported minimum and keep working.
|
|
36
36
|
|
|
37
37
|
## Installation
|
|
38
38
|
|
|
@@ -53,7 +53,7 @@ npx expo install expo-brightness expo-file-system expo-font expo-haptics \
|
|
|
53
53
|
expo-image-manipulator expo-image-picker expo-keep-awake expo-screen-orientation
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
(
|
|
56
|
+
(For Android phones without Google Play services, use `@smileid/usesmileid_huawei_{face,document}` instead of the ML Kit packages in the build you ship to those phones, such as your AppGallery build. Your Google Play build keeps ML Kit.)
|
|
57
57
|
|
|
58
58
|
Everything above except the `@smileid/*` packages is a **required peer dependency**, and must be installed in your app directly: React Native autolinking only links native modules declared in the app's own `package.json`, not a library's transitive ones. `expo`, `expo-modules-core`, `react` and `react-native` are omitted because they ship with your app.
|
|
59
59
|
|
|
@@ -124,11 +124,17 @@ export default function VerificationScreen() {
|
|
|
124
124
|
b.onResult = (result) => {
|
|
125
125
|
switch (result.status) {
|
|
126
126
|
case "success":
|
|
127
|
-
console.log("
|
|
127
|
+
console.log("Submitted job", result.value.jobId);
|
|
128
128
|
break;
|
|
129
129
|
case "failure":
|
|
130
130
|
console.error("Error:", result.error);
|
|
131
131
|
break;
|
|
132
|
+
case "cancelled":
|
|
133
|
+
console.log("The user left the flow");
|
|
134
|
+
break;
|
|
135
|
+
case "captured":
|
|
136
|
+
console.log("Captured", result.job.jobType);
|
|
137
|
+
break;
|
|
132
138
|
}
|
|
133
139
|
};
|
|
134
140
|
}}
|
|
@@ -153,6 +159,7 @@ The server issues `userId` and `jobId` and returns them in the submission respon
|
|
|
153
159
|
b.config((c) => {
|
|
154
160
|
c.enableDebugMode = false;
|
|
155
161
|
c.allowOfflineMode = false;
|
|
162
|
+
c.skipApiSubmission = false;
|
|
156
163
|
c.enableCrashReporting = true;
|
|
157
164
|
c.showAttribution = true;
|
|
158
165
|
});
|
|
@@ -162,6 +169,7 @@ b.config((c) => {
|
|
|
162
169
|
| ---------------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
163
170
|
| `enableDebugMode` | `boolean` | `false` | Shows a validation error screen on build failure |
|
|
164
171
|
| `allowOfflineMode` | `boolean` | `false` | Allows the flow to run without a network connection |
|
|
172
|
+
| `skipApiSubmission` | `boolean` | `false` | Captures the job and hands it back as `{ status: "captured", job }` instead of submitting it. The files stay on disk for you to forward and delete |
|
|
165
173
|
| `enableCrashReporting` | `boolean` | `true` | Enables Sentry crash reporting for the SDK. Set to `false` if your app runs its own Sentry |
|
|
166
174
|
| `showAttribution` | `boolean` | `true` | Shows the "Powered by Smile ID" mark on every screen that carries it. Set to `false` to hide it across the whole journey. Document capture never shows the mark |
|
|
167
175
|
|
|
@@ -268,7 +276,7 @@ b.network((n) => {
|
|
|
268
276
|
i.add(i.gzip());
|
|
269
277
|
i.add(
|
|
270
278
|
i.logging((l) => {
|
|
271
|
-
l.level = LogLevel.body;
|
|
279
|
+
l.level = LogLevel.body; // bodies in a development build only
|
|
272
280
|
}),
|
|
273
281
|
);
|
|
274
282
|
i.add(myCustomInterceptor); // partner-supplied
|
|
@@ -444,13 +452,13 @@ function ExitButton() {
|
|
|
444
452
|
|
|
445
453
|
The hook returns the active `FlowNavigationManager`:
|
|
446
454
|
|
|
447
|
-
| Member | Behaviour
|
|
448
|
-
| ------------------- |
|
|
449
|
-
| `navigateToNext()` | Advance to the next screen. From the last step, calls `onResult({ status: 'success', ... })`.
|
|
450
|
-
| `navigateBack()` | Pop the active screen. From the first step, calls `onResult({ status: '
|
|
451
|
-
| `navigateToStart()` | Reset the path to the first screen — a flow restart after a terminal failure.
|
|
452
|
-
| `exit(reason?)` | Exit immediately — useful for "abort" buttons. Defaults to `cancelled`.
|
|
453
|
-
| `captured` | Snapshot of artifacts collected so far (`selfiePath`, `livenessPaths`, `frontDocumentPath`, etc).
|
|
455
|
+
| Member | Behaviour |
|
|
456
|
+
| ------------------- | ------------------------------------------------------------------------------------------------- |
|
|
457
|
+
| `navigateToNext()` | Advance to the next screen. From the last step, calls `onResult({ status: 'success', ... })`. |
|
|
458
|
+
| `navigateBack()` | Pop the active screen. From the first step, calls `onResult({ status: 'cancelled' })`. |
|
|
459
|
+
| `navigateToStart()` | Reset the path to the first screen — a flow restart after a terminal failure. |
|
|
460
|
+
| `exit(reason?)` | Exit immediately — useful for "abort" buttons. Defaults to `cancelled`. |
|
|
461
|
+
| `captured` | Snapshot of artifacts collected so far (`selfiePath`, `livenessPaths`, `frontDocumentPath`, etc). |
|
|
454
462
|
|
|
455
463
|
Calling `useFlowNavigation()` outside `<UseSmileIDBuilder>` throws — the misuse is loud at first render rather than silently no-op-ing.
|
|
456
464
|
|
|
@@ -458,16 +466,16 @@ Calling `useFlowNavigation()` outside `<UseSmileIDBuilder>` throws — the misus
|
|
|
458
466
|
|
|
459
467
|
## Top-level builder callbacks
|
|
460
468
|
|
|
461
|
-
| Property | Type | Description
|
|
462
|
-
| ------------------ | ----------------------------------------------------------- |
|
|
463
|
-
| `onResult` | `(result: UseSmileIDResult<JobSubmissionResponse>) => void` | Called once when the flow finishes (success or
|
|
464
|
-
| `onAnalyticsEvent` | `((event: UseSmileIDAnalyticsEvent) => void) \| undefined` | Optional. Called for each analytics event during the flow
|
|
469
|
+
| Property | Type | Description |
|
|
470
|
+
| ------------------ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
471
|
+
| `onResult` | `(result: UseSmileIDResult<JobSubmissionResponse>) => void` | Called once when the flow finishes (success, failure, cancelled, or captured when `skipApiSubmission` is on) |
|
|
472
|
+
| `onAnalyticsEvent` | `((event: UseSmileIDAnalyticsEvent) => void) \| undefined` | Optional. Called for each analytics event during the flow |
|
|
465
473
|
|
|
466
474
|
---
|
|
467
475
|
|
|
468
476
|
## Job-specific parameters
|
|
469
477
|
|
|
470
|
-
Each job type that needs identity fields reads them from a top-level builder property. The shared `user_details` part (`b.userDetails`) is
|
|
478
|
+
Each job type that needs identity fields reads them from a top-level builder property. The shared `user_details` part (`b.userDetails`) is optional: the SDK checks none of its fields, and the API reports any a job needs. `country` is the ISO 3166-1 alpha-2 code.
|
|
471
479
|
|
|
472
480
|
| Job type | Builder property | Required fields | Notes |
|
|
473
481
|
| --------------------------------------- | --------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -505,7 +513,7 @@ b.userDetails = {
|
|
|
505
513
|
|
|
506
514
|
`DocumentSide` (exported from `@smileid/usesmileid`) gains `Visa` (`"visa"`) for the residency visa page, so an exhaustive `switch` over it needs the new case.
|
|
507
515
|
|
|
508
|
-
|
|
516
|
+
Pre-flight any payload with `b.validateDocumentVerificationParams(...)` or `b.validateEnhancedDocumentVerificationParams(...)` before assigning.
|
|
509
517
|
|
|
510
518
|
---
|
|
511
519
|
|
|
@@ -522,13 +530,44 @@ b.onResult = (result) => {
|
|
|
522
530
|
case "failure":
|
|
523
531
|
console.error("flow failed:", result.error);
|
|
524
532
|
break;
|
|
533
|
+
case "cancelled":
|
|
534
|
+
console.log("user exited before finishing");
|
|
535
|
+
break;
|
|
536
|
+
case "captured":
|
|
537
|
+
console.log(
|
|
538
|
+
`captured ${result.job.jobType}; forward ${result.job.sessionDirectory}`,
|
|
539
|
+
);
|
|
540
|
+
break;
|
|
525
541
|
}
|
|
526
542
|
};
|
|
527
543
|
```
|
|
528
544
|
|
|
529
545
|
The success branch carries a `JobSubmissionResponse` with `jobId`, `userId`, `status`, `message`, and an optional `createdAt`. Partner-supplied inputs (captured frames, identity fields, consent) are not echoed back — keep references at the call site if you need them after the flow.
|
|
530
546
|
|
|
531
|
-
`UseSmileIDResult<T>` is a discriminated union — `result.status` narrows it, no `try` / `catch` needed.
|
|
547
|
+
`UseSmileIDResult<T>` is a discriminated union — `result.status` narrows it, no `try` / `catch` needed. The statuses are `"success"`, `"failure"`, `"cancelled"` (the user left before finishing — nothing was submitted) and `"captured"`, delivered only when `c.skipApiSubmission = true`.
|
|
548
|
+
|
|
549
|
+
A `"captured"` result carries a `CapturedJob`:
|
|
550
|
+
|
|
551
|
+
| Field | Type | Description |
|
|
552
|
+
| ------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
553
|
+
| `jobType` | `JobType` | The configured job type |
|
|
554
|
+
| `sessionId` | `string` | The flow's session id, also on every analytics event |
|
|
555
|
+
| `sessionDirectory` | `string \| undefined` | The session folder the SDK kept for this job. In this mode the SDK deletes neither it nor the files, so once you have forwarded the job, delete every file listed here, then this folder and everything left in it. Absent when the files don't share one of the SDK's session folders (a document job's selfie and document come from separate capture sessions), or the job captured no files (Enhanced KYC) |
|
|
556
|
+
| `selfieImage` | `string \| undefined` | Selfie file URI for selfie-bearing jobs |
|
|
557
|
+
| `livenessImages` | `readonly string[]` | Liveness frame URIs for selfie-bearing jobs |
|
|
558
|
+
| `frontDocument`, `backDocument` | `string \| undefined` | Document side URIs for document jobs |
|
|
559
|
+
| `visaDocument` | `string \| undefined` | The visa page URI, for Residency Document Verification |
|
|
560
|
+
| `headers` | `Readonly<Record<string, string>>` | `smileid-token`, `smileid-source-sdk` and `smileid-source-sdk-version`, to send as request headers |
|
|
561
|
+
| `encryptedPackage` | `string` | The rest of the request, sealed for Smile ID; send it as the `encrypted_package` form field |
|
|
562
|
+
| `packageNonce`, `packageTag` | `string` | The package's nonce and tag; send them as the `package_nonce` and `package_tag` form fields |
|
|
563
|
+
|
|
564
|
+
To forward the job, send the job type's `/v3` request with `headers`, the images as their usual form parts, and the three package fields. The package holds everything else the SDK would have sent (the consent record, the metadata and the request signature), encrypted with AES-256-GCM for Smile ID. Forward it as is: the server rejects a package that was changed. Forward the image files byte-for-byte too: they carry an integrity seal that re-encoding invalidates. A document imported from the gallery or taken with the manual shutter is kept in the app's cache, which the OS may clear under storage pressure, so forward such a job promptly.
|
|
565
|
+
|
|
566
|
+
Send each file under the form part the SDK would have used: `selfieImage` as `selfie_image`, `livenessImages` as `liveness_images` in the list's order, `frontDocument` as `document`, `backDocument` as `document_back` and `visaDocument` as `visa`. The request signature in the package covers the images by part name and order, so the server rejects a job sent any other way. Forward the job before its access token expires: `headers` carries the token from the capture, and the server rejects the job once that token has expired.
|
|
567
|
+
|
|
568
|
+
On iOS the system can move the app's data container between launches, for example across an app update, so don't save these absolute URIs. To forward a job in a later launch, save each path relative to the container and rebuild it then.
|
|
569
|
+
|
|
570
|
+
The processing screen keeps its spinner after the hand-over, because nothing was submitted, so unmount the flow in `onResult`, as for every other result.
|
|
532
571
|
|
|
533
572
|
### Forwarding failures to a crash reporter
|
|
534
573
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { KameraRegion } from "@smileid/kamera";
|
|
2
|
+
import type { BoundingBox } from "@smileid/usesmileid_bridge";
|
|
3
|
+
/**
|
|
4
|
+
* `box` as the region kamera meters exposure on, in the frame as displayed: flipped first when
|
|
5
|
+
* `flipped`, because the detector saw the unmirrored frame, then clamped to the frame. Null when
|
|
6
|
+
* none of it is inside.
|
|
7
|
+
*/
|
|
8
|
+
export declare function meteringRegion(box: Pick<BoundingBox, "left" | "top" | "width" | "height">, flipped: boolean): KameraRegion | null;
|
|
9
|
+
/** The largest of `faces` by area, or null when there are none. */
|
|
10
|
+
export declare function largestFace<T extends Pick<BoundingBox, "width" | "height">>(faces: readonly T[]): T | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";export function meteringRegion(t,n){const e=n?1-t.left-t.width:t.left,h=i(e),r=i(t.top),c=i(e+t.width),l=i(t.top+t.height);return c<=h||l<=r?null:{x:h,y:r,width:c-h,height:l-r}}export function largestFace(t){let n=null;for(const e of t)(n===null||e.width*e.height>n.width*n.height)&&(n=e);return n}function i(t){return Math.min(1,Math.max(0,t))}
|
|
@@ -16,8 +16,6 @@ export declare const CameraDefaults: {
|
|
|
16
16
|
readonly width: 1280;
|
|
17
17
|
readonly height: 720;
|
|
18
18
|
};
|
|
19
|
-
/** How long a streaming capture waits for its first analyzed frame before reporting the camera unavailable. */
|
|
20
|
-
readonly firstFrameTimeoutMs: 10000;
|
|
21
19
|
/** Longest-edge ceiling (px) for an imported document image; downscale-only. */
|
|
22
20
|
readonly documentMaxDimension: 1600;
|
|
23
21
|
/** JPEG quality (0–1) for imported document images. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export const CameraDefaults={focusRunningDurationMs:600,focusSettledDurationMs:500,analysisTargetFps:30,analysisTargetResolution:{width:640,height:480},documentAnalysisResolution:{width:1280,height:720},
|
|
1
|
+
"use strict";export const CameraDefaults={focusRunningDurationMs:600,focusSettledDurationMs:500,analysisTargetFps:30,analysisTargetResolution:{width:640,height:480},documentAnalysisResolution:{width:1280,height:720},documentMaxDimension:1600,documentJpegQuality:.85,minGalleryDocumentDimensionPx:600,selfieJpegTargetWidth:480,selfieJpegTargetHeight:640};
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { DocumentSide } from "@smileid/usesmileid_bridge";
|
|
2
2
|
export { computeDocumentResizeTarget } from "./document_resize_target";
|
|
3
|
+
/**
|
|
4
|
+
* Imported document image pipeline — brings a manual-shutter still or gallery
|
|
5
|
+
* image onto the SAME byte policy as the native auto-capture artifact: longest
|
|
6
|
+
* edge downscaled to {@link CameraDefaults.documentMaxDimension} at
|
|
7
|
+
* {@link CameraDefaults.documentJpegQuality}, written under the canonical
|
|
8
|
+
* per-side filename. The Smile EXIF `UserComment` tag is stamped downstream by
|
|
9
|
+
* `SmileExifTagger.finalize`, so every upload is byte-policy-identical
|
|
10
|
+
* regardless of capture path.
|
|
11
|
+
*/
|
|
3
12
|
/** Canonical per-image artifact basename, matching the auto-capture naming. */
|
|
4
13
|
export declare function documentArtifactFileName(side: DocumentSide): string;
|
|
5
14
|
/** Source image for an imported document capture (manual shutter or gallery). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{
|
|
1
|
+
"use strict";import{File as i}from"expo-file-system";import{ImageManipulator as m,SaveFormat as s}from"expo-image-manipulator";import{DocumentSide as o,UseSmileIDCrashReporting as u}from"@smileid/usesmileid_bridge";import{CameraDefaults as a}from"./camera_defaults";import{computeDocumentResizeTarget as d}from"./document_resize_target";import{newImportedDocumentFolder as p}from"./imported_document_folder";export{computeDocumentResizeTarget}from"./document_resize_target";export function documentArtifactFileName(t){switch(t){case o.Back:return"document_back.jpg";case o.Visa:return"document_visa.jpg";default:return"document_front.jpg"}}export async function resampleImportedDocument(t,n){const r=t.width!==void 0&&t.height!==void 0?d(t.width,t.height,a.documentMaxDimension):null,e=m.manipulate(t.uri);r&&e.resize(r);const c=await(await e.renderAsync()).saveAsync({compress:a.documentJpegQuality,format:s.JPEG});return f(c.uri,n)}function f(t,n){const r=p();try{r.exists||r.create({intermediates:!0});const e=new i(r,documentArtifactFileName(n));return e.exists&&e.delete(),new i(t).move(e),e.uri}catch(e){u.captureException(e instanceof Error?e:new Error(String(e)));try{r.exists&&r.delete()}catch{}return t}}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Directory } from "expo-file-system";
|
|
2
|
+
/** Subdirectory under the cache directory holding imported document artifacts. */
|
|
3
|
+
export declare const IMPORT_SUBDIRECTORY = "usesmileid_documents";
|
|
4
|
+
/**
|
|
5
|
+
* A new `<cache>/usesmileid_documents/<id>/` folder for one import. Each import
|
|
6
|
+
* gets its own: a skip-submission hand-over gives the partner the path to
|
|
7
|
+
* forward later, so a later flow must never overwrite or delete it.
|
|
8
|
+
*/
|
|
9
|
+
export declare function newImportedDocumentFolder(): Directory;
|
|
10
|
+
/**
|
|
11
|
+
* Delete an imported document together with the folder it was written into. A
|
|
12
|
+
* no-op for any other file, such as a camera capture, so a caller can pass
|
|
13
|
+
* every document path it drops.
|
|
14
|
+
*/
|
|
15
|
+
export declare function deleteImportedDocument(uri: string): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{Directory as r,File as c,Paths as u}from"expo-file-system";import{generateSessionId as s}from"../metadata/use_smile_id_metadata_factory";export const IMPORT_SUBDIRECTORY="usesmileid_documents";export function newImportedDocumentFolder(){return new r(o(),s())}function o(){return new r(u.cache,IMPORT_SUBDIRECTORY)}function n(e){return e.replace(/\/+$/,"")}export function deleteImportedDocument(e){try{const t=new c(e).parentDirectory,i=n(o().uri);if(n(t.parentDirectory.uri)!==i)return;t.exists&&t.delete()}catch{}}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import type { KameraConfig, Lens } from "@smileid/kamera";
|
|
2
2
|
import { CaptureType } from "../core/models/ml_config";
|
|
3
3
|
import { CameraLens } from "./camera_lens";
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* The selfie's analysis interval: 30 frames a second, which its 0.2 s
|
|
6
|
+
* reference window and the settling counted inside each 350 ms interval assume.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SELFIE_ANALYSIS_INTERVAL_MS = 33;
|
|
9
|
+
/**
|
|
10
|
+
* Maps the SDK's capture inputs onto a kamera `KameraConfig` via the tuned kamera presets. A selfie
|
|
11
|
+
* analyses every {@link SELFIE_ANALYSIS_INTERVAL_MS} and asks for each frame's device motion (the
|
|
12
|
+
* shake gate), as the native screens do; on iOS it also asks for kamera's face tracking (ARKit on a
|
|
13
|
+
* TrueDepth front camera, whose tracked face the selfie reads).
|
|
14
|
+
*/
|
|
5
15
|
export declare const KameraConfigFactory: {
|
|
6
16
|
readonly create: (captureType: CaptureType, lens: CameraLens) => KameraConfig;
|
|
7
17
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{KameraDefaults as
|
|
1
|
+
"use strict";import{Platform as n}from"react-native";import{KameraDefaults as o}from"@smileid/kamera";import{CaptureType as m}from"../core/models/ml_config";import{CameraLens as i}from"./camera_lens";export const SELFIE_ANALYSIS_INTERVAL_MS=33,KameraConfigFactory={create(e,a){const r=toKameraLens(a);if(e===m.document)return o.document(r);const t=o.selfie(r);return{...t,analysis:{...t.analysis,minIntervalMs:SELFIE_ANALYSIS_INTERVAL_MS,deviceMotion:!0,...n.OS==="ios"?{faceTracking:!0}:{}}}}};export function toKameraLens(e){return e===i.back?"back":"front"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{useCallback as y,useEffect as l,useMemo as E,useRef as a}from"react";import{documentScanResultSchema as S,faceScanResultSchema as h,FrameProcessingException as B}from"@smileid/usesmileid_bridge";import{
|
|
1
|
+
"use strict";import{useCallback as y,useEffect as l,useMemo as E,useRef as a}from"react";import{documentScanResultSchema as S,faceScanResultSchema as h,FrameProcessingException as B}from"@smileid/usesmileid_bridge";import{isDevelopmentBuild as p}from"../core/is_development_build";import{UseSmileIDDebugTap as R}from"../debug/use_smile_id_debug_tap";export function useSmileIDFrameStream(e,r,o){const i=a(e),s=a(r),u=a(o?.onError),c=a(!1),f=a(!0);l(()=>{i.current=e},[e]),l(()=>{s.current=r},[r]),l(()=>{u.current=o?.onError},[o?.onError]),l(()=>(f.current=!0,()=>{f.current=!1}),[]);const n=y(t=>{const m=u.current;dispatchFrameResult({raw:t,analyzer:i.current,isBusy:()=>c.current,setBusy:d=>{c.current=d},isMounted:()=>f.current,onResult:s.current,...m!==void 0?{onError:m}:{}})},[]);return E(()=>({onFrameResult:n}),[n])}function D(e){if(e===null||typeof e!="object")return null;const r=e.error;return typeof r=="string"?r:null}const _=new Set(["no_frame","session_not_configured"]);export function dispatchFrameResult(e){const{raw:r,analyzer:o,isBusy:i,setBusy:s,isMounted:u,onResult:c,onError:f}=e;if(o&&!i()){s(!0);try{const n=D(r);if(n!==null){if(_.has(n))return;throw new B({reason:n})}if(o.kind==="face"){const t=h.parse(r);if(!u())return;c(t),p()&&R.analyzedFace(t.output)}else{const t=S.parse(r);if(!u())return;c(t),p()&&R.analyzedDocument(t.output)}}catch(n){if(!u())return;f?.(n)}finally{s(!1)}}}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import type { KameraCapabilities, KameraCaptureResult, KameraEvent } from "@smileid/kamera";
|
|
2
2
|
import { KameraSession } from "@smileid/kamera";
|
|
3
|
+
import { type BoundingBox, type DocumentGuide } from "@smileid/usesmileid_bridge";
|
|
3
4
|
import { CameraLens } from "./camera_lens";
|
|
4
5
|
import type { CameraConfiguration, CameraSystemState } from "./camera_configuration";
|
|
5
6
|
import type { FocusPoint, FocusState } from "./focus_state";
|
|
6
7
|
type Listener = (state: CameraSystemState, focus: FocusState) => void;
|
|
7
|
-
/**
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* The kamera session surface this system drives; structural so tests can inject an in-memory fake.
|
|
10
|
+
* `setMeteringRegion` is optional so a session built against the earlier surface still fits; without
|
|
11
|
+
* it the selfie meters the whole frame.
|
|
12
|
+
*/
|
|
13
|
+
export type KameraSessionLike = Pick<KameraSession, "start" | "stop" | "capture" | "setZoomRatio" | "setTorch" | "tapToFocus" | "setExposureCompensation" | "capabilities" | "release" | "addEventListener"> & Partial<Pick<KameraSession, "setMeteringRegion" | "setDocumentGuide" | "setMetricsEnabled">>;
|
|
14
|
+
/**
|
|
15
|
+
* Turns kamera's perf metrics on or off from the next camera start; they arrive as `metric`
|
|
16
|
+
* events. Internal: the capture screen calls it for the perf trace.
|
|
17
|
+
*/
|
|
18
|
+
export declare function setKameraMetricsWanted(enabled: boolean): void;
|
|
19
|
+
/** Test seam: forgets what was sent to kamera. */
|
|
20
|
+
export declare function resetKameraMetricsForTesting(): void;
|
|
9
21
|
/**
|
|
10
22
|
* Kamera-backed orchestrator: drives the native session and republishes its state via `subscribe()`.
|
|
11
23
|
* Pixels never cross the bridge — frame analysis and still capture run in the native cores.
|
|
@@ -51,6 +63,33 @@ export declare class UseSmileIDKameraCameraSystem {
|
|
|
51
63
|
capture(): Promise<KameraCaptureResult>;
|
|
52
64
|
/** Sets zoom, clamped to the session's capability range. */
|
|
53
65
|
setZoomRatio(ratio: number): Promise<void>;
|
|
66
|
+
/** The guide last asked for: what the camera should hold. */
|
|
67
|
+
private _documentGuide;
|
|
68
|
+
/** Whether the current session confirmed `_documentGuide`. */
|
|
69
|
+
private _documentGuideApplied;
|
|
70
|
+
/** The session a guide is being sent to, so only one send is in flight. */
|
|
71
|
+
private _documentGuideSendingTo;
|
|
72
|
+
private _documentGuideFailureReported;
|
|
73
|
+
/**
|
|
74
|
+
* Sets what the document guide frames, so the camera zooms only as far as
|
|
75
|
+
* the document filling the guide needs to stay in focus. Null clears it and
|
|
76
|
+
* leaves the camera's default close-focus floor.
|
|
77
|
+
*
|
|
78
|
+
* Called on every frame: a repeat sends nothing, and only one guide is in
|
|
79
|
+
* flight at a time, the latest one asked for following it. A guide set
|
|
80
|
+
* before the camera starts reaches it once it does. A guide the camera
|
|
81
|
+
* refuses is sent again on the next call; the first refusal is reported to
|
|
82
|
+
* crash reporting. Never rejects.
|
|
83
|
+
*/
|
|
84
|
+
setDocumentGuide(guide: DocumentGuide | null): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Sends the guide asked for to the current session until it holds the
|
|
87
|
+
* latest one, one send at a time; stops at a refusal, which the next call
|
|
88
|
+
* retries, or when the session changes, whose start sends the guide itself.
|
|
89
|
+
*/
|
|
90
|
+
private _applyDocumentGuide;
|
|
91
|
+
/** Sends `guide` to `session`; false when the session refuses it. */
|
|
92
|
+
private _sendDocumentGuide;
|
|
54
93
|
/** Toggles the torch; a no-op without a flash unit. */
|
|
55
94
|
setTorch(enabled: boolean): Promise<void>;
|
|
56
95
|
/**
|
|
@@ -58,6 +97,11 @@ export declare class UseSmileIDKameraCameraSystem {
|
|
|
58
97
|
* Drives the reticle running → success → unspecified on the standard cadence.
|
|
59
98
|
*/
|
|
60
99
|
tapToFocus(point: FocusPoint): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Meters exposure on `face`, a box from the face analyzer, or on the whole frame when null.
|
|
102
|
+
* kamera decides when the camera actually re-meters, so it is called on every analysed frame.
|
|
103
|
+
*/
|
|
104
|
+
meterExposure(face: BoundingBox | null): void;
|
|
61
105
|
/** Releases the native session. The system is unusable afterwards. */
|
|
62
106
|
release(): Promise<void>;
|
|
63
107
|
private _onKameraEvent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{AspectRatio as
|
|
1
|
+
"use strict";import{AspectRatio as f,KameraSession as p}from"@smileid/kamera";import{UseSmileIDCrashReporting as l}from"@smileid/usesmileid_bridge";import{Platform as S}from"react-native";import{CaptureType as c}from"../core/models/ml_config";import{captureDebugBoxesMirrored as g}from"../debug/capture_debug_geometry";import{meteringRegion as w}from"./bounding_box_metering_region";import{CameraDefaults as h}from"./camera_defaults";import{CameraLens as a}from"./camera_lens";import{FocusStates as n}from"./focus_state";import{KameraConfigFactory as _}from"./kamera_config_factory";let m=!1,o=null;export function setKameraMetricsWanted(i){m=i}async function y(i){const e=m;if(e===o)return;o=e;const t=Promise.resolve().then(()=>i.setMetricsEnabled?.(e)).catch(s=>{o=null,l.captureException(s instanceof Error?s:new Error(String(s)))});e&&await t}export function resetKameraMetricsForTesting(){m=!1,o=null}export class UseSmileIDKameraCameraSystem{_createSession;_session=null;_removeEventListener=null;_state;_focusState=n.unspecified();_listeners=new Set;_eventListeners=new Set;_capabilities=null;_config={};_released=!1;_focusRunningTimeout=null;_focusSettledTimeout=null;constructor(e=a.front,t=()=>new p){this._createSession=t,this._state={isCameraRunning:!1,isStreaming:!1,currentLens:e,hasPermission:!1}}get state(){return this._state}get focusState(){return this._focusState}get capabilities(){return this._capabilities}get uprightPreviewAspect(){const e=this._config.captureType??c.selfie;return _.create(e,this._state.currentLens).preview.aspectRatio===f.Ratio16x9?9/16:3/4}get session(){return this._session}subscribe(e){return this._listeners.add(e),()=>this._listeners.delete(e)}addEventListener(e){return this._eventListeners.add(e),()=>this._eventListeners.delete(e)}configure(e){this._config=e,e.lens&&e.lens!==this._state.currentLens&&this._setState({...this._state,currentLens:e.lens})}async start(){if(this._released)throw new Error("UseSmileIDKameraCameraSystem.start() after release() \u2014 create a new system.");await this._teardownSession();const e=this._createSession(),t=e.addEventListener(u=>this._onKameraEvent(u)),s=this._config.captureType??c.selfie,r=this._config.lens??(s===c.document?a.back:a.front);try{await y(e),await e.start(_.create(s,r)),this._capabilities=await e.capabilities()}catch(u){throw t(),await e.release(),u}this._removeEventListener=t,this._session=e,this._documentGuideApplied=this._documentGuide===null,await this._applyDocumentGuide(),this._setState({...this._state,isCameraRunning:!0,isStreaming:!0,currentLens:r,hasPermission:!0})}async stop(){await this._session?.stop(),this._setState({...this._state,isCameraRunning:!1,isStreaming:!1})}async capture(){const e=this._session;if(!e)throw new Error("Cannot capture: camera is not initialised.");return e.capture()}async setZoomRatio(e){const t=this._session;if(!t)return;const s=this._capabilities,r=s?Math.min(s.maxZoomRatio,Math.max(s.minZoomRatio,e)):e;await t.setZoomRatio(r)}_documentGuide=null;_documentGuideApplied=!0;_documentGuideSendingTo=null;_documentGuideFailureReported=!1;async setDocumentGuide(e){d(e,this._documentGuide)||(this._documentGuide=e,this._documentGuideApplied=!1),await this._applyDocumentGuide()}async _applyDocumentGuide(){const e=this._session;if(!(e===null||this._documentGuideApplied||this._documentGuideSendingTo===e)){this._documentGuideSendingTo=e;try{for(;!this._documentGuideApplied&&this._session===e;){const t=this._documentGuide;if(!await this._sendDocumentGuide(e,t))return;this._session===e&&d(t,this._documentGuide)&&(this._documentGuideApplied=!0)}}finally{this._documentGuideSendingTo===e&&(this._documentGuideSendingTo=null)}}}async _sendDocumentGuide(e,t){try{return await e.setDocumentGuide?.(t?.widthMm??0,t?.frameWidthFraction??0),!0}catch(s){return this._documentGuideFailureReported||(this._documentGuideFailureReported=!0,l.captureException(s instanceof Error?s:new Error(String(s)))),!1}}async setTorch(e){await this._session?.setTorch(e)}async tapToFocus(e){const t=this._session;t&&(this._clearFocusTimers(),this._setFocusState(n.running(e)),t.tapToFocus(e.x,e.y).catch(()=>{this._clearFocusTimers(),this._setFocusState(n.unspecified())}),this._focusRunningTimeout=setTimeout(()=>{this._setFocusState(n.success(e)),this._focusSettledTimeout=setTimeout(()=>{this._setFocusState(n.unspecified())},h.focusSettledDurationMs)},h.focusRunningDurationMs))}meterExposure(e){const t=this._session;if(!t)return;const s=e?w(e,g(this._state.currentLens===a.front,S.OS==="android")):null;t.setMeteringRegion?.(s).catch(()=>{})}async release(){this._released=!0,this._clearFocusTimers(),await this._teardownSession(),this._setState({...this._state,isCameraRunning:!1,isStreaming:!1})}_onKameraEvent(e){for(const t of[...this._eventListeners])t(e);e.type==="error"&&this._setState({...this._state,isStreaming:!1})}async _teardownSession(){this._removeEventListener?.(),this._removeEventListener=null;const e=this._session;this._session=null,this._capabilities=null,e&&await e.release()}_clearFocusTimers(){this._focusRunningTimeout&&clearTimeout(this._focusRunningTimeout),this._focusSettledTimeout&&clearTimeout(this._focusSettledTimeout),this._focusRunningTimeout=null,this._focusSettledTimeout=null}_setState(e){this._state=e,this._notify()}_setFocusState(e){this._focusState=e,this._notify()}_notify(){for(const e of[...this._listeners])e(this._state,this._focusState)}}function d(i,e){return i?.widthMm===e?.widthMm&&i?.frameWidthFraction===e?.frameWidthFraction}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export const UseSmileIDErrorCode={Builder:{ValidationError:"BUILDER_VALIDATION_ERROR",InvalidConfiguration:"BUILDER_INVALID_CONFIG",MissingRequiredField:"BUILDER_MISSING_FIELD",InvalidConsentConfig:"BUILDER_INVALID_CONSENT_CONFIG",MissingCaptureType:"BUILDER_MISSING_CAPTURE_TYPE",EmptyScreensBlock:"BUILDER_EMPTY_SCREENS",MissingScreenConfiguration:"BUILDER_MISSING_SCREEN_CONFIG",DuplicateScreenType:"BUILDER_DUPLICATE_SCREEN",InvalidSelfieCaptureConfig:"BUILDER_INVALID_SELFIE_CONFIG",AgentModeWithEnhancedLiveness:"BUILDER_AGENT_MODE_WITH_ENHANCED_LIVENESS",InvalidDocumentCaptureConfig:"BUILDER_INVALID_DOCUMENT_CONFIG",MissingMLConfiguration:"BUILDER_MISSING_ML_CONFIG",MissingNetworkConfiguration:"BUILDER_MISSING_NETWORK_CONFIG",MissingJobType:"BUILDER_MISSING_JOB_TYPE",MissingCaptureForPreview:"BUILDER_MISSING_CAPTURE_FOR_PREVIEW",InvalidScreenOrder:"BUILDER_INVALID_SCREEN_ORDER",ConfigurationBlockFailed:"BUILDER_CONFIG_BLOCK_FAILED",InvalidConsentInformation:"BUILDER_INVALID_CONSENT_INFORMATION",InvalidJobTypeScreenConfig:"BUILDER_INVALID_JOB_TYPE_SCREEN_CONFIG",UnusedJobParams:"BUILDER_UNUSED_JOB_PARAMS"},Camera:{InitializationFailed:"CAMERA_INIT_FAILED",PermissionDenied:"CAMERA_PERMISSION_DENIED",CaptureFailed:"CAMERA_CAPTURE_FAILED",UseCaseNotSupported:"CAMERA_USE_CASE_NOT_SUPPORTED",VideoCaptureDisabled:"CAMERA_VIDEO_DISABLED",Unknown:"CAMERA_UNKNOWN_ERROR"},Network:{ConnectionFailed:"NETWORK_CONNECTION_FAILED",Timeout:"NETWORK_TIMEOUT",RequestFailed:"NETWORK_REQUEST_FAILED",ResponseParseError:"NETWORK_PARSE_ERROR",Unauthorized:"NETWORK_UNAUTHORIZED",Forbidden:"NETWORK_FORBIDDEN",NotFound:"NETWORK_NOT_FOUND",RateLimit:"NETWORK_RATE_LIMIT",ServerError:"NETWORK_SERVER_ERROR",CallbackUrlRequired:"NETWORK_CALLBACK_URL_REQUIRED"},Validation:{InvalidInput:"VALIDATION_INVALID_INPUT",InvalidState:"VALIDATION_INVALID_STATE",ConstraintViolation:"VALIDATION_CONSTRAINT_VIOLATION"},General:{Unknown:"GENERAL_UNKNOWN_ERROR",InvalidState:"GENERAL_INVALID_STATE",OperationFailed:"GENERAL_OPERATION_FAILED"}};const I=new Set([UseSmileIDErrorCode.Camera.CaptureFailed,UseSmileIDErrorCode.Network.ConnectionFailed,UseSmileIDErrorCode.Network.Timeout,UseSmileIDErrorCode.Network.
|
|
1
|
+
"use strict";export const UseSmileIDErrorCode={Builder:{ValidationError:"BUILDER_VALIDATION_ERROR",InvalidConfiguration:"BUILDER_INVALID_CONFIG",MissingRequiredField:"BUILDER_MISSING_FIELD",InvalidConsentConfig:"BUILDER_INVALID_CONSENT_CONFIG",MissingCaptureType:"BUILDER_MISSING_CAPTURE_TYPE",EmptyScreensBlock:"BUILDER_EMPTY_SCREENS",MissingScreenConfiguration:"BUILDER_MISSING_SCREEN_CONFIG",DuplicateScreenType:"BUILDER_DUPLICATE_SCREEN",InvalidSelfieCaptureConfig:"BUILDER_INVALID_SELFIE_CONFIG",AgentModeWithEnhancedLiveness:"BUILDER_AGENT_MODE_WITH_ENHANCED_LIVENESS",InvalidDocumentCaptureConfig:"BUILDER_INVALID_DOCUMENT_CONFIG",MissingMLConfiguration:"BUILDER_MISSING_ML_CONFIG",MissingNetworkConfiguration:"BUILDER_MISSING_NETWORK_CONFIG",MissingJobType:"BUILDER_MISSING_JOB_TYPE",MissingCaptureForPreview:"BUILDER_MISSING_CAPTURE_FOR_PREVIEW",InvalidScreenOrder:"BUILDER_INVALID_SCREEN_ORDER",ConfigurationBlockFailed:"BUILDER_CONFIG_BLOCK_FAILED",InvalidConsentInformation:"BUILDER_INVALID_CONSENT_INFORMATION",InvalidJobTypeScreenConfig:"BUILDER_INVALID_JOB_TYPE_SCREEN_CONFIG",UnusedJobParams:"BUILDER_UNUSED_JOB_PARAMS"},Camera:{InitializationFailed:"CAMERA_INIT_FAILED",PermissionDenied:"CAMERA_PERMISSION_DENIED",CaptureFailed:"CAMERA_CAPTURE_FAILED",UseCaseNotSupported:"CAMERA_USE_CASE_NOT_SUPPORTED",VideoCaptureDisabled:"CAMERA_VIDEO_DISABLED",Unknown:"CAMERA_UNKNOWN_ERROR"},Network:{ConnectionFailed:"NETWORK_CONNECTION_FAILED",Timeout:"NETWORK_TIMEOUT",RequestFailed:"NETWORK_REQUEST_FAILED",ResponseParseError:"NETWORK_PARSE_ERROR",Unauthorized:"NETWORK_UNAUTHORIZED",Forbidden:"NETWORK_FORBIDDEN",NotFound:"NETWORK_NOT_FOUND",RateLimit:"NETWORK_RATE_LIMIT",ServerError:"NETWORK_SERVER_ERROR",CallbackUrlRequired:"NETWORK_CALLBACK_URL_REQUIRED"},Validation:{InvalidInput:"VALIDATION_INVALID_INPUT",InvalidState:"VALIDATION_INVALID_STATE",ConstraintViolation:"VALIDATION_CONSTRAINT_VIOLATION"},General:{Unknown:"GENERAL_UNKNOWN_ERROR",InvalidState:"GENERAL_INVALID_STATE",OperationFailed:"GENERAL_OPERATION_FAILED"}};const I=new Set([UseSmileIDErrorCode.Camera.CaptureFailed,UseSmileIDErrorCode.Network.ConnectionFailed,UseSmileIDErrorCode.Network.Timeout,UseSmileIDErrorCode.Network.RateLimit,UseSmileIDErrorCode.Network.ServerError]),e=new Set(Object.values(UseSmileIDErrorCode.Network));export function isRetryableErrorCode(E){return I.has(E)}export function isNetworkErrorCode(E){return e.has(E)}
|
|
@@ -42,13 +42,17 @@ export declare class NetworkServerException extends UseSmileIDNetworkException {
|
|
|
42
42
|
readonly errorCode?: NetworkCode;
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Network reachability / DNS / TLS / socket failure — never reached the server. A cancelled
|
|
47
|
+
* request carries `NETWORK_REQUEST_FAILED`, which isn't retried.
|
|
48
|
+
*/
|
|
46
49
|
export declare class NetworkTransportException extends UseSmileIDNetworkException {
|
|
47
50
|
readonly errorCode: NetworkCode;
|
|
48
|
-
constructor({ cause, message, errorDetails, }?: {
|
|
51
|
+
constructor({ cause, message, errorDetails, errorCode, }?: {
|
|
49
52
|
readonly cause?: unknown;
|
|
50
53
|
readonly message?: string;
|
|
51
54
|
readonly errorDetails?: Readonly<Record<string, unknown>>;
|
|
55
|
+
readonly errorCode?: NetworkCode;
|
|
52
56
|
});
|
|
53
57
|
}
|
|
54
58
|
/** Request exceeded the configured timeout. Specialised transport failure. */
|
|
@@ -107,11 +111,14 @@ export declare class NetworkUnknownException extends UseSmileIDNetworkException
|
|
|
107
111
|
* server).
|
|
108
112
|
* - `AxiosError` with `response` → [NetworkServerException] carrying
|
|
109
113
|
* the wire status code; status routing via [statusToErrorCode]
|
|
110
|
-
* covers 401/403/404/429/5xx + a generic fallback.
|
|
114
|
+
* covers 401/403/404/408/429/5xx + a generic fallback. A 408 keeps this
|
|
115
|
+
* type, with its `statusCode`, and carries `NETWORK_TIMEOUT`.
|
|
111
116
|
* - Already-typed [UseSmileIDNetworkException] → pass-through so the
|
|
112
117
|
* mapper is safe to call idempotently across retry / wrap paths.
|
|
113
118
|
* - Anything else → [NetworkUnknownException] with the underlying
|
|
114
119
|
* value preserved as `cause` for triage.
|
|
115
120
|
*/
|
|
116
121
|
export declare function mapAxiosError(error: unknown): UseSmileIDNetworkException;
|
|
122
|
+
/** axios's code for a request aborted through a signal or cancel token. */
|
|
123
|
+
export declare const CANCELED = "ERR_CANCELED";
|
|
117
124
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{isAxiosError as a}from"axios";import{UseSmileIDErrorCode as r}from"./use_smile_id_error_code";import{UseSmileIDException as c}from"./use_smile_id_exception";export class UseSmileIDNetworkException extends c{}export class NetworkServerException extends UseSmileIDNetworkException{errorCode;statusCode;constructor({statusCode:t,cause:s,message:o,errorDetails:n,errorCode:i}){super({message:o??`HTTP ${t}`,suggestedFix:"Inspect the server response body and retry if the failure is transient.",cause:s,errorDetails:n}),this.statusCode=t,this.errorCode=i??u(t)}}export class NetworkTransportException extends UseSmileIDNetworkException{errorCode
|
|
1
|
+
"use strict";import{isAxiosError as a}from"axios";import{UseSmileIDErrorCode as r}from"./use_smile_id_error_code";import{UseSmileIDException as c}from"./use_smile_id_exception";export class UseSmileIDNetworkException extends c{}export class NetworkServerException extends UseSmileIDNetworkException{errorCode;statusCode;constructor({statusCode:t,cause:s,message:o,errorDetails:n,errorCode:i}){super({message:o??`HTTP ${t}`,suggestedFix:"Inspect the server response body and retry if the failure is transient.",cause:s,errorDetails:n}),this.statusCode=t,this.errorCode=i??u(t)}}export class NetworkTransportException extends UseSmileIDNetworkException{errorCode;constructor({cause:t,message:s="Network transport failure.",errorDetails:o,errorCode:n=r.Network.ConnectionFailed}={}){super({message:s,suggestedFix:"Check the device's network connection and verify the configured base URL is reachable.",cause:t,errorDetails:o}),this.errorCode=n}}export class NetworkTimeoutException extends UseSmileIDNetworkException{errorCode=r.Network.Timeout;constructor({cause:t,message:s="The request timed out.",errorDetails:o}={}){super({message:s,suggestedFix:"Increase the timeout via `n.timeouts(...)` if appropriate, or retry once connectivity recovers.",cause:t,errorDetails:o})}}export class NetworkResponseParseException extends UseSmileIDNetworkException{errorCode=r.Network.ResponseParseError;constructor({cause:t,message:s="Failed to parse response body.",errorDetails:o}={}){super({message:s,suggestedFix:"Confirm the SDK version matches the backend's response schema.",cause:t,errorDetails:o})}}export class NetworkRequestBuildException extends UseSmileIDNetworkException{errorCode=r.Network.RequestFailed;constructor({cause:t,message:s="Failed to build request.",errorDetails:o}={}){super({message:s,suggestedFix:"Verify the request payload satisfies the multipart / URL contract before submission.",cause:t,errorDetails:o})}}export class NetworkUnknownException extends UseSmileIDNetworkException{errorCode=r.Network.RequestFailed;constructor({cause:t,message:s="Unknown network error.",errorDetails:o}={}){super({message:s,suggestedFix:"Log the wrapped `cause` for triage \u2014 anything reaching this branch represents a gap in error mapping.",cause:t,errorDetails:o})}}function u(e){return e===401?r.Network.Unauthorized:e===403?r.Network.Forbidden:e===404?r.Network.NotFound:e===408?r.Network.Timeout:e===429?r.Network.RateLimit:e>=500&&e<600?r.Network.ServerError:r.Network.RequestFailed}export function mapAxiosError(e){return e instanceof UseSmileIDNetworkException?e:a(e)?d(e):e instanceof Error?new NetworkUnknownException({message:e.message,cause:e}):new NetworkUnknownException({message:String(e),cause:e})}function d(e){if(e.code==="ERR_BAD_OPTION"||e.code==="ERR_BAD_OPTION_VALUE"||e.code==="ERR_INVALID_URL")return new NetworkRequestBuildException({message:e.message,cause:e});if(e.code===CANCELED)return new NetworkTransportException({message:e.message,cause:e,errorCode:r.Network.RequestFailed});if(e.code==="ECONNABORTED")return new NetworkTimeoutException({message:e.message,cause:e});if(!e.response)return new NetworkTransportException({message:e.message,cause:e});const t=e.response.status,s=e.response.data;return new NetworkServerException({statusCode:t,message:s?.message??`HTTP ${t}: ${e.message}`,cause:e,...s?.message?.trim()===p?{errorCode:r.Network.CallbackUrlRequired}:{}})}export const CANCELED="ERR_CANCELED";const p="A valid callback_url is required in the request or a default callback URL must be configured in the portal.";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";export function isDevelopmentBuild(){return typeof __DEV__<"u"&&__DEV__}
|
|
@@ -33,6 +33,12 @@ export type ConfigOptions = {
|
|
|
33
33
|
* `docs/Architecture.md § Rollout`.
|
|
34
34
|
*/
|
|
35
35
|
readonly allowOfflineMode: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Hand the captured job back through `onResult` as `{ status: "captured" }`
|
|
38
|
+
* instead of submitting it. The captured files are kept on disk for you to
|
|
39
|
+
* forward and delete. Absent reads as `false`.
|
|
40
|
+
*/
|
|
41
|
+
readonly skipApiSubmission?: boolean;
|
|
36
42
|
/**
|
|
37
43
|
* Journey-wide "Powered by Smile ID" branding; document capture never shows
|
|
38
44
|
* it. Defaults to `true`.
|
|
@@ -29,7 +29,7 @@ export type AnalyzerRegistry = {
|
|
|
29
29
|
readonly selfie?: FaceAnalyzer;
|
|
30
30
|
/**
|
|
31
31
|
* Wire-shape `FaceConfig` built from the DSL scope (analyzer key,
|
|
32
|
-
*
|
|
32
|
+
* FaceDetectorConfig). Falls back to native
|
|
33
33
|
* platform defaults when omitted.
|
|
34
34
|
*/
|
|
35
35
|
readonly selfieConfig?: FaceConfig;
|
|
@@ -24,7 +24,10 @@ export declare enum LogLevel {
|
|
|
24
24
|
basic = "basic",
|
|
25
25
|
/** `basic` + request/response headers (pre-redaction applied). */
|
|
26
26
|
headers = "headers",
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* `headers` + request/response bodies, in a development build (`__DEV__`) only. A job's body
|
|
29
|
+
* carries the images, consent and device metadata, so any other build logs `headers`.
|
|
30
|
+
*/
|
|
28
31
|
body = "body"
|
|
29
32
|
}
|
|
30
33
|
export type NetworkLoggingConfig = {
|
|
@@ -16,17 +16,19 @@ export type ConsentInformation = {
|
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
18
|
* Subject's personal details, sent as the `user_details` JSON part.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* The SDK checks none of these fields: it submits them as given, empty
|
|
20
|
+
* included, and the API rejects a job that lacks one it needs, naming the
|
|
21
|
+
* field. Which fields a job needs is the API's call, per partner and
|
|
22
|
+
* product: `GET /v3/services/config` lists them.
|
|
21
23
|
*/
|
|
22
24
|
export type UserDetails = {
|
|
23
|
-
/** Subject's given / first names.
|
|
25
|
+
/** Subject's given / first names. */
|
|
24
26
|
readonly givenNames: string;
|
|
25
|
-
/** Subject's family / last name.
|
|
27
|
+
/** Subject's family / last name. */
|
|
26
28
|
readonly lastName: string;
|
|
27
|
-
/**
|
|
29
|
+
/** Email address. */
|
|
28
30
|
readonly email?: string;
|
|
29
|
-
/**
|
|
31
|
+
/** Phone number in international format (leading `+`). */
|
|
30
32
|
readonly phoneNumber?: string;
|
|
31
33
|
};
|
|
32
34
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CapturedJob } from "../../data/model/captured_job";
|
|
1
2
|
/**
|
|
2
3
|
* The outcome of an SDK operation: success with a value, failure with an
|
|
3
4
|
* error, or cancellation when the user exits the flow. Surfaced to
|
|
@@ -16,6 +17,15 @@ export type UseSmileIDResult<T> = {
|
|
|
16
17
|
readonly error: unknown;
|
|
17
18
|
} | {
|
|
18
19
|
readonly status: "cancelled";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The flow captured everything the job needs and stopped short of
|
|
23
|
+
* submitting it, because `c.skipApiSubmission = true`. `job` holds the
|
|
24
|
+
* files and metadata to forward.
|
|
25
|
+
*/
|
|
26
|
+
| {
|
|
27
|
+
readonly status: "captured";
|
|
28
|
+
readonly job: CapturedJob;
|
|
19
29
|
};
|
|
20
30
|
/** A successful outcome carrying the resulting value. */
|
|
21
31
|
export declare function success<T>(value: T): UseSmileIDResult<T>;
|
|
@@ -27,3 +37,4 @@ export declare function failure<T>(error: unknown): UseSmileIDResult<T>;
|
|
|
27
37
|
* so cancellation can be handled separately.
|
|
28
38
|
*/
|
|
29
39
|
export declare function cancelled<T>(): UseSmileIDResult<T>;
|
|
40
|
+
export declare function captured<T>(job: CapturedJob): UseSmileIDResult<T>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export function success(t){return{status:"success",value:t}}export function failure(t){return{status:"failure",error:t}}export function cancelled(){return{status:"cancelled"}}
|
|
1
|
+
"use strict";export function success(t){return{status:"success",value:t}}export function failure(t){return{status:"failure",error:t}}export function cancelled(){return{status:"cancelled"}}export function captured(t){return{status:"captured",job:t}}
|