@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.
Files changed (169) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/README.md +57 -18
  3. package/dist/camera/bounding_box_metering_region.d.ts +10 -0
  4. package/dist/camera/bounding_box_metering_region.js +1 -0
  5. package/dist/camera/camera_defaults.d.ts +0 -2
  6. package/dist/camera/camera_defaults.js +1 -1
  7. package/dist/camera/document_image_pipeline.d.ts +9 -0
  8. package/dist/camera/document_image_pipeline.js +1 -1
  9. package/dist/camera/imported_document_folder.d.ts +15 -0
  10. package/dist/camera/imported_document_folder.js +1 -0
  11. package/dist/camera/kamera_config_factory.d.ts +11 -1
  12. package/dist/camera/kamera_config_factory.js +1 -1
  13. package/dist/camera/use_smile_id_frame_stream.js +1 -1
  14. package/dist/camera/use_smile_id_kamera_camera_system.d.ts +46 -2
  15. package/dist/camera/use_smile_id_kamera_camera_system.js +1 -1
  16. package/dist/core/exception/use_smile_id_error_code.js +1 -1
  17. package/dist/core/exception/use_smile_id_network_exception.d.ts +10 -3
  18. package/dist/core/exception/use_smile_id_network_exception.js +1 -1
  19. package/dist/core/is_development_build.d.ts +6 -0
  20. package/dist/core/is_development_build.js +1 -0
  21. package/dist/core/models/config.d.ts +6 -0
  22. package/dist/core/models/ml_config.d.ts +1 -1
  23. package/dist/core/models/network_config.d.ts +4 -1
  24. package/dist/core/models/params.d.ts +8 -6
  25. package/dist/core/models/result.d.ts +11 -0
  26. package/dist/core/models/result.js +1 -1
  27. package/dist/core/perf/capture_perf_trace.d.ts +90 -0
  28. package/dist/core/perf/capture_perf_trace.js +1 -0
  29. package/dist/data/api/interceptors/logging_interceptor.js +1 -1
  30. package/dist/data/api/interceptors/smile_auth_interceptor.js +1 -1
  31. package/dist/data/api/request_capture.d.ts +16 -0
  32. package/dist/data/api/request_capture.js +1 -0
  33. package/dist/data/api/smile_id_api.d.ts +1 -1
  34. package/dist/data/api/smile_id_api.js +1 -1
  35. package/dist/data/factory/networking_factory.d.ts +3 -1
  36. package/dist/data/factory/networking_factory.js +1 -1
  37. package/dist/data/model/captured_job.d.ts +49 -0
  38. package/dist/data/model/captured_job.js +1 -0
  39. package/dist/data/model/job_request.d.ts +1 -1
  40. package/dist/data/model/sealed_job_request.d.ts +15 -0
  41. package/dist/data/model/sealed_job_request.js +1 -0
  42. package/dist/data/model/token_payload.d.ts +5 -7
  43. package/dist/data/model/token_payload.js +1 -1
  44. package/dist/data/model/user_details_payload.d.ts +6 -4
  45. package/dist/data/repository/use_smile_id_repository.d.ts +7 -0
  46. package/dist/data/repository/use_smile_id_repository_impl.d.ts +29 -2
  47. package/dist/data/repository/use_smile_id_repository_impl.js +1 -1
  48. package/dist/data/security/job_package_crypto.d.ts +41 -0
  49. package/dist/data/security/job_package_crypto.js +1 -0
  50. package/dist/data/security/job_package_sealer.d.ts +35 -0
  51. package/dist/data/security/job_package_sealer.js +1 -0
  52. package/dist/data/security/signing_crypto.d.ts +34 -0
  53. package/dist/data/security/signing_crypto.js +1 -1
  54. package/dist/data/util/retry_util.d.ts +9 -6
  55. package/dist/data/util/retry_util.js +1 -1
  56. package/dist/debug/selfie_preview_debug_geometry.d.ts +13 -13
  57. package/dist/debug/selfie_preview_debug_geometry.js +1 -1
  58. package/dist/debug/use_smile_id_debug.d.ts +25 -2
  59. package/dist/debug/use_smile_id_debug.js +1 -1
  60. package/dist/debug/use_smile_id_document_preview_debug_overlay.js +1 -1
  61. package/dist/debug/use_smile_id_selfie_preview_debug_overlay.d.ts +5 -1
  62. package/dist/debug/use_smile_id_selfie_preview_debug_overlay.js +1 -1
  63. package/dist/index.d.ts +4 -1
  64. package/dist/index.js +1 -1
  65. package/dist/l10n/strings_en.d.ts +3 -3
  66. package/dist/l10n/strings_en.js +2 -2
  67. package/dist/l10n/use_smile_id_localizations.d.ts +5 -7
  68. package/dist/l10n/use_smile_id_localizations.js +1 -1
  69. package/dist/metadata/metadata_configuration_mappers.d.ts +3 -5
  70. package/dist/metadata/metadata_configuration_mappers.js +1 -1
  71. package/dist/metadata/use_smile_id_metadata_factory.js +1 -1
  72. package/dist/presentation/assets/si_dark_lighting_animation.json +1 -927
  73. package/dist/presentation/assets/si_device_orientation_animation.json +1 -850
  74. package/dist/presentation/assets/si_document_green_book_hero_animation.json +1 -4740
  75. package/dist/presentation/assets/si_document_hero_animation.json +1 -4870
  76. package/dist/presentation/assets/si_document_hero_back_animation.json +1 -2122
  77. package/dist/presentation/assets/si_document_passport_hero_animation.json +1 -14540
  78. package/dist/presentation/assets/si_ess_all_directions_hero_animation.json +1 -5906
  79. package/dist/presentation/assets/si_ess_animation_all_directions.json +1 -5906
  80. package/dist/presentation/assets/si_selfie_hero_animation.json +1 -1468
  81. package/dist/presentation/assets/si_smile_to_capture_animation.json +1 -1412
  82. package/dist/presentation/components/helpers/captured_image_uri.d.ts +11 -0
  83. package/dist/presentation/components/helpers/captured_image_uri.js +1 -0
  84. package/dist/presentation/components/helpers/captured_oval_region.d.ts +40 -0
  85. package/dist/presentation/components/helpers/captured_oval_region.js +1 -0
  86. package/dist/presentation/components/helpers/document_interior_crop.d.ts +13 -2
  87. package/dist/presentation/components/helpers/document_interior_crop.js +1 -1
  88. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts +35 -29
  89. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js +1 -1
  90. package/dist/presentation/components/helpers/selfie_interior_crop.js +1 -1
  91. package/dist/presentation/components/use_smile_id_capture_content.d.ts +5 -2
  92. package/dist/presentation/components/use_smile_id_capture_content.js +1 -1
  93. package/dist/presentation/components/use_smile_id_captured_document_image.d.ts +8 -4
  94. package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -1
  95. package/dist/presentation/components/use_smile_id_captured_image_oval.d.ts +6 -0
  96. package/dist/presentation/components/use_smile_id_captured_image_oval.js +1 -1
  97. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +17 -7
  98. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +1 -1
  99. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_direction_animation.d.ts +13 -14
  100. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_direction_animation.js +1 -1
  101. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_full_animation.js +1 -1
  102. package/dist/presentation/components/use_smile_id_face_oval_image.js +1 -1
  103. package/dist/presentation/flow/analytics/unsatisfied_reasons.d.ts +14 -0
  104. package/dist/presentation/flow/analytics/unsatisfied_reasons.js +1 -0
  105. package/dist/presentation/flow/analytics/use_smile_id_analytics_event.d.ts +8 -7
  106. package/dist/presentation/flow/analytics/use_smile_id_analytics_event.js +1 -1
  107. package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.d.ts +9 -0
  108. package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.js +1 -1
  109. package/dist/presentation/flow/config/flow_configuration_camera.d.ts +9 -0
  110. package/dist/presentation/flow/config/flow_configuration_camera.js +1 -0
  111. package/dist/presentation/flow/dsl/builder/config_builder.d.ts +6 -0
  112. package/dist/presentation/flow/dsl/builder/config_builder.js +1 -1
  113. package/dist/presentation/flow/dsl/builder/ml_builder.d.ts +1 -7
  114. package/dist/presentation/flow/dsl/builder/ml_builder.js +1 -1
  115. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +0 -9
  116. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +1 -1
  117. package/dist/presentation/flow/flow_navigation_manager.d.ts +39 -70
  118. package/dist/presentation/flow/flow_navigation_manager.js +1 -1
  119. package/dist/presentation/flow/flow_navigation_manager_analytics.d.ts +23 -0
  120. package/dist/presentation/flow/flow_navigation_manager_analytics.js +1 -0
  121. package/dist/presentation/flow/flow_navigation_manager_documents.d.ts +18 -0
  122. package/dist/presentation/flow/flow_navigation_manager_documents.js +1 -0
  123. package/dist/presentation/flow/flow_navigation_manager_job_request.d.ts +7 -0
  124. package/dist/presentation/flow/flow_navigation_manager_job_request.js +1 -0
  125. package/dist/presentation/flow/flow_navigation_manager_routing.d.ts +12 -0
  126. package/dist/presentation/flow/flow_navigation_manager_routing.js +1 -0
  127. package/dist/presentation/flow/flow_navigation_manager_submission.d.ts +17 -0
  128. package/dist/presentation/flow/flow_navigation_manager_submission.js +1 -0
  129. package/dist/presentation/flow/job_submission_state.d.ts +4 -0
  130. package/dist/presentation/flow/screen/helpers/face_session_config.d.ts +1 -1
  131. package/dist/presentation/flow/screen/helpers/failure_message.js +1 -1
  132. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +1 -1
  133. package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
  134. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +1 -1
  135. package/dist/presentation/flow/screen/orchestrated_processing_screen.js +1 -1
  136. package/dist/presentation/flow/use_smile_id_flow.js +1 -1
  137. package/dist/presentation/flow/validation/flow_validator.d.ts +0 -19
  138. package/dist/presentation/flow/validation/flow_validator.js +1 -1
  139. package/dist/presentation/flow/validation/job_type_validator.js +1 -1
  140. package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts +0 -12
  141. package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -1
  142. package/dist/presentation/screen/helpers/capture_ui_state.d.ts +33 -6
  143. package/dist/presentation/screen/helpers/capture_ui_state.js +1 -1
  144. package/dist/presentation/screen/helpers/dropped_frames.d.ts +6 -0
  145. package/dist/presentation/screen/helpers/dropped_frames.js +1 -0
  146. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -0
  147. package/dist/presentation/screen/helpers/ess_turn_haptics.js +1 -1
  148. package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts +5 -1
  149. package/dist/presentation/screen/helpers/instructions_screen_helpers.js +1 -1
  150. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +23 -1
  151. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +1 -1
  152. package/dist/presentation/screen/helpers/use_debounced_feedback_key.d.ts +1 -1
  153. package/dist/presentation/screen/helpers/use_debounced_feedback_key.js +1 -1
  154. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -1
  155. package/dist/presentation/screen/use_smile_id_capture_screen.js +1 -1
  156. package/dist/presentation/screen/use_smile_id_consent_screen.js +1 -1
  157. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +3 -0
  158. package/dist/presentation/screen/use_smile_id_preview_screen.js +1 -1
  159. package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +3 -0
  160. package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
  161. package/dist/presentation/utils/locale_utils.d.ts +3 -6
  162. package/dist/presentation/utils/locale_utils.js +1 -1
  163. package/dist/testing/scenario_expectations.d.ts +7 -2
  164. package/dist/testing/scenario_expectations.js +1 -1
  165. package/dist/testing/scripted_scan_results.d.ts +2 -1
  166. package/dist/testing/scripted_scan_results.js +1 -1
  167. package/package.json +7 -7
  168. package/dist/presentation/flow/validation/user_details_validator.d.ts +0 -8
  169. 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.2.0` is built and verified against Expo SDK 57 and React Native 0.86. The floors above are the supported minimum and keep working.
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
- (Use `@smileid/usesmileid_huawei_{face,document}` instead of mlkit on no-GMS Android targets.)
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("Captured:", result.value);
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: 'failure', error: ... })` with `cancelled` semantics. |
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 failure) |
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 required by every KYC and document job; `country` is the ISO 3166-1 alpha-2 code.
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
- `user_details` requires non-blank `givenNames` and `lastName` plus at least one of `email` / `phoneNumber`. Pre-flight any payload with `b.validateDocumentVerificationParams(...)`, `b.validateEnhancedDocumentVerificationParams(...)`, or `b.validateUserDetails(...)` before assigning.
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},firstFrameTimeoutMs:1e4,documentMaxDimension:1600,documentJpegQuality:.85,minGalleryDocumentDimensionPx:600,selfieJpegTargetWidth:480,selfieJpegTargetHeight:640};
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{Directory as m,File as i,Paths as s}from"expo-file-system";import{ImageManipulator as u,SaveFormat as d}from"expo-image-manipulator";import{DocumentSide as o,UseSmileIDCrashReporting as p}from"@smileid/usesmileid_bridge";import{CameraDefaults as a}from"./camera_defaults";import{computeDocumentResizeTarget as f}from"./document_resize_target";export{computeDocumentResizeTarget}from"./document_resize_target";const l="usesmileid_documents";export function documentArtifactFileName(e){switch(e){case o.Back:return"document_back.jpg";case o.Visa:return"document_visa.jpg";default:return"document_front.jpg"}}export async function resampleImportedDocument(e,n){const t=e.width!==void 0&&e.height!==void 0?f(e.width,e.height,a.documentMaxDimension):null,r=u.manipulate(e.uri);t&&r.resize(t);const c=await(await r.renderAsync()).saveAsync({compress:a.documentJpegQuality,format:d.JPEG});return g(c.uri,n)}function g(e,n){try{const t=new m(s.cache,l);t.exists||t.create({intermediates:!0});const r=new i(t,documentArtifactFileName(n));return r.exists&&r.delete(),new i(e).move(r),r.uri}catch(t){return p.captureException(t instanceof Error?t:new Error(String(t))),e}}
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
- /** Maps the SDK's capture inputs onto a kamera `KameraConfig` via the tuned kamera presets. */
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 t}from"@smileid/kamera";import{CaptureType as a}from"../core/models/ml_config";import{CameraLens as m}from"./camera_lens";export const KameraConfigFactory={create(e,o){const r=toKameraLens(o);return e===a.document?t.document(r):t.selfie(r)}};export function toKameraLens(e){return e===m.back?"back":"front"}
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{isDevBuild as R}from"../debug/use_smile_id_debug";import{UseSmileIDDebugTap as d}from"../debug/use_smile_id_debug_tap";export function useSmileIDFrameStream(e,r,u){const i=a(e),s=a(r),o=a(u?.onError),c=a(!1),f=a(!0);l(()=>{i.current=e},[e]),l(()=>{s.current=r},[r]),l(()=>{o.current=u?.onError},[u?.onError]),l(()=>(f.current=!0,()=>{f.current=!1}),[]);const n=y(t=>{const m=o.current;dispatchFrameResult({raw:t,analyzer:i.current,isBusy:()=>c.current,setBusy:p=>{c.current=p},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:u,isBusy:i,setBusy:s,isMounted:o,onResult:c,onError:f}=e;if(u&&!i()){s(!0);try{const n=D(r);if(n!==null){if(_.has(n))return;throw new B({reason:n})}if(u.kind==="face"){const t=h.parse(r);if(!o())return;c(t),R()&&d.analyzedFace(t.output)}else{const t=S.parse(r);if(!o())return;c(t),R()&&d.analyzedDocument(t.output)}}catch(n){if(!o())return;f?.(n)}finally{s(!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{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
- /** The kamera session surface this system drives; structural so tests can inject an in-memory fake. */
8
- export type KameraSessionLike = Pick<KameraSession, "start" | "stop" | "capture" | "setZoomRatio" | "setTorch" | "tapToFocus" | "setExposureCompensation" | "capabilities" | "release" | "addEventListener">;
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 _,KameraSession as h}from"@smileid/kamera";import{CaptureType as r}from"../core/models/ml_config";import{CameraDefaults as c}from"./camera_defaults";import{CameraLens as o}from"./camera_lens";import{FocusStates as i}from"./focus_state";import{KameraConfigFactory as u}from"./kamera_config_factory";export class UseSmileIDKameraCameraSystem{_createSession;_session=null;_removeEventListener=null;_state;_focusState=i.unspecified();_listeners=new Set;_eventListeners=new Set;_capabilities=null;_config={};_released=!1;_focusRunningTimeout=null;_focusSettledTimeout=null;constructor(e=o.front,t=()=>new h){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??r.selfie;return u.create(e,this._state.currentLens).preview.aspectRatio===_.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(n=>this._onKameraEvent(n)),s=this._config.captureType??r.selfie,a=this._config.lens??(s===r.document?o.back:o.front);try{await e.start(u.create(s,a)),this._capabilities=await e.capabilities()}catch(n){throw t(),await e.release(),n}this._removeEventListener=t,this._session=e,this._setState({...this._state,isCameraRunning:!0,isStreaming:!0,currentLens:a,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,a=s?Math.min(s.maxZoomRatio,Math.max(s.minZoomRatio,e)):e;await t.setZoomRatio(a)}async setTorch(e){await this._session?.setTorch(e)}async tapToFocus(e){const t=this._session;t&&(this._clearFocusTimers(),this._setFocusState(i.running(e)),t.tapToFocus(e.x,e.y).catch(()=>{this._clearFocusTimers(),this._setFocusState(i.unspecified())}),this._focusRunningTimeout=setTimeout(()=>{this._setFocusState(i.success(e)),this._focusSettledTimeout=setTimeout(()=>{this._setFocusState(i.unspecified())},c.focusSettledDurationMs)},c.focusRunningDurationMs))}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)}}
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.RequestFailed,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)}
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
- /** Network reachability / DNS / TLS / socket failure — never reached the server. */
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=r.Network.ConnectionFailed;constructor({cause:t,message:s="Network transport failure.",errorDetails:o}={}){super({message:s,suggestedFix:"Check the device's network connection and verify the configured base URL is reachable.",cause:t,errorDetails:o})}}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===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==="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}:{}})}const p="A valid callback_url is required in the request or a default callback URL must be configured in the portal.";
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,6 @@
1
+ /**
2
+ * Whether this is a development build. `__DEV__` is bundler-injected and
3
+ * absent outside Metro-like environments, where a bare reference throws
4
+ * rather than reading falsy. @internal
5
+ */
6
+ export declare function isDevelopmentBuild(): boolean;
@@ -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
- * ScanPerformanceProfile, FaceDetectorConfig). Falls back to native
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
- /** `headers` + request/response bodies. Verbose — partner-debug only. */
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
- * Requires non-blank `givenNames` and `lastName`, plus at least one of
20
- * `email` or `phoneNumber`.
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. Required, non-blank. */
25
+ /** Subject's given / first names. */
24
26
  readonly givenNames: string;
25
- /** Subject's family / last name. Required, non-blank. */
27
+ /** Subject's family / last name. */
26
28
  readonly lastName: string;
27
- /** Optional email address. */
29
+ /** Email address. */
28
30
  readonly email?: string;
29
- /** Optional phone number in international format (leading `+`). */
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}}