@smileid/usesmileid 12.2.0 → 12.2.1-SNAPSHOT.44

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 (151) hide show
  1. package/CHANGELOG.md +0 -1
  2. package/README.md +39 -6
  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/document_image_pipeline.d.ts +9 -0
  6. package/dist/camera/document_image_pipeline.js +1 -1
  7. package/dist/camera/imported_document_folder.d.ts +15 -0
  8. package/dist/camera/imported_document_folder.js +1 -0
  9. package/dist/camera/kamera_config_factory.d.ts +11 -1
  10. package/dist/camera/kamera_config_factory.js +1 -1
  11. package/dist/camera/use_smile_id_frame_stream.js +1 -1
  12. package/dist/camera/use_smile_id_kamera_camera_system.d.ts +46 -2
  13. package/dist/camera/use_smile_id_kamera_camera_system.js +1 -1
  14. package/dist/core/exception/use_smile_id_error_code.js +1 -1
  15. package/dist/core/exception/use_smile_id_network_exception.d.ts +10 -3
  16. package/dist/core/exception/use_smile_id_network_exception.js +1 -1
  17. package/dist/core/is_development_build.d.ts +6 -0
  18. package/dist/core/is_development_build.js +1 -0
  19. package/dist/core/models/config.d.ts +6 -0
  20. package/dist/core/models/ml_config.d.ts +1 -1
  21. package/dist/core/models/network_config.d.ts +4 -1
  22. package/dist/core/models/result.d.ts +11 -0
  23. package/dist/core/models/result.js +1 -1
  24. package/dist/core/perf/capture_perf_trace.d.ts +90 -0
  25. package/dist/core/perf/capture_perf_trace.js +1 -0
  26. package/dist/data/api/interceptors/logging_interceptor.js +1 -1
  27. package/dist/data/api/request_capture.d.ts +16 -0
  28. package/dist/data/api/request_capture.js +1 -0
  29. package/dist/data/api/smile_id_api.d.ts +1 -1
  30. package/dist/data/factory/networking_factory.d.ts +3 -1
  31. package/dist/data/factory/networking_factory.js +1 -1
  32. package/dist/data/model/captured_job.d.ts +49 -0
  33. package/dist/data/model/captured_job.js +1 -0
  34. package/dist/data/model/job_request.d.ts +1 -1
  35. package/dist/data/model/sealed_job_request.d.ts +15 -0
  36. package/dist/data/model/sealed_job_request.js +1 -0
  37. package/dist/data/repository/use_smile_id_repository.d.ts +7 -0
  38. package/dist/data/repository/use_smile_id_repository_impl.d.ts +29 -2
  39. package/dist/data/repository/use_smile_id_repository_impl.js +1 -1
  40. package/dist/data/security/job_package_crypto.d.ts +41 -0
  41. package/dist/data/security/job_package_crypto.js +1 -0
  42. package/dist/data/security/job_package_sealer.d.ts +35 -0
  43. package/dist/data/security/job_package_sealer.js +1 -0
  44. package/dist/data/security/signing_crypto.d.ts +34 -0
  45. package/dist/data/security/signing_crypto.js +1 -1
  46. package/dist/data/util/retry_util.d.ts +9 -6
  47. package/dist/data/util/retry_util.js +1 -1
  48. package/dist/debug/selfie_preview_debug_geometry.d.ts +13 -13
  49. package/dist/debug/selfie_preview_debug_geometry.js +1 -1
  50. package/dist/debug/use_smile_id_debug.d.ts +25 -2
  51. package/dist/debug/use_smile_id_debug.js +1 -1
  52. package/dist/debug/use_smile_id_document_preview_debug_overlay.js +1 -1
  53. package/dist/debug/use_smile_id_selfie_preview_debug_overlay.d.ts +5 -1
  54. package/dist/debug/use_smile_id_selfie_preview_debug_overlay.js +1 -1
  55. package/dist/index.d.ts +4 -1
  56. package/dist/index.js +1 -1
  57. package/dist/l10n/strings_en.d.ts +3 -3
  58. package/dist/l10n/strings_en.js +2 -2
  59. package/dist/l10n/use_smile_id_localizations.d.ts +5 -7
  60. package/dist/l10n/use_smile_id_localizations.js +1 -1
  61. package/dist/metadata/metadata_configuration_mappers.d.ts +3 -5
  62. package/dist/metadata/metadata_configuration_mappers.js +1 -1
  63. package/dist/presentation/assets/si_dark_lighting_animation.json +1 -927
  64. package/dist/presentation/assets/si_device_orientation_animation.json +1 -850
  65. package/dist/presentation/assets/si_document_green_book_hero_animation.json +1 -4740
  66. package/dist/presentation/assets/si_document_hero_animation.json +1 -4870
  67. package/dist/presentation/assets/si_document_hero_back_animation.json +1 -2122
  68. package/dist/presentation/assets/si_document_passport_hero_animation.json +1 -14540
  69. package/dist/presentation/assets/si_ess_all_directions_hero_animation.json +1 -5906
  70. package/dist/presentation/assets/si_ess_animation_all_directions.json +1 -5906
  71. package/dist/presentation/assets/si_selfie_hero_animation.json +1 -1468
  72. package/dist/presentation/assets/si_smile_to_capture_animation.json +1 -1412
  73. package/dist/presentation/components/helpers/captured_image_uri.d.ts +11 -0
  74. package/dist/presentation/components/helpers/captured_image_uri.js +1 -0
  75. package/dist/presentation/components/helpers/captured_oval_region.d.ts +40 -0
  76. package/dist/presentation/components/helpers/captured_oval_region.js +1 -0
  77. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts +35 -29
  78. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js +1 -1
  79. package/dist/presentation/components/helpers/selfie_interior_crop.js +1 -1
  80. package/dist/presentation/components/use_smile_id_capture_content.d.ts +5 -2
  81. package/dist/presentation/components/use_smile_id_capture_content.js +1 -1
  82. package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -1
  83. package/dist/presentation/components/use_smile_id_captured_image_oval.d.ts +6 -0
  84. package/dist/presentation/components/use_smile_id_captured_image_oval.js +1 -1
  85. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +17 -7
  86. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +1 -1
  87. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_direction_animation.d.ts +13 -14
  88. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_direction_animation.js +1 -1
  89. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_full_animation.js +1 -1
  90. package/dist/presentation/components/use_smile_id_face_oval_image.js +1 -1
  91. package/dist/presentation/flow/analytics/unsatisfied_reasons.d.ts +14 -0
  92. package/dist/presentation/flow/analytics/unsatisfied_reasons.js +1 -0
  93. package/dist/presentation/flow/analytics/use_smile_id_analytics_event.d.ts +8 -7
  94. package/dist/presentation/flow/analytics/use_smile_id_analytics_event.js +1 -1
  95. package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.d.ts +9 -0
  96. package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.js +1 -1
  97. package/dist/presentation/flow/config/flow_configuration_camera.d.ts +9 -0
  98. package/dist/presentation/flow/config/flow_configuration_camera.js +1 -0
  99. package/dist/presentation/flow/dsl/builder/config_builder.d.ts +6 -0
  100. package/dist/presentation/flow/dsl/builder/config_builder.js +1 -1
  101. package/dist/presentation/flow/dsl/builder/ml_builder.d.ts +1 -7
  102. package/dist/presentation/flow/dsl/builder/ml_builder.js +1 -1
  103. package/dist/presentation/flow/flow_navigation_manager.d.ts +39 -70
  104. package/dist/presentation/flow/flow_navigation_manager.js +1 -1
  105. package/dist/presentation/flow/flow_navigation_manager_analytics.d.ts +23 -0
  106. package/dist/presentation/flow/flow_navigation_manager_analytics.js +1 -0
  107. package/dist/presentation/flow/flow_navigation_manager_documents.d.ts +18 -0
  108. package/dist/presentation/flow/flow_navigation_manager_documents.js +1 -0
  109. package/dist/presentation/flow/flow_navigation_manager_job_request.d.ts +7 -0
  110. package/dist/presentation/flow/flow_navigation_manager_job_request.js +1 -0
  111. package/dist/presentation/flow/flow_navigation_manager_routing.d.ts +12 -0
  112. package/dist/presentation/flow/flow_navigation_manager_routing.js +1 -0
  113. package/dist/presentation/flow/flow_navigation_manager_submission.d.ts +17 -0
  114. package/dist/presentation/flow/flow_navigation_manager_submission.js +1 -0
  115. package/dist/presentation/flow/job_submission_state.d.ts +4 -0
  116. package/dist/presentation/flow/screen/helpers/face_session_config.d.ts +1 -1
  117. package/dist/presentation/flow/screen/helpers/failure_message.js +1 -1
  118. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +1 -1
  119. package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
  120. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +1 -1
  121. package/dist/presentation/flow/screen/orchestrated_processing_screen.js +1 -1
  122. package/dist/presentation/flow/use_smile_id_flow.js +1 -1
  123. package/dist/presentation/flow/validation/job_type_validator.js +1 -1
  124. package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts +0 -12
  125. package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -1
  126. package/dist/presentation/screen/helpers/capture_ui_state.d.ts +33 -6
  127. package/dist/presentation/screen/helpers/capture_ui_state.js +1 -1
  128. package/dist/presentation/screen/helpers/dropped_frames.d.ts +6 -0
  129. package/dist/presentation/screen/helpers/dropped_frames.js +1 -0
  130. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -0
  131. package/dist/presentation/screen/helpers/ess_turn_haptics.js +1 -1
  132. package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts +5 -1
  133. package/dist/presentation/screen/helpers/instructions_screen_helpers.js +1 -1
  134. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +23 -1
  135. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +1 -1
  136. package/dist/presentation/screen/helpers/use_debounced_feedback_key.d.ts +1 -1
  137. package/dist/presentation/screen/helpers/use_debounced_feedback_key.js +1 -1
  138. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -1
  139. package/dist/presentation/screen/use_smile_id_capture_screen.js +1 -1
  140. package/dist/presentation/screen/use_smile_id_consent_screen.js +1 -1
  141. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +3 -0
  142. package/dist/presentation/screen/use_smile_id_preview_screen.js +1 -1
  143. package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +3 -0
  144. package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
  145. package/dist/presentation/utils/locale_utils.d.ts +3 -6
  146. package/dist/presentation/utils/locale_utils.js +1 -1
  147. package/dist/testing/scenario_expectations.d.ts +7 -2
  148. package/dist/testing/scenario_expectations.js +1 -1
  149. package/dist/testing/scripted_scan_results.d.ts +2 -1
  150. package/dist/testing/scripted_scan_results.js +1 -1
  151. package/package.json +7 -7
