@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
@@ -1 +1 @@
1
- "use strict";import{SmileIDAPI as u,buildAxiosClient as g,SmileAuthInterceptor as I,attachLoggingInterceptor as k}from"../api/smile_id_api";import{createGzipInterceptor as d}from"../api/interceptors/gzip_interceptor";import{SmileRequestMacInterceptor as y}from"../api/interceptors/smile_request_mac_interceptor";import{SmileIntegrityInterceptor as w}from"../api/interceptors/smile_integrity_interceptor";import{TokenRefreshInterceptor as C}from"../api/interceptors/token_refresh_interceptor";import{TokenHolder as S}from"../api/auth/token_holder";import{SmileIDCryptoManager as A}from"../security/smile_id_crypto_manager";import{SecurityPolicy as P}from"../security/security_policy";import{decodeSecurityPolicyBitCode as F}from"../util/use_smile_id_token_decoder";import{Attestation as T}from"@smileid/usesmileid_bridge";import{SigningSecretProvider as b}from"@smileid/usesmileid_bridge/internal";import{MissingPartnerConfigException as n}from"../../core/exception";import{UseSmileIDMetadataFactory as x}from"../../metadata/use_smile_id_metadata_factory";export function makeNetworkingFactory(t,o=()=>b.signingSecret()){const r=t.config?.partnerConfig;if(!r||r.partnerId.trim()==="")throw n.missingPartnerId();if(!t.config?.token||t.config.token.trim()==="")throw n.missingAuthToken();const c=/[\u0000-\u001F\u007F-\u009F]/;if(c.test(r.partnerId))throw n.controlCharacterPartnerId();if(c.test(t.config.token))throw n.controlCharacterAuthToken();if(!isResolvableCallbackUrl(r.callbackUrl??""))throw n.unresolvableCallbackUrl();const e=g(t),m=new x,f=new u(e,r);T.warmUp().catch(()=>{});const a=new S(t.config?.token??""),s=P.fromBitCode(F(t.config?.token??""));d().attach(e);const p=t.config?.logging;p&&k(e,p),new w({securityPolicy:s}).attach(e);const l=new A(o());if(new y({cryptoManager:l,securityPolicy:s}).attach(e),new I({partnerConfig:r,tokenHolder:a}).attach(e),new C({tokenHolder:a,onTokenExpired:t.config?.onTokenExpired}).attach(e),t.interceptors)for(const h of t.interceptors){const i=h;typeof i=="function"?i(e):typeof i?.attach=="function"&&i.attach(e)}return{api:f,client:e,metadata:m}}const M=/^callback_[0-9a-hjkmnp-tv-z]{26}$/;export function isResolvableCallbackUrl(t){if(t===""||M.test(t))return!0;const o=t.indexOf("://");if(o<=0)return!1;const r=t.slice(0,o).toLowerCase();if(r!=="http"&&r!=="https")return!1;const e=((t.slice(o+3).split(/[/?#]/)[0]??"").split("@").pop()??"").split(":")[0]??"";return e!==""&&!/[\s\u0000-\u001F\u007F-\u009F]/.test(e)}
1
+ "use strict";import{SmileIDAPI as I,buildAxiosClient as d,SmileAuthInterceptor as k,attachLoggingInterceptor as y}from"../api/smile_id_api";import{createGzipInterceptor as w}from"../api/interceptors/gzip_interceptor";import{captureAdapter as C}from"../api/request_capture";import{SmileRequestMacInterceptor as S}from"../api/interceptors/smile_request_mac_interceptor";import{SmileIntegrityInterceptor as A}from"../api/interceptors/smile_integrity_interceptor";import{TokenRefreshInterceptor as P}from"../api/interceptors/token_refresh_interceptor";import{TokenHolder as F}from"../api/auth/token_holder";import{SmileIDCryptoManager as T}from"../security/smile_id_crypto_manager";import{SecurityPolicy as b}from"../security/security_policy";import{decodeSecurityPolicyBitCode as x}from"../util/use_smile_id_token_decoder";import{Attestation as M}from"@smileid/usesmileid_bridge";import{SigningSecretProvider as R}from"@smileid/usesmileid_bridge/internal";import{MissingPartnerConfigException as c}from"../../core/exception";import{UseSmileIDMetadataFactory as E}from"../../metadata/use_smile_id_metadata_factory";export function makeNetworkingFactory(t,o=()=>R.signingSecret(),a={}){const e=t.config?.partnerConfig;if(!e||e.partnerId.trim()==="")throw c.missingPartnerId();if(!t.config?.token||t.config.token.trim()==="")throw c.missingAuthToken();const n=/[\u0000-\u001F\u007F-\u009F]/;if(n.test(e.partnerId))throw c.controlCharacterPartnerId();if(n.test(t.config.token))throw c.controlCharacterAuthToken();if(!isResolvableCallbackUrl(e.callbackUrl??""))throw c.unresolvableCallbackUrl();const i=a.capturing===!0,r=d(t);i&&(r.defaults.adapter=C);const u=new E,h=new I(r,e);i||M.warmUp().catch(()=>{});const p=new F(t.config?.token??""),f=b.fromBitCode(x(t.config?.token??""));i||w().attach(r);const m=t.config?.logging;m&&!i&&y(r,m),new A({securityPolicy:f}).attach(r);const g=new T(o());if(new S({cryptoManager:g,securityPolicy:f}).attach(r),new k({partnerConfig:e,tokenHolder:p}).attach(r),new P({tokenHolder:p,onTokenExpired:t.config?.onTokenExpired}).attach(r),t.interceptors&&!i)for(const l of t.interceptors){const s=l;typeof s=="function"?s(r):typeof s?.attach=="function"&&s.attach(r)}return{api:h,client:r,metadata:u}}const U=/^callback_[0-9a-hjkmnp-tv-z]{26}$/;export function isResolvableCallbackUrl(t){if(t===""||U.test(t))return!0;const o=t.indexOf("://");if(o<=0)return!1;const a=t.slice(0,o).toLowerCase();if(a!=="http"&&a!=="https")return!1;const n=((t.slice(o+3).split(/[/?#]/)[0]??"").split("@").pop()??"").split(":")[0]??"";return n!==""&&!/[\s\u0000-\u001F\u007F-\u009F]/.test(n)}
@@ -0,0 +1,49 @@
1
+ import type { JobRequest } from "./job_request";
2
+ import type { SealedJobRequest } from "./sealed_job_request";
3
+ /**
4
+ * Everything the flow captured for a job that was handed back instead of
5
+ * submitted (`c.skipApiSubmission = true`). Forward it to Smile ID from your
6
+ * own backend: send the job type's `/v3` request with `headers` as request
7
+ * headers, the images as their usual form parts, and `encryptedPackage`,
8
+ * `packageNonce` and `packageTag` as the `encrypted_package`, `package_nonce`
9
+ * and `package_tag` form fields.
10
+ *
11
+ * Everything else the SDK would have sent (the consent record, the metadata,
12
+ * the signature and the other `smileid-*` headers) travels inside
13
+ * `encryptedPackage`, encrypted for Smile ID: forward it as is, since the
14
+ * server rejects a package that was changed. The image files must
15
+ * reach the server byte-for-byte too, since they carry an integrity seal that
16
+ * a re-encode invalidates. The SDK deletes neither the files nor
17
+ * `sessionDirectory` in this mode: once the job has been forwarded, delete
18
+ * every file it lists, then `sessionDirectory` and everything left in it.
19
+ * `sessionDirectory` is the SDK's session folder holding every listed file,
20
+ * and absent when they don't share one (the capture sessions can store them
21
+ * apart), the folder isn't one of the SDK's own, or the job captured no files
22
+ * (Enhanced KYC). A document imported from the gallery or taken with the manual
23
+ * shutter lives in the app's cache, which the OS may clear under storage
24
+ * pressure, so forward such a job promptly.
25
+ */
26
+ export type CapturedJob = {
27
+ readonly jobType: JobRequest["jobType"];
28
+ readonly sessionId: string;
29
+ readonly sessionDirectory?: string;
30
+ readonly selfieImage?: string;
31
+ readonly livenessImages: readonly string[];
32
+ readonly frontDocument?: string;
33
+ readonly backDocument?: string;
34
+ /** The visa page of a Residency Document Verification job, sent as the `visa` part. */
35
+ readonly visaDocument?: string;
36
+ /** `smileid-token`, `smileid-source-sdk` and `smileid-source-sdk-version`, to send as they are. */
37
+ readonly headers: Readonly<Record<string, string>>;
38
+ /** The sealed request, hex-encoded; the `encrypted_package` form field. */
39
+ readonly encryptedPackage: string;
40
+ /** The package's AES-GCM nonce, hex-encoded; the `package_nonce` form field. */
41
+ readonly packageNonce: string;
42
+ /** The package's AES-GCM authentication tag, hex-encoded; the `package_tag` form field. */
43
+ readonly packageTag: string;
44
+ };
45
+ /**
46
+ * The partner-facing hand-over of `request`: the files its legs produced and
47
+ * the `sealed` request, with nothing the partner supplied echoed back.
48
+ */
49
+ export declare function capturedJobFrom(request: JobRequest, sessionId: string, sealed: SealedJobRequest): CapturedJob;
@@ -0,0 +1 @@
1
+ "use strict";import{JobType as t}from"../../core/models/job_type";export function capturedJobFrom(e,a,c){const n=m(e),i=r([n.selfieImage,...n.livenessImages,n.frontDocument,n.backDocument,n.visaDocument]);return Object.freeze({jobType:e.jobType,sessionId:a,...i!==void 0?{sessionDirectory:i}:{},...n.selfieImage!==void 0?{selfieImage:n.selfieImage}:{},livenessImages:Object.freeze([...n.livenessImages]),...n.frontDocument!==void 0?{frontDocument:n.frontDocument}:{},...n.backDocument!==void 0?{backDocument:n.backDocument}:{},...n.visaDocument!==void 0?{visaDocument:n.visaDocument}:{},headers:c.headers,encryptedPackage:c.encryptedPackage,packageNonce:c.packageNonce,packageTag:c.packageTag})}function m(e){switch(e.jobType){case t.smartSelfieEnrollment:case t.smartSelfieAuthentication:case t.biometricKyc:return{selfieImage:e.selfieImage,livenessImages:e.livenessImages};case t.documentVerification:case t.enhancedDocumentVerification:return{selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,...e.backDocument!==void 0?{backDocument:e.backDocument}:{}};case t.residencyDocumentVerification:return{selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,visaDocument:e.visaDocument};case t.enhancedKyc:case t.bvn:return{livenessImages:[]}}}function o(e){const a=e.lastIndexOf("/");if(a<=0)return;const c=e.slice(0,a);return/^[a-z][a-z0-9+.-]*:\/*$/i.test(c)?void 0:c}const f="UseSmileID";function r(e){const a=new Set(e.filter(s=>s!==void 0).map(o));if(a.size!==1)return;const[c]=a;if(c===void 0)return;const n=o(c);return n?.slice(n.lastIndexOf("/")+1)===f?c:void 0}
@@ -48,7 +48,7 @@ export type SmartSelfieEnrollmentRequest = BaseJobRequest & {
48
48
  * from the `sdk_config` JWT claim's `livenessImageCount` when
49
49
  * present (`livenessImageCount` maps to `numSamples = count + 1`,
50
50
  * so `expectedLivenessImageCount = count`); otherwise the
51
- * `ScanPerformanceProfile` default minus one (`numSamples - 1`).
51
+ * native default (`FaceCaptureDefaults.numSamples`) minus one (`numSamples - 1`).
52
52
  * For Enhanced Smart Selfie this is always
53
53
  * `EnhancedSmartSelfieConfig.EXPECTED_LIVENESS_IMAGE_COUNT` (7).
54
54
  *
@@ -0,0 +1,15 @@
1
+ /**
2
+ * A job request signed and sealed for the partner to forward
3
+ * (`c.skipApiSubmission = true`), produced by `JobPackageSealer`. The images
4
+ * travel beside it, unencrypted.
5
+ *
6
+ * `headers` go on the forwarded request as they are; `encryptedPackage`,
7
+ * `packageNonce` and `packageTag` are hex strings sent as the
8
+ * `encrypted_package`, `package_nonce` and `package_tag` form fields.
9
+ */
10
+ export type SealedJobRequest = {
11
+ readonly headers: Readonly<Record<string, string>>;
12
+ readonly encryptedPackage: string;
13
+ readonly packageNonce: string;
14
+ readonly packageTag: string;
15
+ };
@@ -0,0 +1 @@
1
+ "use strict";export{};
@@ -1,6 +1,7 @@
1
1
  import type { SdkConfig } from "../model/sdk_config";
2
2
  import type { JobRequest } from "../model/job_request";
3
3
  import type { JobSubmissionResponse } from "../model/job_submission_response";
4
+ import type { SealedJobRequest } from "../model/sealed_job_request";
4
5
  import type { UseSmileIDResult } from "../../core/models/result";
5
6
  /**
6
7
  * Repository abstraction over `SmileIDAPI` + retry policy.
@@ -16,4 +17,10 @@ export interface UseSmileIDRepository {
16
17
  * discriminator drives dispatch to the matching API method.
17
18
  */
18
19
  submit(request: JobRequest, sdkConfig?: SdkConfig | undefined): Promise<UseSmileIDResult<JobSubmissionResponse>>;
20
+ /**
21
+ * Build and sign the request `submit` would send for `request` without
22
+ * sending it, and seal it into the package a skipped submission hands the
23
+ * partner to forward (`c.skipApiSubmission`).
24
+ */
25
+ seal(request: JobRequest, sdkConfig?: SdkConfig | undefined): Promise<UseSmileIDResult<SealedJobRequest>>;
19
26
  }
@@ -4,6 +4,13 @@ import type { SmileIDAPI } from "../api/smile_id_api";
4
4
  import type { RetryConfig } from "../../core/models/network_config";
5
5
  import type { UseSmileIDResult } from "../../core/models/result";
6
6
  import type { SdkConfig } from "../model/sdk_config";
7
+ import type { SealedJobRequest } from "../model/sealed_job_request";
8
+ import type { JobPackageSealer } from "../security/job_package_sealer";
9
+ /** What a skipped submission seals its request with. */
10
+ export type CaptureDependencies = {
11
+ readonly api: SmileIDAPI;
12
+ readonly sealer: JobPackageSealer;
13
+ };
7
14
  /**
8
15
  * Concrete repository.
9
16
  *
@@ -15,15 +22,35 @@ import type { SdkConfig } from "../model/sdk_config";
15
22
  export declare class UseSmileIDRepositoryImpl {
16
23
  private readonly _api;
17
24
  private readonly _retry;
18
- constructor(api: SmileIDAPI, retry: RetryConfig);
25
+ private readonly _capture;
26
+ /**
27
+ * `capture` is what a skipped submission seals its request with: the capture
28
+ * client (`makeNetworkingFactory(config, secret, { capturing: true })`, the
29
+ * same interceptors stopped before the network) and the sealer.
30
+ */
31
+ constructor(api: SmileIDAPI, retry: RetryConfig, capture?: CaptureDependencies);
32
+ /**
33
+ * Sends `request` through the capture client, which runs the same
34
+ * interceptors as a submission and then stops the request
35
+ * (`CapturedRequest`) instead of sending it, so the sealed request is
36
+ * compressed and signed exactly as `submit`'s first attempt would be.
37
+ */
38
+ seal(request: JobRequest, sdkConfig?: SdkConfig | undefined): Promise<UseSmileIDResult<SealedJobRequest>>;
19
39
  submit(request: JobRequest, sdkConfig?: SdkConfig | undefined): Promise<UseSmileIDResult<JobSubmissionResponse>>;
40
+ /**
41
+ * Pre-flight payload compression — best effort: shrink the captured JPEGs under the Lambda
42
+ * cap before transmission (runs once, outside the retry loop), but never block submission, so
43
+ * any compression failure ships the captured images as-is and lets the server validate. The
44
+ * per-image original + final compressed sizes are stamped onto the metadata for telemetry.
45
+ */
46
+ private _compressed;
20
47
  /**
21
48
  * Pre-flight payload compression entry — extracts the captured image URIs for the job type and
22
49
  * compresses the bundle under the transmission cap (metadata is measured, never modified).
23
50
  * No-op for job types that carry no captured images (Enhanced KYC / BVN).
24
51
  */
25
52
  private _compressPayload;
26
- private _dispatch;
53
+ private static _send;
27
54
  /**
28
55
  * Translate any caught error into the typed
29
56
  * [UseSmileIDNetworkException] hierarchy. Delegates to the canonical
@@ -1 +1 @@
1
- "use strict";import{failure as s,success as m}from"../../core/models/result";import{JobType as t}from"../../core/models/job_type";import{withRetry as l}from"../util/retry_util";import{InvalidFieldValueException as f}from"../../core/exception/job_validation_exceptions";import{UseSmileIDValidationException as p}from"../../core/exception/use_smile_id_validation_exception";import{mapAxiosError as d}from"../../core/exception/use_smile_id_network_exception";import{PayloadCompressionRunner as c}from"../compression/payload_compression_runner";function _(r){const e=[],i=(n,a)=>{a!==void 0&&a>0&&e.push({name:n,value:String(a)})};return i("selfie_original_size",r.selfieOriginalMb),i("selfie_final_compressed_size",r.selfieFinalMb),i("document_front_original_size",r.frontOriginalMb),i("document_front_final_compressed_size",r.frontFinalMb),i("document_back_original_size",r.backOriginalMb),i("document_back_final_compressed_size",r.backFinalMb),i("document_visa_original_size",r.visaOriginalMb),i("document_visa_final_compressed_size",r.visaFinalMb),e}export class UseSmileIDRepositoryImpl{_api;_retry;constructor(e,i){this._api=e,this._retry=i}async submit(e,i){if(e.jobType===t.bvn)return s(new f({fieldName:"jobType",reason:"BVN submission is not yet supported"}));let n=e;try{const a=await this._compressPayload(e,i),o=a?_(a):[];o.length>0&&(n={...e,metadata:e.metadata.withAdditionalEntries(o)})}catch{}try{const a=await l(this._retry,o=>this._dispatch(n,o));return m(a)}catch(a){return a instanceof p?s(a):s(this._mapError(a))}}async _compressPayload(e,i){const n=JSON.stringify(e.metadata.toEntries()).length;switch(e.jobType){case t.smartSelfieEnrollment:case t.smartSelfieAuthentication:case t.biometricKyc:return c.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,metadataBytes:n,sdkConfig:i});case t.documentVerification:case t.enhancedDocumentVerification:return c.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,backDocument:e.backDocument,metadataBytes:n,sdkConfig:i});case t.residencyDocumentVerification:return c.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,visaDocument:e.visaDocument,metadataBytes:n,sdkConfig:i});default:return}}_dispatch(e,i){const n=i>0?{...e,metadata:e.metadata.withAdditionalEntries([{name:"network_retries",value:String(i)}])}:e;switch(n.jobType){case t.smartSelfieEnrollment:return this._api.smartSelfieEnrollment(n);case t.smartSelfieAuthentication:return this._api.smartSelfieAuthentication(n);case t.enhancedKyc:return this._api.enhancedKyc(n);case t.biometricKyc:return this._api.biometricKyc(n);case t.documentVerification:return this._api.documentVerification(n);case t.enhancedDocumentVerification:return this._api.enhancedDocumentVerification(n);case t.residencyDocumentVerification:return this._api.residencyDocumentVerification(n)}}_mapError(e){return d(e)}}
1
+ "use strict";import{failure as a,success as m}from"../../core/models/result";import{JobType as r}from"../../core/models/job_type";import{withRetry as d}from"../util/retry_util";import{InvalidFieldValueException as l}from"../../core/exception/job_validation_exceptions";import{UseSmileIDValidationException as f}from"../../core/exception/use_smile_id_validation_exception";import{mapAxiosError as p}from"../../core/exception/use_smile_id_network_exception";import{PayloadCompressionRunner as c}from"../compression/payload_compression_runner";import{CapturedRequest as _}from"../api/request_capture";function h(s){const e=[],t=(i,n)=>{n!==void 0&&n>0&&e.push({name:i,value:String(n)})};return t("selfie_original_size",s.selfieOriginalMb),t("selfie_final_compressed_size",s.selfieFinalMb),t("document_front_original_size",s.frontOriginalMb),t("document_front_final_compressed_size",s.frontFinalMb),t("document_back_original_size",s.backOriginalMb),t("document_back_final_compressed_size",s.backFinalMb),t("document_visa_original_size",s.visaOriginalMb),t("document_visa_final_compressed_size",s.visaFinalMb),e}export class UseSmileIDRepositoryImpl{_api;_retry;_capture;constructor(e,t,i){this._api=e,this._retry=t,this._capture=i}async seal(e,t){if(e.jobType===r.bvn)return a(new l({fieldName:"jobType",reason:"BVN submission is not yet supported"}));const i=this._capture;if(!i)return a(new Error("No capture client to seal the request through"));const n=await this._compressed(e,t);try{return await UseSmileIDRepositoryImpl._send(i.api,n,0),a(new Error("The capture client sent the request instead of stopping it"))}catch(o){if(o instanceof _)try{return m(await i.sealer.seal(o.config))}catch(u){return a(u)}return o instanceof f?a(o):a(this._mapError(o))}}async submit(e,t){if(e.jobType===r.bvn)return a(new l({fieldName:"jobType",reason:"BVN submission is not yet supported"}));const i=await this._compressed(e,t);try{const n=await d(this._retry,o=>UseSmileIDRepositoryImpl._send(this._api,i,o));return m(n)}catch(n){return n instanceof f?a(n):a(this._mapError(n))}}async _compressed(e,t){try{const i=await this._compressPayload(e,t),n=i?h(i):[];if(n.length>0)return{...e,metadata:e.metadata.withAdditionalEntries(n)}}catch{}return e}async _compressPayload(e,t){const i=JSON.stringify(e.metadata.toEntries()).length;switch(e.jobType){case r.smartSelfieEnrollment:case r.smartSelfieAuthentication:case r.biometricKyc:return c.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,metadataBytes:i,sdkConfig:t});case r.documentVerification:case r.enhancedDocumentVerification:return c.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,backDocument:e.backDocument,metadataBytes:i,sdkConfig:t});case r.residencyDocumentVerification:return c.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,visaDocument:e.visaDocument,metadataBytes:i,sdkConfig:t});default:return}}static _send(e,t,i){const n=i>0?{...t,metadata:t.metadata.withAdditionalEntries([{name:"network_retries",value:String(i)}])}:t;switch(n.jobType){case r.smartSelfieEnrollment:return e.smartSelfieEnrollment(n);case r.smartSelfieAuthentication:return e.smartSelfieAuthentication(n);case r.enhancedKyc:return e.enhancedKyc(n);case r.biometricKyc:return e.biometricKyc(n);case r.documentVerification:return e.documentVerification(n);case r.enhancedDocumentVerification:return e.enhancedDocumentVerification(n);case r.residencyDocumentVerification:return e.residencyDocumentVerification(n)}}_mapError(e){return p(e)}}
@@ -0,0 +1,41 @@
1
+ /** GCM's standard 96-bit nonce, fresh for every package. */
2
+ export declare const JOB_PACKAGE_NONCE_BYTES = 12;
3
+ /** GCM's full 128-bit authentication tag. */
4
+ export declare const JOB_PACKAGE_TAG_BYTES = 16;
5
+ /** AES-256's key length, which the secret's UTF-8 bytes must have. */
6
+ export declare const JOB_PACKAGE_KEY_BYTES = 32;
7
+ /** The encrypted bytes, and the nonce and tag the server needs to open them. */
8
+ export type SealedBytes = {
9
+ readonly ciphertext: Uint8Array;
10
+ readonly nonce: Uint8Array;
11
+ readonly tag: Uint8Array;
12
+ };
13
+ /**
14
+ * AES-256-GCM sealing for the job package a skipped submission hands the
15
+ * partner (`c.skipApiSubmission = true`). The server opens it with the same SDK
16
+ * encryption secret, and GCM's tag fails on any changed byte, so the partner
17
+ * forwards the package as is. The secret ships in the app, so this keeps the
18
+ * package from casual reading, not from someone who extracts it.
19
+ *
20
+ * The secret is the secret rotator's `sdk_encryption` for this SDK version:
21
+ * 32 characters since secret-rotator #27, whose UTF-8 bytes are the AES-256 key
22
+ * as they are. The server keys the cipher the same way
23
+ * (`Buffer.from(secret, 'utf8')` in lambda's `decrypt.ts`). Runs on the Web
24
+ * Crypto provider the request signing uses.
25
+ */
26
+ export declare const JobPackageCrypto: {
27
+ /**
28
+ * The AES key for `secret`: its UTF-8 bytes. Throws `SmileIDCryptoError`
29
+ * unless they are `JOB_PACKAGE_KEY_BYTES` long, as a secret bundled before
30
+ * secret-rotator #27 is not.
31
+ */
32
+ key(secret: string): Uint8Array;
33
+ /**
34
+ * Encrypts `plaintext` under `key` with `nonce`, a fresh random one unless a
35
+ * test pins it. Web Crypto appends the tag to the ciphertext; it is split off
36
+ * so the two travel separately.
37
+ */
38
+ seal(plaintext: Uint8Array, key: Uint8Array, nonce?: Uint8Array): Promise<SealedBytes>;
39
+ };
40
+ /** Lowercase hex, the encoding every field of the job package travels in. */
41
+ export declare function toHex(bytes: Uint8Array): string;
@@ -0,0 +1 @@
1
+ "use strict";import{SmileIDCryptoError as a}from"./smile_id_crypto_error";import{exactBuffer as c,requireSubtle as l,utf8Bytes as u}from"./signing_crypto";export const JOB_PACKAGE_NONCE_BYTES=12,JOB_PACKAGE_TAG_BYTES=16,JOB_PACKAGE_KEY_BYTES=32;function p(){try{const t=require("react-native-quick-crypto"),r=t.getRandomValues??t.default?.getRandomValues;if(r)return r}catch{}const e=globalThis.crypto;if(e?.getRandomValues)return e.getRandomValues.bind(e);throw new a("No secure random source available to seal the job package. Install the `react-native-quick-crypto` peer dependency, or expose a global `crypto`.")}export const JobPackageCrypto={key(e){const t=u(e);if(t.length!==JOB_PACKAGE_KEY_BYTES)throw new a("The SDK encryption secret is not the 32 characters AES-256 takes");return t},async seal(e,t,r=p()(new Uint8Array(JOB_PACKAGE_NONCE_BYTES))){const s=l();try{const o=await s.importKey("raw",c(t),"AES-GCM",!1,["encrypt"]),n=new Uint8Array(await s.encrypt({name:"AES-GCM",iv:c(r),tagLength:JOB_PACKAGE_TAG_BYTES*8},o,c(e))),i=n.length-JOB_PACKAGE_TAG_BYTES;return{ciphertext:n.slice(0,i),nonce:r.slice(),tag:n.slice(i)}}catch(o){throw o instanceof a?o:new a(`Sealing the job package failed: ${String(o)}`)}}};export function toHex(e){let t="";for(const r of e)t+=r.toString(16).padStart(2,"0");return t}
@@ -0,0 +1,35 @@
1
+ import type { InternalAxiosRequestConfig } from "axios";
2
+ import type { SealedJobRequest } from "../model/sealed_job_request";
3
+ /**
4
+ * Seals a signed job request into the package a skipped submission hands the
5
+ * partner.
6
+ *
7
+ * `seal` takes the request exactly as the SDK would have sent it, after the
8
+ * auth, request-MAC and integrity interceptors ran, so the signing is the same
9
+ * as for a submission. Everything but the images is encrypted together as
10
+ *
11
+ * ```
12
+ * { "headers": { every smileid-* header, lowercase }, "params": { every non-file form field } }
13
+ * ```
14
+ *
15
+ * and the partner forwards the ciphertext, nonce and tag as the
16
+ * `encrypted_package`, `package_nonce` and `package_tag` form fields alongside
17
+ * the images. The server opens the package and merges it back into the request
18
+ * before anything downstream reads it, so `params` carries each field the way
19
+ * the server's form parser produces it: the JSON fields parsed, every other
20
+ * field its string.
21
+ *
22
+ * The partner sends the token and source SDK headers
23
+ * (`SealedJobRequest.headers`) in the clear: the server authorises the request
24
+ * with the token and picks the secret to open the package with by the source
25
+ * SDK and its version.
26
+ */
27
+ export declare class JobPackageSealer {
28
+ private readonly _encryptionSecret;
29
+ constructor(encryptionSecret: () => string);
30
+ /**
31
+ * Seals `config`. Throws `SmileIDCryptoError` in a build without the secret,
32
+ * or with one that is not 32 characters.
33
+ */
34
+ seal(config: InternalAxiosRequestConfig): Promise<SealedJobRequest>;
35
+ }
@@ -0,0 +1 @@
1
+ "use strict";import{JobPackageCrypto as f,toHex as o}from"./job_package_crypto";import{utf8Bytes as d}from"./signing_crypto";import{SmileIDCryptoError as u}from"./smile_id_crypto_error";const m="smileid-",S=new Set(["smileid-token","smileid-source-sdk","smileid-source-sdk-version"]),h=new Set(["partner_params","metadata","failure_reason","consent","user_details","payload","countries","aliases","id_types"]);function y(e){return typeof e=="object"&&e!==null&&typeof e.getParts=="function"}export class JobPackageSealer{_encryptionSecret;constructor(t){this._encryptionSecret=t}async seal(t){const r=this._encryptionSecret();if(r.trim()==="")throw new u("The build carries no SDK encryption secret; run ./secrets.sh");const i={};for(const[s,n]of Object.entries(t.headers?.toJSON?.()??t.headers??{})){const l=s.toLowerCase();l.startsWith(m)&&n!=null&&(i[l]=String(n))}const c=Object.fromEntries(Object.entries(i).sort(([s],[n])=>s.localeCompare(n))),p=JSON.stringify({headers:c,params:g(t.data)}),a=await f.seal(d(p),f.key(r));return Object.freeze({headers:Object.freeze(Object.fromEntries(Object.entries(c).filter(([s])=>S.has(s)))),encryptedPackage:o(a.ciphertext),packageNonce:o(a.nonce),packageTag:o(a.tag)})}}function g(e){const t={};if(!y(e))return t;for(const r of e.getParts())r.uri!=null||r.string===void 0||(t[r.fieldName]=h.has(r.fieldName)?O(r.string):r.string);return t}function O(e){try{return JSON.parse(e)}catch{return e}}
@@ -2,6 +2,40 @@
2
2
  export declare function utf8Bytes(input: string): Uint8Array;
3
3
  /** Standard-alphabet Base64 of [bytes] (no line wrap). */
4
4
  export declare function base64Encode(bytes: Uint8Array): string;
5
+ /**
6
+ * Minimal structural type for the Web Crypto `SubtleCrypto` surface this module
7
+ * uses. Declared locally because the workspace tsconfig omits the DOM lib.
8
+ */
9
+ export interface SubtleLike {
10
+ importKey(format: "raw", keyData: ArrayBuffer, algorithm: {
11
+ name: "HMAC";
12
+ hash: "SHA-256";
13
+ } | "PBKDF2" | "AES-GCM", extractable: boolean, keyUsages: readonly ("sign" | "deriveBits" | "encrypt")[]): Promise<unknown>;
14
+ encrypt(algorithm: {
15
+ name: "AES-GCM";
16
+ iv: ArrayBuffer;
17
+ tagLength: number;
18
+ }, key: unknown, data: ArrayBuffer): Promise<ArrayBuffer>;
19
+ sign(algorithm: "HMAC", key: unknown, data: ArrayBuffer): Promise<ArrayBuffer>;
20
+ deriveBits(algorithm: {
21
+ name: "PBKDF2";
22
+ salt: ArrayBuffer;
23
+ iterations: number;
24
+ hash: "SHA-256";
25
+ }, key: unknown, length: number): Promise<ArrayBuffer>;
26
+ }
27
+ /**
28
+ * The resolved Web Crypto provider, shared with the job-package sealing.
29
+ *
30
+ * @throws {SmileIDCryptoError} when no provider is available.
31
+ */
32
+ export declare function requireSubtle(): SubtleLike;
33
+ /**
34
+ * The exact bytes of [bytes] as a standalone `ArrayBuffer`. Reading `.buffer`
35
+ * directly is wrong when the view covers only part of a larger buffer (a
36
+ * `subarray` / `slice`), which would feed the surrounding bytes into PBKDF2/HMAC.
37
+ */
38
+ export declare function exactBuffer(bytes: Uint8Array): ArrayBuffer;
5
39
  /**
6
40
  * Low-level signing primitives: PBKDF2 key derivation and HMAC-SHA256. Stateless
7
41
  * and deterministic, so the server reproduces the same key and MAC from the same
@@ -1 +1 @@
1
- "use strict";import{SmileIDCryptoError as a}from"./smile_id_crypto_error";const f=32,h=2e5;export function utf8Bytes(t){return new TextEncoder().encode(t)}const c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";export function base64Encode(t){let n="";for(let e=0;e<t.length;e+=3){const r=t[e],o=e+1<t.length?t[e+1]:0,l=e+2<t.length?t[e+2]:0;n+=c[r>>2],n+=c[(r&3)<<4|o>>4],n+=e+1<t.length?c[(o&15)<<2|l>>6]:"=",n+=e+2<t.length?c[l&63]:"="}return n}let i;function p(){if(i)return i;const t=globalThis.crypto?.subtle;if(t)return i=t,i;try{const n=require("react-native-quick-crypto"),e=n.subtle??n.webcrypto?.subtle??n.default?.subtle??n.default?.webcrypto?.subtle;if(e)return i=e,i}catch{}return null}function u(){const t=p();if(!t)throw new a("No Web Crypto provider available for request signing. Install the `react-native-quick-crypto` peer dependency (with its peers `react-native-nitro-modules` and `react-native-quick-base64`), or expose a global `crypto.subtle`.");return t}function s(t){return t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)}export const SigningCrypto={async deriveKey(t,n){const e=u();try{const r=await e.importKey("raw",s(utf8Bytes(t)),"PBKDF2",!1,["deriveBits"]),o=await e.deriveBits({name:"PBKDF2",salt:s(n),iterations:h,hash:"SHA-256"},r,f*8);return new Uint8Array(o)}catch(r){throw r instanceof a?r:new a(`Key derivation failed: ${String(r)}`)}},async hmacSha256(t,n){const e=u();try{const r=await e.importKey("raw",s(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await e.sign("HMAC",r,s(n));return new Uint8Array(o)}catch(r){throw r instanceof a?r:new a(`MAC computation failed: ${String(r)}`)}}};
1
+ "use strict";import{SmileIDCryptoError as a}from"./smile_id_crypto_error";const l=32,u=2e5;export function utf8Bytes(t){return new TextEncoder().encode(t)}const c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";export function base64Encode(t){let r="";for(let e=0;e<t.length;e+=3){const n=t[e],o=e+1<t.length?t[e+1]:0,s=e+2<t.length?t[e+2]:0;r+=c[n>>2],r+=c[(n&3)<<4|o>>4],r+=e+1<t.length?c[(o&15)<<2|s>>6]:"=",r+=e+2<t.length?c[s&63]:"="}return r}let i;function f(){if(i)return i;const t=globalThis.crypto?.subtle;if(t)return i=t,i;try{const r=require("react-native-quick-crypto"),e=r.subtle??r.webcrypto?.subtle??r.default?.subtle??r.default?.webcrypto?.subtle;if(e)return i=e,i}catch{}return null}export function requireSubtle(){const t=f();if(!t)throw new a("No Web Crypto provider available for request signing. Install the `react-native-quick-crypto` peer dependency (with its peers `react-native-nitro-modules` and `react-native-quick-base64`), or expose a global `crypto.subtle`.");return t}export function exactBuffer(t){return t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)}export const SigningCrypto={async deriveKey(t,r){const e=requireSubtle();try{const n=await e.importKey("raw",exactBuffer(utf8Bytes(t)),"PBKDF2",!1,["deriveBits"]),o=await e.deriveBits({name:"PBKDF2",salt:exactBuffer(r),iterations:u,hash:"SHA-256"},n,l*8);return new Uint8Array(o)}catch(n){throw n instanceof a?n:new a(`Key derivation failed: ${String(n)}`)}},async hmacSha256(t,r){const e=requireSubtle();try{const n=await e.importKey("raw",exactBuffer(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),o=await e.sign("HMAC",n,exactBuffer(r));return new Uint8Array(o)}catch(n){throw n instanceof a?n:new a(`MAC computation failed: ${String(n)}`)}}};
@@ -11,16 +11,19 @@ import { type RetryConfig } from "../../core/models/network_config";
11
11
  */
12
12
  export declare function withRetry<T>(config: RetryConfig, block: (attempt: number) => Promise<T>): Promise<T>;
13
13
  /**
14
- * Network-class failure check. Returns `true` for axios errors that
15
- * indicate transient transport problems (timeout, 5xx, 408, 429) and
16
- * `false` for application errors the caller must handle (400, 401,
17
- * 403, …) or for non-axios errors (programming bugs).
14
+ * Network-class failure check. Returns `true` for a 5xx, 408 or 429
15
+ * response, and for any failure with none but a cancelled request: React
16
+ * Native's XHR adapter reports a connection failure, a failed TLS handshake
17
+ * and a redirect loop alike as `ERR_NETWORK`, so they can't be told apart.
18
+ * Other 4xx responses and non-axios errors (programming bugs) are not retried.
18
19
  */
19
20
  export declare function isRetryable(error: unknown): boolean;
20
21
  /**
21
22
  * Compute the delay (in milliseconds) before the next retry attempt.
22
23
  * `attempt` is 0-indexed (0 = first retry, after the initial call
23
24
  * failed). Config delays are in seconds; the return value is converted
24
- * to ms so it can drive `setTimeout` directly.
25
+ * to ms so it can drive `setTimeout` directly. Jitter moves the delay by
26
+ * up to `jitter` of itself either way (`jitterDraw` in [-1, 1], random by
27
+ * default), with jitter clamped to [0, 1] and never past `maxDelay`.
25
28
  */
26
- export declare function computeDelayMs(config: RetryConfig, attempt: number): number;
29
+ export declare function computeDelayMs(config: RetryConfig, attempt: number, jitterDraw?: number): number;
@@ -1 +1 @@
1
- "use strict";import{isAxiosError as i}from"axios";import{RetryStrategy as m}from"../../core/models/network_config";export async function withRetry(t,e){if(t.strategy===m.none||t.maxAttempts<=1)return e(0);let a;for(let r=0;r<t.maxAttempts;r++)try{return await e(r)}catch(s){if(a=s,!isRetryable(s)||r===t.maxAttempts-1)throw s;const n=computeDelayMs(t,r);await u(n)}throw a}export function isRetryable(t){if(!i(t))return!1;if(!t.response)return!0;const e=t.response.status;return e>=500||e===408||e===429}export function computeDelayMs(t,e){if(t.strategy==="none")return 0;if(t.strategy==="fixed")return t.fixedDelay*1e3;const a=t.maxDelay*1e3,r=t.initialDelay*1e3*Math.pow(t.multiplier,e),s=Math.min(r,a),n=(Math.random()*2-1)*t.jitter,o=s*(1+n);return Math.max(0,Math.min(a,Math.round(o)))}function u(t){return new Promise(e=>setTimeout(e,t))}
1
+ "use strict";import{isAxiosError as m}from"axios";import{CANCELED as u}from"../../core/exception/use_smile_id_network_exception";import{RetryStrategy as p}from"../../core/models/network_config";export async function withRetry(t,e){if(t.strategy===p.none||t.maxAttempts<=1)return e(0);let s;for(let r=0;r<t.maxAttempts;r++)try{return await e(r)}catch(a){if(s=a,!isRetryable(a)||r===t.maxAttempts-1)throw a;const n=computeDelayMs(t,r);await l(n)}throw s}export function isRetryable(t){if(!m(t)||t.code===u)return!1;if(!t.response)return!0;const e=t.response.status;return e>=500&&e<600||e===408||e===429}export function computeDelayMs(t,e,s=Math.random()*2-1){if(t.strategy==="none")return 0;if(t.strategy==="fixed")return t.fixedDelay*1e3;const r=t.maxDelay*1e3,a=t.initialDelay*1e3*Math.pow(t.multiplier,e),n=Math.min(a,r),i=s*Math.min(1,Math.max(0,t.jitter)),o=n*(1+i);return Math.max(0,Math.min(r,Math.round(o)))}function l(t){return new Promise(e=>setTimeout(e,t))}
@@ -1,3 +1,6 @@
1
+ import type { capturedOvalRegion } from "../presentation/components/helpers/captured_oval_region";
2
+ /** How the preview places the selfie in its oval. */
3
+ export type SelfiePreviewPlacement = ReturnType<typeof capturedOvalRegion>;
1
4
  /** A width × height in layout units (or image pixels). */
2
5
  export type DebugSize = {
3
6
  readonly width: number;
@@ -24,10 +27,10 @@ export type SelfiePreviewPanelGeometry = {
24
27
  /**
25
28
  * Pure geometry for the selfie preview-screen debug overlay.
26
29
  *
27
- * The preview screen draws the captured selfie at
28
- * `SelfieInteriorCrop.coverScale` into an oval-sized box and clips it to an
29
- * ellipse: the frame's interior covers the box, so the border ring never
30
- * reaches the user. These helpers compute where the full frame, the ellipse
30
+ * The preview screen draws the captured selfie into an oval-sized box through
31
+ * `capturedOvalRegion` and clips it to an ellipse: the region the capture oval
32
+ * showed fills the box, and the frame's interior always covers it, so the
33
+ * border ring never reaches the user. These helpers compute where the full frame, the ellipse
31
34
  * the user actually sees, and the interior crop land — in the oval box's
32
35
  * coordinate space for the in-place marking (`inPlace`) and in a fit-scaled
33
36
  * thumbnail's coordinate space for the full-frame panel (`panel`).
@@ -38,18 +41,15 @@ export declare const SelfiePreviewDebugGeometry: {
38
41
  readonly plannedBorderFraction: 0.15;
39
42
  /**
40
43
  * Maps the captured frame onto the preview screen the way the production
41
- * layout does — `SelfieInteriorCrop.coverScale` centred in the oval box: the
42
- * uniform scale at which the interior covers the box, centred. Returns `null`
43
- * when either size is degenerate.
44
+ * layout does, through `placement`. Returns `null` when either size is
45
+ * degenerate.
44
46
  */
45
- readonly inPlace: (imageSize: DebugSize, ovalRect: DebugRect) => SelfiePreviewInPlaceGeometry | null;
47
+ readonly inPlace: (imageSize: DebugSize, ovalRect: DebugRect, placement: SelfiePreviewPlacement) => SelfiePreviewInPlaceGeometry | null;
46
48
  /**
47
49
  * Lays the full frame out inside a panel the way contain scaling would
48
50
  * (uniform contain scale, centred) and maps the planned interior plus the
49
- * ellipse the preview screen shows — the centre window, shaped like the oval
50
- * box, that the interior crop leaves visible — into the same panel
51
- * coordinates.
52
- * Returns `null` when any size is degenerate.
51
+ * window the preview screen shows through `placement` into the same panel
52
+ * coordinates. Returns `null` when any size is degenerate.
53
53
  */
54
- readonly panel: (imageSize: DebugSize, ovalSize: DebugSize, panelSize: DebugSize) => SelfiePreviewPanelGeometry | null;
54
+ readonly panel: (imageSize: DebugSize, ovalSize: DebugSize, panelSize: DebugSize, placement: SelfiePreviewPlacement) => SelfiePreviewPanelGeometry | null;
55
55
  };
@@ -1 +1 @@
1
- "use strict";import{SelfieInteriorCrop as d}from"../presentation/components/helpers/selfie_interior_crop";const f=d.borderFraction;export const SelfiePreviewDebugGeometry={plannedBorderFraction:f,inPlace(t,h){if(e(t)||e(h))return null;const n=d.coverScale(t,h),r=c(u(h),{width:t.width*n,height:t.height*n});return{frameRect:r,plannedInteriorRect:l(r,w(t)*n)}},panel(t,h,n){if(e(t)||e(h)||e(n))return null;const r=Math.min(n.width/t.width,n.height/t.height),o=c({x:n.width/2,y:n.height/2},{width:t.width*r,height:t.height*r}),i=d.coverScale(t,h);return{frameRect:o,plannedInteriorRect:l(o,w(t)*r),visibleOvalRect:c(u(o),{width:h.width/i*r,height:h.height/i*r})}}};function w(t){return f*Math.min(t.width,t.height)}function u(t){return{x:t.left+t.width/2,y:t.top+t.height/2}}function c(t,h){return{left:t.x-h.width/2,top:t.y-h.height/2,width:h.width,height:h.height}}function l(t,h){return{left:t.left+h,top:t.top+h,width:t.width-2*h,height:t.height-2*h}}function e(t){return t.width<=0||t.height<=0}
1
+ "use strict";import{SelfieInteriorCrop as l}from"../presentation/components/helpers/selfie_interior_crop";const i=l.borderFraction;export const SelfiePreviewDebugGeometry={plannedBorderFraction:i,inPlace(t,h,o){if(e(t)||e(h))return null;const f=o.scale(t,h),n={width:t.width*f,height:t.height*f},d=o.offset(n,h),r={left:h.left+d.x,top:h.top+d.y,...n};return{frameRect:r,plannedInteriorRect:u(r,w(t)*f)}},panel(t,h,o,f){if(e(t)||e(h)||e(o))return null;const n=Math.min(o.width/t.width,o.height/t.height),d=s({x:o.width/2,y:o.height/2},{width:t.width*n,height:t.height*n}),r=f.scale(t,h),c=f.offset({width:t.width*r,height:t.height*r},h);return{frameRect:d,plannedInteriorRect:u(d,w(t)*n),visibleOvalRect:{left:d.left-c.x/r*n,top:d.top-c.y/r*n,width:h.width/r*n,height:h.height/r*n}}}};function w(t){return i*Math.min(t.width,t.height)}function s(t,h){return{left:t.x-h.width/2,top:t.y-h.height/2,width:h.width,height:h.height}}function u(t,h){return{left:t.left+h,top:t.top+h,width:t.width-2*h,height:t.height-2*h}}function e(t){return t.width<=0||t.height<=0}
@@ -1,3 +1,4 @@
1
+ import type { CapturePerfReport } from "../core/perf/capture_perf_trace";
1
2
  type Listener = () => void;
2
3
  /**
3
4
  * Debug hook state the SDK's screens subscribe to, so toggling a switch
@@ -12,6 +13,13 @@ export declare const UseSmileIDDebugHooks: {
12
13
  readonly isDocumentPreviewOverlayEnabled: () => boolean;
13
14
  /** Sets the flag and notifies subscribers; ungated, for tests and the public setter. */
14
15
  readonly setDocumentPreviewOverlayEnabled: (enabled: boolean) => void;
16
+ /** Whether each capture screen logs its perf trace. */
17
+ readonly isPerfTraceEnabled: () => boolean;
18
+ /** Sets the flag; read when a capture screen opens. */
19
+ readonly setPerfTraceEnabled: (enabled: boolean) => void;
20
+ /** Where each capture attempt's perf report also goes, beside the log; null for nowhere. */
21
+ readonly perfTraceListener: () => ((report: CapturePerfReport) => void) | null;
22
+ readonly setPerfTraceListener: (listener: ((report: CapturePerfReport) => void) | null) => void;
15
23
  readonly subscribe: (listener: Listener) => () => void;
16
24
  };
17
25
  /**
@@ -51,11 +59,26 @@ export declare const UseSmileIDDebug: {
51
59
  readonly setDocumentDebugOverlaysEnabled: (enabled: boolean) => void;
52
60
  /** Whether the document debug overlays are currently installed. */
53
61
  readonly isDocumentDebugOverlaysEnabled: () => boolean;
62
+ /**
63
+ * Logs each capture's timings, one line per capture screen through
64
+ * `console.log`: when the screen appeared, asked the camera to start, the
65
+ * camera ran, its first frame, the first analysis, the capture and its files
66
+ * on disk, plus the camera's analysis and still timings. Release builds too,
67
+ * since that is where timings mean something; it logs no images or personal
68
+ * data. Read when a capture screen opens.
69
+ */
70
+ readonly setPerfTraceEnabled: (enabled: boolean) => void;
71
+ /** Whether capture screens log their perf trace. */
72
+ readonly isPerfTraceEnabled: () => boolean;
73
+ /**
74
+ * Also hands each capture attempt's {@link CapturePerfReport} to `listener`: its marks and
75
+ * timings, typed, and its log line, for a release build, whose `console.log` reaches no device
76
+ * log, or to forward the timings to your own analytics. Null stops it.
77
+ */
78
+ readonly setPerfTraceListener: (listener: ((report: CapturePerfReport) => void) | null) => void;
54
79
  };
55
80
  /** Subscribes a component to the selfie preview overlay flag. */
56
81
  export declare function useSelfiePreviewOverlayEnabled(): boolean;
57
82
  /** Subscribes a component to the document overlays flag. */
58
83
  export declare function useDocumentPreviewOverlayEnabled(): boolean;
59
- /** Whether this is a dev build, where the debug overlays can switch on. @internal */
60
- export declare function isDevBuild(): boolean;
61
84
  export {};
@@ -1 +1 @@
1
- "use strict";import{useSyncExternalStore as t}from"react";let n=!1,i=!1;const r=new Set;export const UseSmileIDDebugHooks={isSelfiePreviewOverlayEnabled(){return n},setSelfiePreviewOverlayEnabled(e){n!==e&&(n=e,r.forEach(l=>l()))},isDocumentPreviewOverlayEnabled(){return i},setDocumentPreviewOverlayEnabled(e){i!==e&&(i=e,r.forEach(l=>l()))},subscribe(e){return r.add(e),()=>r.delete(e)}},UseSmileIDDebug={setSelfieDebugOverlaysEnabled(e){isDevBuild()&&UseSmileIDDebugHooks.setSelfiePreviewOverlayEnabled(e)},isSelfieDebugOverlaysEnabled(){return UseSmileIDDebugHooks.isSelfiePreviewOverlayEnabled()},setDocumentDebugOverlaysEnabled(e){isDevBuild()&&UseSmileIDDebugHooks.setDocumentPreviewOverlayEnabled(e)},isDocumentDebugOverlaysEnabled(){return UseSmileIDDebugHooks.isDocumentPreviewOverlayEnabled()}};export function useSelfiePreviewOverlayEnabled(){return t(UseSmileIDDebugHooks.subscribe,UseSmileIDDebugHooks.isSelfiePreviewOverlayEnabled,UseSmileIDDebugHooks.isSelfiePreviewOverlayEnabled)}export function useDocumentPreviewOverlayEnabled(){return t(UseSmileIDDebugHooks.subscribe,UseSmileIDDebugHooks.isDocumentPreviewOverlayEnabled,UseSmileIDDebugHooks.isDocumentPreviewOverlayEnabled)}export function isDevBuild(){return typeof __DEV__<"u"&&__DEV__}
1
+ "use strict";import{useSyncExternalStore as t}from"react";import{isDevelopmentBuild as i}from"../core/is_development_build";let n=!1,a=!1,s=!1,u=null;const r=new Set;export const UseSmileIDDebugHooks={isSelfiePreviewOverlayEnabled(){return n},setSelfiePreviewOverlayEnabled(e){n!==e&&(n=e,r.forEach(l=>l()))},isDocumentPreviewOverlayEnabled(){return a},setDocumentPreviewOverlayEnabled(e){a!==e&&(a=e,r.forEach(l=>l()))},isPerfTraceEnabled(){return s},setPerfTraceEnabled(e){s=e},perfTraceListener(){return u},setPerfTraceListener(e){u=e},subscribe(e){return r.add(e),()=>r.delete(e)}},UseSmileIDDebug={setSelfieDebugOverlaysEnabled(e){i()&&UseSmileIDDebugHooks.setSelfiePreviewOverlayEnabled(e)},isSelfieDebugOverlaysEnabled(){return UseSmileIDDebugHooks.isSelfiePreviewOverlayEnabled()},setDocumentDebugOverlaysEnabled(e){i()&&UseSmileIDDebugHooks.setDocumentPreviewOverlayEnabled(e)},isDocumentDebugOverlaysEnabled(){return UseSmileIDDebugHooks.isDocumentPreviewOverlayEnabled()},setPerfTraceEnabled(e){UseSmileIDDebugHooks.setPerfTraceEnabled(e)},isPerfTraceEnabled(){return UseSmileIDDebugHooks.isPerfTraceEnabled()},setPerfTraceListener(e){UseSmileIDDebugHooks.setPerfTraceListener(e)}};export function useSelfiePreviewOverlayEnabled(){return t(UseSmileIDDebugHooks.subscribe,UseSmileIDDebugHooks.isSelfiePreviewOverlayEnabled,UseSmileIDDebugHooks.isSelfiePreviewOverlayEnabled)}export function useDocumentPreviewOverlayEnabled(){return t(UseSmileIDDebugHooks.subscribe,UseSmileIDDebugHooks.isDocumentPreviewOverlayEnabled,UseSmileIDDebugHooks.isDocumentPreviewOverlayEnabled)}
@@ -1 +1 @@
1
- "use strict";import{jsx as i,jsxs as d}from"react/jsx-runtime";import{useId as v}from"react";import{Image as I,StyleSheet as n,View as o}from"react-native";import{useSafeAreaInsets as b}from"react-native-safe-area-context";import h,{ClipPath as S,Defs as y,Path as c,Rect as a}from"react-native-svg";import{DocumentPreviewDebugGeometry as s}from"./document_preview_debug_geometry";import{BORDER_RING_FILL as u,BOUNDARY_STROKE as m,HIDDEN_TODAY_DIM as P,PANEL_WIDTH as g,PlannedBoundary as _,VISIBLE_BOUNDARY_COLOR as f,debugPreviewStyles as w,rectPath as R,ringPath as D}from"./preview_debug_drawing";import{SelfiePreviewDebugHud as p}from"./use_smile_id_selfie_preview_debug_overlay";export function UseSmileIDDocumentPreviewDebugOverlay(t){const r=b();return i(o,{pointerEvents:"box-none",style:n.absoluteFill,testID:"si_debug_document_preview_overlay",children:i(o,{style:[w.hudAnchor,{top:r.top+8}],children:i(p,{title:"DOCUMENT",children:i(z,{...t})})})})}export function DocumentPreviewHiddenRegionsInPlace(t){const r=`si_debug_card_clip_${v().replace(/:/g,"")}`,l=s.inPlace(t.imageSize,t.cardSize,t.borderFraction);if(!l)return null;const e={left:0,top:0,width:t.cardSize.width,height:t.cardSize.height};return d(h,{width:"100%",height:"100%",style:n.absoluteFill,testID:"si_debug_document_preview_in_place",children:[i(y,{children:i(S,{id:r,children:i(a,{x:0,y:0,width:e.width,height:e.height})})}),i(c,{d:D(l.frameRect,l.plannedInteriorRect),fill:u,fillRule:"evenodd",clipPath:`url(#${r})`}),i(_,{rect:l.plannedInteriorRect}),i(a,{x:e.left,y:e.top,width:e.width,height:e.height,fill:"none",stroke:f,strokeWidth:m})]})}function z(t){const r=t.imageSize?t.imageSize.width/t.imageSize.height:x,l={width:g,height:g/r},e=t.imageSize?s.panel(t.imageSize,t.cardSize,l,t.borderFraction):null;return d(o,{style:[w.panel,l],testID:"si_debug_document_preview_panel",children:[i(I,{source:{uri:t.uri},resizeMode:"contain",style:n.absoluteFill}),e?d(h,{width:l.width,height:l.height,style:n.absoluteFill,testID:"si_debug_document_preview_panel_markings",children:[i(c,{d:`${R(e.frameRect)} ${R(e.visibleCardRect)}`,fill:P,fillRule:"evenodd"}),i(c,{d:D(e.frameRect,e.plannedInteriorRect),fill:u,fillRule:"evenodd"}),i(_,{rect:e.plannedInteriorRect}),i(a,{x:e.visibleCardRect.left,y:e.visibleCardRect.top,width:e.visibleCardRect.width,height:e.visibleCardRect.height,fill:"none",stroke:f,strokeWidth:m})]}):null]})}const x=1.586;
1
+ "use strict";import{jsx as i,jsxs as d}from"react/jsx-runtime";import{useId as I}from"react";import{Image as v,StyleSheet as n,View as o}from"react-native";import{useSafeAreaInsets as b}from"react-native-safe-area-context";import h,{ClipPath as S,Defs as y,Path as c,Rect as a}from"react-native-svg";import{DocumentPreviewDebugGeometry as s}from"./document_preview_debug_geometry";import{BORDER_RING_FILL as u,BOUNDARY_STROKE as m,HIDDEN_TODAY_DIM as P,PANEL_WIDTH as g,PlannedBoundary as _,VISIBLE_BOUNDARY_COLOR as f,debugPreviewStyles as w,rectPath as R,ringPath as D}from"./preview_debug_drawing";import{SelfiePreviewDebugHud as p}from"./use_smile_id_selfie_preview_debug_overlay";import{capturedImageUri as z}from"../presentation/components/helpers/captured_image_uri";export function UseSmileIDDocumentPreviewDebugOverlay(t){const r=b();return i(o,{pointerEvents:"box-none",style:n.absoluteFill,testID:"si_debug_document_preview_overlay",children:i(o,{style:[w.hudAnchor,{top:r.top+8}],children:i(p,{title:"DOCUMENT",children:i(x,{...t})})})})}export function DocumentPreviewHiddenRegionsInPlace(t){const r=`si_debug_card_clip_${I().replace(/:/g,"")}`,l=s.inPlace(t.imageSize,t.cardSize,t.borderFraction);if(!l)return null;const e={left:0,top:0,width:t.cardSize.width,height:t.cardSize.height};return d(h,{width:"100%",height:"100%",style:n.absoluteFill,testID:"si_debug_document_preview_in_place",children:[i(y,{children:i(S,{id:r,children:i(a,{x:0,y:0,width:e.width,height:e.height})})}),i(c,{d:D(l.frameRect,l.plannedInteriorRect),fill:u,fillRule:"evenodd",clipPath:`url(#${r})`}),i(_,{rect:l.plannedInteriorRect}),i(a,{x:e.left,y:e.top,width:e.width,height:e.height,fill:"none",stroke:f,strokeWidth:m})]})}function x(t){const r=t.imageSize?t.imageSize.width/t.imageSize.height:E,l={width:g,height:g/r},e=t.imageSize?s.panel(t.imageSize,t.cardSize,l,t.borderFraction):null;return d(o,{style:[w.panel,l],testID:"si_debug_document_preview_panel",children:[i(v,{source:{uri:z(t.uri)},resizeMode:"contain",style:n.absoluteFill}),e?d(h,{width:l.width,height:l.height,style:n.absoluteFill,testID:"si_debug_document_preview_panel_markings",children:[i(c,{d:`${R(e.frameRect)} ${R(e.visibleCardRect)}`,fill:P,fillRule:"evenodd"}),i(c,{d:D(e.frameRect,e.plannedInteriorRect),fill:u,fillRule:"evenodd"}),i(_,{rect:e.plannedInteriorRect}),i(a,{x:e.visibleCardRect.left,y:e.visibleCardRect.top,width:e.visibleCardRect.width,height:e.visibleCardRect.height,fill:"none",stroke:f,strokeWidth:m})]}):null]})}const E=1.586;
@@ -1,10 +1,12 @@
1
1
  import { type ReactNode } from "react";
2
- import { type DebugRect, type DebugSize } from "./selfie_preview_debug_geometry";
2
+ import { type DebugRect, type DebugSize, type SelfiePreviewPlacement } from "./selfie_preview_debug_geometry";
3
3
  export type UseSmileIDSelfiePreviewDebugOverlayProps = {
4
4
  /** Local file URI of the captured selfie the preview screen shows. */
5
5
  readonly uri: string;
6
6
  /** The laid-out oval box, in this overlay's coordinate space. */
7
7
  readonly ovalRect: DebugRect;
8
+ /** How the preview places the selfie in `ovalRect`. */
9
+ readonly placement: SelfiePreviewPlacement;
8
10
  };
9
11
  /**
10
12
  * Developer overlay for the post-capture selfie preview screen: shows which
@@ -38,6 +40,7 @@ export declare function UseSmileIDSelfiePreviewDebugOverlay(props: UseSmileIDSel
38
40
  export declare function SelfiePreviewHiddenRegionsInPlace(props: {
39
41
  readonly imageSize: DebugSize;
40
42
  readonly ovalRect: DebugRect;
43
+ readonly placement: SelfiePreviewPlacement;
41
44
  }): import("react").JSX.Element | null;
42
45
  /**
43
46
  * Markings over the full-frame panel: everything outside the user-visible
@@ -48,6 +51,7 @@ export declare function SelfiePreviewPanelMarkings(props: {
48
51
  readonly imageSize: DebugSize;
49
52
  readonly ovalSize: DebugSize;
50
53
  readonly panelSize: DebugSize;
54
+ readonly placement: SelfiePreviewPlacement;
51
55
  }): import("react").JSX.Element | null;
52
56
  /**
53
57
  * Collapsible heads-up display chrome: dark rounded background, the title
@@ -1 +1 @@
1
- "use strict";import{jsx as i,jsxs as a}from"react/jsx-runtime";import{useEffect as b,useId as w,useState as f}from"react";import{Image as D,Pressable as x,StyleSheet as d,Text as g,View as o}from"react-native";import{useSafeAreaInsets as p}from"react-native-safe-area-context";import m,{ClipPath as E,Defs as $,Ellipse as c,Path as h}from"react-native-svg";import{decodeUprightImageSize as A}from"../presentation/components/helpers/selfie_interior_crop";import{SelfiePreviewDebugGeometry as S}from"./selfie_preview_debug_geometry";import{BORDER_RING_FILL as _,BOUNDARY_STROKE as v,HIDDEN_TODAY_DIM as F,PANEL_WIDTH as R,PlannedBoundary as y,VISIBLE_BOUNDARY_COLOR as I,debugPreviewStyles as O,rectPath as T,ringPath as P}from"./preview_debug_drawing";export function UseSmileIDSelfiePreviewDebugOverlay(e){const t=p(),[l,n]=f(null);return b(()=>{let s=!0;return A(e.uri).then(z=>{s&&n(z)}),()=>{s=!1}},[e.uri]),a(o,{pointerEvents:"box-none",style:d.absoluteFill,testID:"si_debug_selfie_preview_overlay",children:[l?i(o,{pointerEvents:"none",style:d.absoluteFill,children:i(SelfiePreviewHiddenRegionsInPlace,{imageSize:l,ovalRect:e.ovalRect})}):null,i(o,{style:[r.hudAnchor,{top:t.top+8}],children:i(SelfiePreviewDebugHud,{title:"FRAME",children:i(L,{uri:e.uri,imageSize:l,ovalSize:e.ovalRect})})})]})}export function SelfiePreviewHiddenRegionsInPlace(e){const t=`si_debug_oval_clip_${w().replace(/:/g,"")}`,l=S.inPlace(e.imageSize,e.ovalRect);if(!l)return null;const n=e.ovalRect;return a(m,{width:"100%",height:"100%",testID:"si_debug_selfie_preview_in_place",children:[i($,{children:i(E,{id:t,children:i(c,{...u(n)})})}),i(h,{d:P(l.frameRect,l.plannedInteriorRect),fill:_,fillRule:"evenodd",clipPath:`url(#${t})`}),i(y,{rect:l.plannedInteriorRect}),i(c,{...u(n),fill:"none",stroke:I,strokeWidth:v})]})}export function SelfiePreviewPanelMarkings(e){const t=S.panel(e.imageSize,e.ovalSize,e.panelSize);if(!t)return null;const l=t.visibleOvalRect;return a(m,{width:e.panelSize.width,height:e.panelSize.height,style:d.absoluteFill,testID:"si_debug_selfie_preview_panel_markings",children:[i(h,{d:`${T(t.frameRect)} ${k(l)}`,fill:F,fillRule:"evenodd"}),i(h,{d:P(t.frameRect,t.plannedInteriorRect),fill:_,fillRule:"evenodd"}),i(y,{rect:t.plannedInteriorRect}),i(c,{...u(l),fill:"none",stroke:I,strokeWidth:v})]})}export function SelfiePreviewDebugHud(e){const[t,l]=f(!0);return a(o,{style:r.hud,children:[a(o,{style:r.hudHeader,children:[t?i(g,{style:r.hudText,children:e.title}):null,i(x,{accessibilityRole:"button",testID:"si_debug_hud_toggle",onPress:()=>l(n=>!n),hitSlop:8,style:r.hudChevron,children:i(g,{style:r.hudText,children:t?"\u25B2":"\u25BC"})})]}),t?e.children:null]})}function L(e){const t=e.imageSize?e.imageSize.width/e.imageSize.height:N,l={width:R,height:R/t};return a(o,{style:[r.panel,l],testID:"si_debug_selfie_preview_panel",children:[i(D,{source:{uri:e.uri},resizeMode:"contain",style:d.absoluteFill}),e.imageSize?i(SelfiePreviewPanelMarkings,{imageSize:e.imageSize,ovalSize:e.ovalSize,panelSize:l}):null]})}function u(e){return{cx:e.left+e.width/2,cy:e.top+e.height/2,rx:e.width/2,ry:e.height/2}}function k(e){const{cx:t,cy:l,rx:n,ry:s}=u(e);return`M${t-n},${l} a${n},${s} 0 1,0 ${2*n},0 a${n},${s} 0 1,0 ${-2*n},0 Z`}const N=3/4,r=O;
1
+ "use strict";import{jsx as i,jsxs as a}from"react/jsx-runtime";import{useEffect as b,useId as w,useState as m}from"react";import{Image as D,Pressable as x,StyleSheet as d,Text as f,View as o}from"react-native";import{useSafeAreaInsets as p}from"react-native-safe-area-context";import g,{ClipPath as E,Defs as $,Ellipse as u,Path as h}from"react-native-svg";import{decodeUprightImageSize as A}from"../presentation/components/helpers/selfie_interior_crop";import{SelfiePreviewDebugGeometry as S}from"./selfie_preview_debug_geometry";import{BORDER_RING_FILL as _,BOUNDARY_STROKE as v,HIDDEN_TODAY_DIM as F,PANEL_WIDTH as R,PlannedBoundary as y,VISIBLE_BOUNDARY_COLOR as I,debugPreviewStyles as O,rectPath as T,ringPath as P}from"./preview_debug_drawing";import{capturedImageUri as L}from"../presentation/components/helpers/captured_image_uri";export function UseSmileIDSelfiePreviewDebugOverlay(e){const t=p(),[l,n]=m(null);return b(()=>{let c=!0;return A(e.uri).then(z=>{c&&n(z)}),()=>{c=!1}},[e.uri]),a(o,{pointerEvents:"box-none",style:d.absoluteFill,testID:"si_debug_selfie_preview_overlay",children:[l?i(o,{pointerEvents:"none",style:d.absoluteFill,children:i(SelfiePreviewHiddenRegionsInPlace,{imageSize:l,ovalRect:e.ovalRect,placement:e.placement})}):null,i(o,{style:[r.hudAnchor,{top:t.top+8}],children:i(SelfiePreviewDebugHud,{title:"FRAME",children:i(k,{uri:e.uri,imageSize:l,ovalSize:e.ovalRect,placement:e.placement})})})]})}export function SelfiePreviewHiddenRegionsInPlace(e){const t=`si_debug_oval_clip_${w().replace(/:/g,"")}`,l=S.inPlace(e.imageSize,e.ovalRect,e.placement);if(!l)return null;const n=e.ovalRect;return a(g,{width:"100%",height:"100%",testID:"si_debug_selfie_preview_in_place",children:[i($,{children:i(E,{id:t,children:i(u,{...s(n)})})}),i(h,{d:P(l.frameRect,l.plannedInteriorRect),fill:_,fillRule:"evenodd",clipPath:`url(#${t})`}),i(y,{rect:l.plannedInteriorRect}),i(u,{...s(n),fill:"none",stroke:I,strokeWidth:v})]})}export function SelfiePreviewPanelMarkings(e){const t=S.panel(e.imageSize,e.ovalSize,e.panelSize,e.placement);if(!t)return null;const l=t.visibleOvalRect;return a(g,{width:e.panelSize.width,height:e.panelSize.height,style:d.absoluteFill,testID:"si_debug_selfie_preview_panel_markings",children:[i(h,{d:`${T(t.frameRect)} ${N(l)}`,fill:F,fillRule:"evenodd"}),i(h,{d:P(t.frameRect,t.plannedInteriorRect),fill:_,fillRule:"evenodd"}),i(y,{rect:t.plannedInteriorRect}),i(u,{...s(l),fill:"none",stroke:I,strokeWidth:v})]})}export function SelfiePreviewDebugHud(e){const[t,l]=m(!0);return a(o,{style:r.hud,children:[a(o,{style:r.hudHeader,children:[t?i(f,{style:r.hudText,children:e.title}):null,i(x,{accessibilityRole:"button",testID:"si_debug_hud_toggle",onPress:()=>l(n=>!n),hitSlop:8,style:r.hudChevron,children:i(f,{style:r.hudText,children:t?"\u25B2":"\u25BC"})})]}),t?e.children:null]})}function k(e){const t=e.imageSize?e.imageSize.width/e.imageSize.height:U,l={width:R,height:R/t};return a(o,{style:[r.panel,l],testID:"si_debug_selfie_preview_panel",children:[i(D,{source:{uri:L(e.uri)},resizeMode:"contain",style:d.absoluteFill}),e.imageSize?i(SelfiePreviewPanelMarkings,{imageSize:e.imageSize,ovalSize:e.ovalSize,placement:e.placement,panelSize:l}):null]})}function s(e){return{cx:e.left+e.width/2,cy:e.top+e.height/2,rx:e.width/2,ry:e.height/2}}function N(e){const{cx:t,cy:l,rx:n,ry:c}=s(e);return`M${t-n},${l} a${n},${c} 0 1,0 ${2*n},0 a${n},${c} 0 1,0 ${-2*n},0 Z`}const U=3/4,r=O;
package/dist/index.d.ts CHANGED
@@ -47,7 +47,8 @@ export { JobType, jobTypeFromValue, requiresDocument, requiresSelfie, type JobTy
47
47
  export { FlowStep } from "./core/models/flow_step";
48
48
  export { DocumentType, type DocumentOrientation, type GenericDocumentArgs, } from "./core/models/document_type";
49
49
  export { DEFAULT_ACTIVATE_MANUAL_AFTER_MS, DocumentCaptureMode, type AutoCaptureWithManualFallbackArgs, } from "./core/models/document_capture_mode";
50
- export { cancelled, failure, success, type UseSmileIDResult, } from "./core/models/result";
50
+ export { cancelled, captured, failure, success, type UseSmileIDResult, } from "./core/models/result";
51
+ export type { CapturedJob } from "./data/model/captured_job";
51
52
  export type { ConfigOptions } from "./core/models/config";
52
53
  export { GlobalPrivacyControl, GLOBAL_PRIVACY_CONTROL_WIRE_KEY, globalPrivacyControlFromWire, } from "./core/models/global_privacy_control";
53
54
  export { LogLevel } from "./core/models/network_config";
@@ -118,3 +119,5 @@ export { UseSmileIDProcessingScreen, type ProcessingState, type UseSmileIDProces
118
119
  export { ForceMaxBrightness } from "./presentation/utils/force_max_brightness";
119
120
  export type { ForceMaxBrightnessProps } from "./presentation/utils/force_max_brightness";
120
121
  export { UseSmileIDDebug } from "./debug/use_smile_id_debug";
122
+ export { CapturePerfMark } from "./core/perf/capture_perf_trace";
123
+ export type { CapturePerfOutcome, CapturePerfReport, CapturePerfStat, } from "./core/perf/capture_perf_trace";