package/CHANGELOG.md CHANGED
@@ -4,7 +4,6 @@ Full release notes: [Smile ID docs](https://docs.usesmileid.com/developer-resour
4
4
 
5
5
  See the repository CHANGELOG for the 12.2.0 release notes.
6
6
 
7
-
8
7
  ## 12.1.1
9
8
 
10
9
  See the repository CHANGELOG for the 12.1.1 release notes.
package/README.md CHANGED
@@ -153,6 +153,7 @@ The server issues `userId` and `jobId` and returns them in the submission respon
153
153
  b.config((c) => {
154
154
  c.enableDebugMode = false;
155
155
  c.allowOfflineMode = false;
156
+ c.skipApiSubmission = false;
156
157
  c.enableCrashReporting = true;
157
158
  c.showAttribution = true;
158
159
  });
@@ -162,6 +163,7 @@ b.config((c) => {
162
163
  | ---------------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
163
164
  | `enableDebugMode` | `boolean` | `false` | Shows a validation error screen on build failure |
164
165
  | `allowOfflineMode` | `boolean` | `false` | Allows the flow to run without a network connection |
166
+ | `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
167
  | `enableCrashReporting` | `boolean` | `true` | Enables Sentry crash reporting for the SDK. Set to `false` if your app runs its own Sentry |
166
168
  | `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
169
 
@@ -268,7 +270,7 @@ b.network((n) => {
268
270
  i.add(i.gzip());
269
271
  i.add(
270
272
  i.logging((l) => {
271
- l.level = LogLevel.body;
273
+ l.level = LogLevel.body; // bodies in a development build only
272
274
  }),
273
275
  );
274
276
  i.add(myCustomInterceptor); // partner-supplied
@@ -458,10 +460,10 @@ Calling `useFlowNavigation()` outside `<UseSmileIDBuilder>` throws — the misus
458
460
 
459
461
  ## Top-level builder callbacks
460
462
 
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 |
463
+ | Property | Type | Description |
464
+ | ------------------ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
465
+ | `onResult` | `(result: UseSmileIDResult<JobSubmissionResponse>) => void` | Called once when the flow finishes (success, failure, cancelled, or captured when `skipApiSubmission` is on) |
466
+ | `onAnalyticsEvent` | `((event: UseSmileIDAnalyticsEvent) => void) \| undefined` | Optional. Called for each analytics event during the flow |
465
467
 
466
468
  ---
467
469
 
@@ -522,13 +524,44 @@ b.onResult = (result) => {
522
524
  case "failure":
523
525
  console.error("flow failed:", result.error);
524
526
  break;
527
+ case "cancelled":
528
+ console.log("user exited before finishing");
529
+ break;
530
+ case "captured":
531
+ console.log(
532
+ `captured ${result.job.jobType}; forward ${result.job.sessionDirectory}`,
533
+ );
534
+ break;
525
535
  }
526
536
  };
527
537
  ```
528
538
 
529
539
  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
540
 
531
- `UseSmileIDResult<T>` is a discriminated union — `result.status` narrows it, no `try` / `catch` needed.
541
+ `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`.
542
+
543
+ A `"captured"` result carries a `CapturedJob`:
544
+
545
+ | Field | Type | Description |
546
+ | ------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
547
+ | `jobType` | `JobType` | The configured job type |
548
+ | `sessionId` | `string` | The flow's session id, also on every analytics event |
549
+ | `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) |
550
+ | `selfieImage` | `string \| undefined` | Selfie file URI for selfie-bearing jobs |
551
+ | `livenessImages` | `readonly string[]` | Liveness frame URIs for selfie-bearing jobs |
552
+ | `frontDocument`, `backDocument` | `string \| undefined` | Document side URIs for document jobs |
553
+ | `visaDocument` | `string \| undefined` | The visa page URI, for Residency Document Verification |
554
+ | `headers` | `Readonly<Record<string, string>>` | `smileid-token`, `smileid-source-sdk` and `smileid-source-sdk-version`, to send as request headers |
555
+ | `encryptedPackage` | `string` | The rest of the request, sealed for Smile ID; send it as the `encrypted_package` form field |
556
+ | `packageNonce`, `packageTag` | `string` | The package's nonce and tag; send them as the `package_nonce` and `package_tag` form fields |
557
+
558
+ 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.
559
+
560
+ 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 soon after the flow ends: the package carries the access token and the request timestamp from the capture, which the server checks and which can't be refreshed without capturing again.
561
+
562
+ 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.
563
+
564
+ 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
565
 
533
566
  ### Forwarding failures to a crash reporter
534
567
 
@@ -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))}
@@ -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 = {
@@ -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}}
@@ -0,0 +1,90 @@
1
+ import type { KameraMetric } from "@smileid/kamera";
2
+ /**
3
+ * The points a capture passes, in order, each timed from the capture screen
4
+ * appearing. The names are the log's, so a run on any platform reads the same.
5
+ */
6
+ export declare const CapturePerfMark: {
7
+ /** The capture screen appeared: the trace's zero. */
8
+ readonly screenShown: "screen_shown";
9
+ /** The screen asked the camera to start. */
10
+ readonly cameraStartRequested: "camera_start_requested";
11
+ /** The camera's start resolved. */
12
+ readonly cameraRunning: "camera_running";
13
+ /** kamera delivered its first frame. */
14
+ readonly firstFrame: "first_frame";
15
+ /** Analysis began: as the camera runs, or on a retry's Start tap. */
16
+ readonly streamingStarted: "streaming_started";
17
+ /** The first frame analysis came back. */
18
+ readonly firstScanResult: "first_scan_result";
19
+ /** The capture reached its terminal captured state. */
20
+ readonly captureComplete: "capture_complete";
21
+ /** The captured images are ready to hand off. */
22
+ readonly filesReady: "files_ready";
23
+ };
24
+ export type CapturePerfMark = (typeof CapturePerfMark)[keyof typeof CapturePerfMark];
25
+ /**
26
+ * How a capture attempt ended, as the log line names it. After `failed`, `retake` or `timeout`, the
27
+ * next attempt gets its own line.
28
+ */
29
+ export type CapturePerfOutcome = "captured" | "gallery" | "failed" | "retake" | "timeout" | "abandoned";
30
+ /** Whether the user can follow `outcome` with another attempt, which gets a fresh trace. */
31
+ export declare function capturePerfOutcomeContinues(outcome: CapturePerfOutcome): boolean;
32
+ /** Median and 95th percentile of some milliseconds, over `count` samples. */
33
+ export type CapturePerfStat = {
34
+ readonly count: number;
35
+ readonly p50Ms: number;
36
+ readonly p95Ms: number;
37
+ };
38
+ /** What one capture measured. */
39
+ export type CapturePerfSnapshot = {
40
+ /** Each mark reached, in milliseconds from `screen_shown`. */
41
+ readonly marks: Readonly<Partial<Record<CapturePerfMark, number>>>;
42
+ /** kamera's own start-to-first-frame, and whether it paid the camera stack's first start. */
43
+ readonly timeToFirstFrameMs?: number;
44
+ readonly coldStart?: boolean;
45
+ /** Take-to-close per analysed frame. */
46
+ readonly analysis?: CapturePerfStat;
47
+ /** How long each analysed frame waited in the frame hub before it was taken. */
48
+ readonly wait?: CapturePerfStat;
49
+ /** The last still's capture-to-result latency. */
50
+ readonly captureLatencyMs?: number;
51
+ };
52
+ /**
53
+ * What one capture attempt measured, as `UseSmileIDDebug.setPerfTraceListener` hands it over: the
54
+ * same as its log line, typed.
55
+ */
56
+ export type CapturePerfReport = CapturePerfSnapshot & {
57
+ /** The capture timed: `selfie`, or `document` and its side (`document front`). */
58
+ readonly capture: string;
59
+ /** How the attempt ended. */
60
+ readonly outcome: CapturePerfOutcome;
61
+ /** The log line: `[UseSmileID perf] <capture> <outcome> mark=ms …`. */
62
+ readonly line: string;
63
+ };
64
+ /**
65
+ * Times one capture screen: the marks it passes plus kamera's metrics. Pure: the
66
+ * screen feeds it and reads {@link CapturePerfTrace.summaryLine} back.
67
+ */
68
+ export declare class CapturePerfTrace {
69
+ private readonly _now;
70
+ private readonly _startedAt;
71
+ private readonly _marks;
72
+ private readonly _analysisMs;
73
+ private readonly _waitMs;
74
+ private _timeToFirstFrameMs;
75
+ private _coldStart;
76
+ private _captureLatencyMs;
77
+ constructor(now?: () => number);
78
+ /** Records `mark` the first time it is reached; a repeat keeps the first. */
79
+ mark(mark: CapturePerfMark): void;
80
+ /** Folds in one kamera metric. */
81
+ onMetric(metric: KameraMetric): void;
82
+ snapshot(): CapturePerfSnapshot;
83
+ /** The attempt's report for `capture` ending with `outcome`, its log line included. */
84
+ report(capture: string, outcome: CapturePerfOutcome): CapturePerfReport;
85
+ /**
86
+ * One log line: `[UseSmileID perf] <label> mark=ms … analysis=p50/p95ms(n) …`, the marks in
87
+ * capture order and only those reached.
88
+ */
89
+ summaryLine(label: string): string;
90
+ }
@@ -0,0 +1 @@
1
+ "use strict";export const CapturePerfMark={screenShown:"screen_shown",cameraStartRequested:"camera_start_requested",cameraRunning:"camera_running",firstFrame:"first_frame",streamingStarted:"streaming_started",firstScanResult:"first_scan_result",captureComplete:"capture_complete",filesReady:"files_ready"};const u=[CapturePerfMark.screenShown,CapturePerfMark.cameraStartRequested,CapturePerfMark.cameraRunning,CapturePerfMark.firstFrame,CapturePerfMark.streamingStarted,CapturePerfMark.firstScanResult,CapturePerfMark.captureComplete,CapturePerfMark.filesReady];export function capturePerfOutcomeContinues(e){return e==="failed"||e==="retake"||e==="timeout"}export class CapturePerfTrace{_now;_startedAt;_marks={};_analysisMs=[];_waitMs=[];_timeToFirstFrameMs;_coldStart;_captureLatencyMs;constructor(t=m){this._now=t,this._startedAt=t(),this._marks[CapturePerfMark.screenShown]=0}mark(t){this._marks[t]===void 0&&(this._marks[t]=Math.max(0,Math.round(this._now()-this._startedAt)))}onMetric(t){switch(t.name){case"firstFrame":if(this._timeToFirstFrameMs!==void 0)return;this.mark(CapturePerfMark.firstFrame),this._timeToFirstFrameMs=t.timeToFirstFrameMs,this._coldStart=t.coldStart;break;case"frameAnalyzed":if(this._marks[CapturePerfMark.streamingStarted]===void 0||this._marks[CapturePerfMark.captureComplete]!==void 0)return;this._analysisMs.push(t.analysisMs),this._waitMs.push(t.waitMs);break;case"captureCompleted":this._captureLatencyMs=t.latencyMs;break}}snapshot(){return{marks:{...this._marks},...this._timeToFirstFrameMs!==void 0?{timeToFirstFrameMs:this._timeToFirstFrameMs}:{},...this._coldStart!==void 0?{coldStart:this._coldStart}:{},...this._analysisMs.length>0?{analysis:n(this._analysisMs)}:{},...this._waitMs.length>0?{wait:n(this._waitMs)}:{},...this._captureLatencyMs!==void 0?{captureLatencyMs:this._captureLatencyMs}:{}}}report(t,s){return{...this.snapshot(),capture:t,outcome:s,line:this.summaryLine(`${t} ${s}`)}}summaryLine(t){const s=this.snapshot(),a=u.filter(r=>s.marks[r]!==void 0).map(r=>`${r}=${s.marks[r]}ms`);return s.timeToFirstFrameMs!==void 0&&a.push(`kamera_first_frame=${s.timeToFirstFrameMs}ms${s.coldStart?"(cold)":""}`),s.analysis&&a.push(`analysis=${c(s.analysis)}`),s.wait&&a.push(`wait=${c(s.wait)}`),s.captureLatencyMs!==void 0&&a.push(`still=${s.captureLatencyMs}ms`),`[UseSmileID perf] ${t} ${a.join(" ")}`}}function m(){const e=globalThis.performance;return e?e.now():Date.now()}function n(e){const t=[...e].sort((s,a)=>s-a);return{count:t.length,p50Ms:i(t,.5),p95Ms:i(t,.95)}}function i(e,t){const s=Math.ceil(t*e.length);return e[Math.min(e.length,Math.max(1,s))-1]??0}function c(e){return`${Math.round(e.p50Ms)}/${Math.round(e.p95Ms)}ms(${e.count})`}
@@ -1 +1 @@
1
- "use strict";import{LogLevel as u}from"../../../core/models/network_config";import{ALWAYS_REDACT as i,ALWAYS_REDACT_BODY_KEYS as p}from"./sensitive_headers";export function attachLoggingInterceptor(e,t){if(!t.enabled||t.level===u.none){const o=e.interceptors.request.use(l=>l),a=e.interceptors.response.use(l=>l);return{request:o,response:a}}const s=new Set(t.redactHeaders.map(o=>o.toLowerCase())),r=e.interceptors.request.use(o=>(m(o,t.level,s),o)),n=e.interceptors.response.use(o=>(b(o,t.level),o),o=>(y(o,t.level),Promise.reject(o)));return{request:r,response:n}}const d=/^([a-z][a-z\d+\-.]*:)?\/\//i;function f(e){const t=e.baseURL??"",s=e.url??"";return!t||d.test(s)?s:s?`${t.replace(/\/+$/,"")}/${s.replace(/^\/+/,"")}`:t}function m(e,t,s){const r=(e.method??"GET").toUpperCase(),n=f(e);if(console.log(`[UseSmileID] \u2192 ${r} ${n}`),t===u.headers||t===u.body){const o=D(e.headers,s);console.log("[UseSmileID] headers:",o)}}function b(e,t){console.log(`[UseSmileID] \u2190 ${e.status} ${e.statusText} ${e.config.url??""}`),t===u.body&&console.log("[UseSmileID] body:",c(e.data))}function y(e,t){const s=e.response?.status??"no-response";console.warn(`[UseSmileID] \u2717 ${s} ${e.config?.url??""} \u2014 ${e.message}`),t===u.body&&e.response&&console.warn("[UseSmileID] body:",c(e.response.data))}const A=6;function c(e,t=0){if(t>=A)return"<depth-limit>";if(Array.isArray(e))return e.map(s=>c(s,t+1));if(e!==null&&typeof e=="object"){const s={};for(const[r,n]of Object.entries(e))s[r]=p.has(r.toLowerCase())?"<redacted>":c(n,t+1);return s}return e}function D(e,t){const s={};for(const[r,n]of Object.entries(e??{})){const o=r.toLowerCase();i.has(o)||t.has(o)?s[r]="<redacted>":s[r]=n}return s}
1
+ "use strict";import{LogLevel as u}from"../../../core/models/network_config";import{ALWAYS_REDACT as d,ALWAYS_REDACT_BODY_KEYS as p}from"./sensitive_headers";import{isDevelopmentBuild as f}from"../../../core/is_development_build";export function attachLoggingInterceptor(e,t){if(!t.enabled||t.level===u.none){const r=e.interceptors.request.use(a=>a),i=e.interceptors.response.use(a=>a);return{request:r,response:i}}const s=new Set(t.redactHeaders.map(r=>r.toLowerCase())),o=t.level===u.body&&!f()?u.headers:t.level,n=e.interceptors.request.use(r=>(y(r,o,s),r)),c=e.interceptors.response.use(r=>(D(r,o),r),r=>(A(r,o),Promise.reject(r)));return{request:n,response:c}}const m=/^([a-z][a-z\d+\-.]*:)?\/\//i;function b(e){const t=e.baseURL??"",s=e.url??"";return!t||m.test(s)?s:s?`${t.replace(/\/+$/,"")}/${s.replace(/^\/+/,"")}`:t}function y(e,t,s){const o=(e.method??"GET").toUpperCase(),n=b(e);if(console.log(`[UseSmileID] \u2192 ${o} ${n}`),t===u.headers||t===u.body){const c=S(e.headers,s);console.log("[UseSmileID] headers:",c)}}function D(e,t){console.log(`[UseSmileID] \u2190 ${e.status} ${e.statusText} ${e.config.url??""}`),t===u.body&&console.log("[UseSmileID] body:",l(e.data))}function A(e,t){const s=e.response?.status??"no-response";console.warn(`[UseSmileID] \u2717 ${s} ${e.config?.url??""} \u2014 ${e.message}`),t===u.body&&e.response&&console.warn("[UseSmileID] body:",l(e.response.data))}const L=6;function l(e,t=0){if(t>=L)return"<depth-limit>";if(Array.isArray(e))return e.map(s=>l(s,t+1));if(e!==null&&typeof e=="object"){const s={};for(const[o,n]of Object.entries(e))s[o]=p.has(o.toLowerCase())?"<redacted>":l(n,t+1);return s}return e}function S(e,t){const s={};for(const[o,n]of Object.entries(e??{})){const c=o.toLowerCase();d.has(c)||t.has(c)?s[o]="<redacted>":s[o]=n}return s}
@@ -0,0 +1,16 @@
1
+ import type { AxiosAdapter, InternalAxiosRequestConfig } from "axios";
2
+ /**
3
+ * The request the capture client stopped: its config as it would have gone to
4
+ * the wire, after every request interceptor (auth, request-MAC, integrity) ran.
5
+ */
6
+ export declare class CapturedRequest extends Error {
7
+ readonly config: InternalAxiosRequestConfig;
8
+ constructor(config: InternalAxiosRequestConfig);
9
+ }
10
+ /**
11
+ * The capture client's adapter: axios calls it once the request interceptors
12
+ * have run, so it rejects with the signed request instead of sending it. A
13
+ * skipped submission seals its request through this client, so the request it
14
+ * seals is signed exactly as a submission is and never leaves the device.
15
+ */
16
+ export declare const captureAdapter: AxiosAdapter;
@@ -0,0 +1 @@
1
+ "use strict";export class CapturedRequest extends Error{config;constructor(t){super("The request was captured for sealing, not sent"),this.name="CapturedRequest",this.config=t}}export const captureAdapter=e=>Promise.reject(new CapturedRequest(e));
@@ -66,7 +66,7 @@ export declare class SmileIDAPI {
66
66
  *
67
67
  * `expectedLivenessCount` is the expected number of liveness frames
68
68
  * for this submission — derived from the decoded `sdk_config` claim
69
- * when present, otherwise the `ScanPerformanceProfile` default (7).
69
+ * when present, otherwise the native default (7).
70
70
  */
71
71
  private _buildSelfieFormData;
72
72
  /**
@@ -45,7 +45,9 @@ import type { MetadataProvider } from "../../metadata/use_smile_id_metadata_fact
45
45
  * the request-path order so the LIFO unwind lands on the canonical
46
46
  * order described above.
47
47
  */
48
- export declare function makeNetworkingFactory(config: NetworkConfig, signingSecret?: () => string): {
48
+ export declare function makeNetworkingFactory(config: NetworkConfig, signingSecret?: () => string, options?: {
49
+ readonly capturing?: boolean;
50
+ }): {
49
51
  readonly api: SmileIDAPI;
50
52
  readonly client: AxiosInstance;
51
53
  readonly metadata: MetadataProvider;