@smileid/usesmileid 12.1.0 → 12.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/README.md +28 -17
  3. package/dist/camera/document_image_pipeline.d.ts +1 -1
  4. package/dist/camera/document_image_pipeline.js +1 -1
  5. package/dist/camera/use_smile_id_frame_stream.js +1 -1
  6. package/dist/camera/use_smile_id_kamera_camera_system.d.ts +6 -0
  7. package/dist/camera/use_smile_id_kamera_camera_system.js +1 -1
  8. package/dist/core/exception/invalid_job_type_screen_config_exception.js +1 -1
  9. package/dist/core/exception/missing_partner_config_exception.d.ts +2 -0
  10. package/dist/core/exception/missing_partner_config_exception.js +1 -1
  11. package/dist/core/exception/unused_job_params_exception.js +1 -1
  12. package/dist/core/exception/use_smile_id_error_code.d.ts +1 -0
  13. package/dist/core/exception/use_smile_id_error_code.js +1 -1
  14. package/dist/core/exception/use_smile_id_network_exception.d.ts +2 -1
  15. package/dist/core/exception/use_smile_id_network_exception.js +1 -1
  16. package/dist/core/models/document_type.d.ts +1 -1
  17. package/dist/core/models/job_type.d.ts +5 -0
  18. package/dist/core/models/job_type.js +1 -1
  19. package/dist/core/models/network_config.d.ts +2 -3
  20. package/dist/core/models/params.d.ts +18 -0
  21. package/dist/core/models/params.js +1 -1
  22. package/dist/data/api/interceptors/smile_auth_interceptor.js +1 -1
  23. package/dist/data/api/smile_id_api.d.ts +6 -12
  24. package/dist/data/api/smile_id_api.js +1 -1
  25. package/dist/data/compression/compressible_asset.d.ts +9 -1
  26. package/dist/data/compression/compressible_asset.js +1 -1
  27. package/dist/data/compression/payload_compression_runner.d.ts +3 -0
  28. package/dist/data/compression/payload_compression_runner.js +1 -1
  29. package/dist/data/compression/payload_compressor.js +1 -1
  30. package/dist/data/factory/networking_factory.d.ts +2 -0
  31. package/dist/data/factory/networking_factory.js +1 -1
  32. package/dist/data/model/job_request.d.ts +20 -1
  33. package/dist/data/model/token_payload.d.ts +17 -1
  34. package/dist/data/model/token_payload.js +1 -1
  35. package/dist/data/repository/use_smile_id_repository_impl.js +1 -1
  36. package/dist/debug/capture_debug_geometry.d.ts +29 -0
  37. package/dist/debug/capture_debug_geometry.js +1 -0
  38. package/dist/debug/document_preview_debug_geometry.d.ts +36 -0
  39. package/dist/debug/document_preview_debug_geometry.js +1 -0
  40. package/dist/debug/preview_debug_drawing.d.ts +51 -0
  41. package/dist/debug/preview_debug_drawing.js +1 -0
  42. package/dist/debug/selfie_preview_debug_geometry.d.ts +55 -0
  43. package/dist/debug/selfie_preview_debug_geometry.js +1 -0
  44. package/dist/debug/use_smile_id_debug.d.ts +61 -0
  45. package/dist/debug/use_smile_id_debug.js +1 -0
  46. package/dist/debug/use_smile_id_debug_tap.d.ts +25 -0
  47. package/dist/debug/use_smile_id_debug_tap.js +1 -0
  48. package/dist/debug/use_smile_id_document_capture_debug_overlay.d.ts +56 -0
  49. package/dist/debug/use_smile_id_document_capture_debug_overlay.js +1 -0
  50. package/dist/debug/use_smile_id_document_preview_debug_overlay.d.ts +32 -0
  51. package/dist/debug/use_smile_id_document_preview_debug_overlay.js +1 -0
  52. package/dist/debug/use_smile_id_selfie_capture_debug_overlay.d.ts +27 -0
  53. package/dist/debug/use_smile_id_selfie_capture_debug_overlay.js +1 -0
  54. package/dist/debug/use_smile_id_selfie_preview_debug_overlay.d.ts +60 -0
  55. package/dist/debug/use_smile_id_selfie_preview_debug_overlay.js +1 -0
  56. package/dist/index.d.ts +8 -1
  57. package/dist/index.js +1 -1
  58. package/dist/l10n/strings_en.d.ts +2 -1
  59. package/dist/l10n/strings_en.js +2 -2
  60. package/dist/l10n/use_smile_id_localizations.d.ts +1 -0
  61. package/dist/l10n/use_smile_id_localizations.js +1 -1
  62. package/dist/metadata/document_image_origin.d.ts +1 -0
  63. package/dist/metadata/document_image_origin.js +1 -1
  64. package/dist/metadata/metadata_configuration_mappers.js +1 -1
  65. package/dist/metadata/use_smile_id_metadata_factory.d.ts +4 -4
  66. package/dist/metadata/use_smile_id_metadata_factory.js +1 -1
  67. package/dist/presentation/assets/si_document_visa_hero_animation.json +1 -0
  68. package/dist/presentation/components/helpers/document_interior_crop.d.ts +54 -0
  69. package/dist/presentation/components/helpers/document_interior_crop.js +1 -0
  70. package/dist/presentation/components/helpers/selfie_interior_crop.d.ts +60 -0
  71. package/dist/presentation/components/helpers/selfie_interior_crop.js +1 -0
  72. package/dist/presentation/components/use_smile_id_capture_content.d.ts +7 -1
  73. package/dist/presentation/components/use_smile_id_capture_content.js +1 -1
  74. package/dist/presentation/components/use_smile_id_captured_document_image.d.ts +16 -0
  75. package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -1
  76. package/dist/presentation/components/use_smile_id_captured_image_oval.d.ts +12 -4
  77. package/dist/presentation/components/use_smile_id_captured_image_oval.js +1 -1
  78. package/dist/presentation/components/use_smile_id_document_overlay.d.ts +12 -1
  79. package/dist/presentation/components/use_smile_id_document_overlay.js +1 -1
  80. package/dist/presentation/components/use_smile_id_document_shimmer.d.ts +10 -5
  81. package/dist/presentation/components/use_smile_id_document_shimmer.js +1 -1
  82. package/dist/presentation/flow/analytics/use_smile_id_analytics_event.d.ts +4 -1
  83. package/dist/presentation/flow/analytics/use_smile_id_analytics_event.js +1 -1
  84. package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.d.ts +89 -0
  85. package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.js +1 -0
  86. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +4 -2
  87. package/dist/presentation/flow/dsl/builder/screens_builder.js +1 -1
  88. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +17 -13
  89. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +1 -1
  90. package/dist/presentation/flow/flow_navigation_manager.d.ts +52 -6
  91. package/dist/presentation/flow/flow_navigation_manager.js +1 -1
  92. package/dist/presentation/flow/model/instructions_defaults.d.ts +1 -0
  93. package/dist/presentation/flow/model/instructions_defaults.js +1 -1
  94. package/dist/presentation/flow/screen/helpers/always_enabled_slot.d.ts +8 -0
  95. package/dist/presentation/flow/screen/helpers/always_enabled_slot.js +1 -0
  96. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +1 -1
  97. package/dist/presentation/flow/screen/orchestrated_consent_screen.js +1 -1
  98. package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
  99. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +1 -1
  100. package/dist/presentation/flow/screen/orchestrated_processing_screen.js +1 -1
  101. package/dist/presentation/flow/validation/flow_validator.d.ts +13 -12
  102. package/dist/presentation/flow/validation/flow_validator.js +1 -1
  103. package/dist/presentation/flow/validation/job_type_validator.d.ts +9 -1
  104. package/dist/presentation/flow/validation/job_type_validator.js +1 -1
  105. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +6 -1
  106. package/dist/presentation/screen/use_smile_id_capture_screen.js +1 -1
  107. package/dist/presentation/screen/use_smile_id_document_back_instructions.js +1 -1
  108. package/dist/presentation/screen/use_smile_id_document_preview_screen.d.ts +5 -0
  109. package/dist/presentation/screen/use_smile_id_document_preview_screen.js +1 -1
  110. package/dist/presentation/screen/use_smile_id_preview_screen.js +1 -1
  111. package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +5 -0
  112. package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
  113. package/package.json +15 -15
@@ -9,6 +9,9 @@ export declare const tokenPayloadSchema: z.ZodObject<{
9
9
  last_name: z.ZodCatch<z.ZodOptional<z.ZodString>>;
10
10
  email: z.ZodCatch<z.ZodOptional<z.ZodString>>;
11
11
  phone_number: z.ZodCatch<z.ZodOptional<z.ZodString>>;
12
+ country: z.ZodCatch<z.ZodOptional<z.ZodString>>;
13
+ id_type: z.ZodCatch<z.ZodOptional<z.ZodString>>;
14
+ id_number: z.ZodCatch<z.ZodOptional<z.ZodString>>;
12
15
  consent: z.ZodCatch<z.ZodOptional<z.ZodObject<{
13
16
  granted: z.ZodCatch<z.ZodOptional<z.ZodLiteral<true>>>;
14
17
  granted_at: z.ZodCatch<z.ZodOptional<z.ZodString>>;
@@ -37,7 +40,8 @@ export type TokenConsent = {
37
40
  * Presence flags for the identity fields the token binds, parsed from
38
41
  * the `payload` claim. A flag is `true` iff the claim carries the key
39
42
  * with a non-empty string value (the value itself is an opaque vault
40
- * reference — the server injects the real value at submission).
43
+ * reference — the server injects the real value at submission, and
44
+ * rejects a request whose body contradicts it).
41
45
  */
42
46
  export type TokenPayload = {
43
47
  readonly hasGivenNames: boolean;
@@ -45,6 +49,10 @@ export type TokenPayload = {
45
49
  readonly hasEmail: boolean;
46
50
  readonly hasPhoneNumber: boolean;
47
51
  readonly consent?: TokenConsent;
52
+ /** Absent reads as not bound, as does `false`. */
53
+ readonly hasCountry?: boolean;
54
+ readonly hasIdType?: boolean;
55
+ readonly hasIdNumber?: boolean;
48
56
  };
49
57
  /**
50
58
  * Parse a raw `payload` claim value into a `TokenPayload`, or
@@ -53,6 +61,14 @@ export type TokenPayload = {
53
61
  * unknown keys, and degrades wrong-typed fields to absent.
54
62
  */
55
63
  export declare function parseTokenPayload(rawClaim: unknown): TokenPayload | undefined;
64
+ /** The params key of an ID field a token can bind. */
65
+ export type TokenBoundIdField = "country" | "idType" | "idNumber";
66
+ /**
67
+ * Whether the token binds the ID field named by its params key. The one
68
+ * rule both validation (a bound field may be blank) and request assembly
69
+ * (a bound field is never sent) read, so the two cannot disagree.
70
+ */
71
+ export declare function isIdFieldBoundByToken(payload: TokenPayload | undefined, field: TokenBoundIdField): boolean;
56
72
  /**
57
73
  * Token consent is COMPLETE when `granted` is `true` and every other
58
74
  * subfield is non-blank. A complete binding — granted with every
@@ -1 +1 @@
1
- "use strict";import{z as t}from"zod";const u=t.object({granted:t.literal(!0).optional().catch(void 0),granted_at:t.string().optional().catch(void 0),notice_language:t.string().optional().catch(void 0),notice_privacy_policy_url:t.string().optional().catch(void 0)});export const tokenPayloadSchema=t.object({given_names:t.string().optional().catch(void 0),last_name:t.string().optional().catch(void 0),email:t.string().optional().catch(void 0),phone_number:t.string().optional().catch(void 0),consent:u.optional().catch(void 0)});export function parseTokenPayload(n){if(n===null||typeof n!="object"||Array.isArray(n))return;const e=tokenPayloadSchema.safeParse(n);if(!e.success)return;const i=e.data,a=s(i.consent);return{hasGivenNames:r(i.given_names),hasLastName:r(i.last_name),hasEmail:r(i.email),hasPhoneNumber:r(i.phone_number),...a!==void 0?{consent:a}:{}}}export function isTokenConsentComplete(n){return n!==void 0&&tokenConsentMissingSubfields(n).length===0}export function tokenConsentMissingSubfields(n){const e=[];return n.granted!==!0&&e.push("granted"),o(n.grantedAt)||e.push("granted_at"),o(n.noticeLanguage)||e.push("notice_language"),o(n.noticePrivacyPolicyUrl)||e.push("notice_privacy_policy_url"),e}export function bindsRequiredUserDetails(n){return n===void 0?!1:n.hasGivenNames&&n.hasLastName&&(n.hasEmail||n.hasPhoneNumber)}function s(n){if(n===void 0)return;const e={...n.granted===!0?{granted:!0}:{},...n.granted_at!==void 0?{grantedAt:n.granted_at}:{},...n.notice_language!==void 0?{noticeLanguage:n.notice_language}:{},...n.notice_privacy_policy_url!==void 0?{noticePrivacyPolicyUrl:n.notice_privacy_policy_url}:{}};return Object.keys(e).length>0?e:void 0}function r(n){return n!==void 0&&n.length>0}function o(n){return n!==void 0&&n.trim().length>0}
1
+ "use strict";import{z as t}from"zod";const a=t.object({granted:t.literal(!0).optional().catch(void 0),granted_at:t.string().optional().catch(void 0),notice_language:t.string().optional().catch(void 0),notice_privacy_policy_url:t.string().optional().catch(void 0)});export const tokenPayloadSchema=t.object({given_names:t.string().optional().catch(void 0),last_name:t.string().optional().catch(void 0),email:t.string().optional().catch(void 0),phone_number:t.string().optional().catch(void 0),country:t.string().optional().catch(void 0),id_type:t.string().optional().catch(void 0),id_number:t.string().optional().catch(void 0),consent:a.optional().catch(void 0)});export function parseTokenPayload(n){if(n===null||typeof n!="object"||Array.isArray(n))return;const e=tokenPayloadSchema.safeParse(n);if(!e.success)return;const i=e.data,u=s(i.consent);return{hasGivenNames:r(i.given_names),hasLastName:r(i.last_name),hasEmail:r(i.email),hasPhoneNumber:r(i.phone_number),...u!==void 0?{consent:u}:{},hasCountry:r(i.country),hasIdType:r(i.id_type),hasIdNumber:r(i.id_number)}}export function isIdFieldBoundByToken(n,e){switch(e){case"country":return n?.hasCountry===!0;case"idType":return n?.hasIdType===!0;case"idNumber":return n?.hasIdNumber===!0}}export function isTokenConsentComplete(n){return n!==void 0&&tokenConsentMissingSubfields(n).length===0}export function tokenConsentMissingSubfields(n){const e=[];return n.granted!==!0&&e.push("granted"),o(n.grantedAt)||e.push("granted_at"),o(n.noticeLanguage)||e.push("notice_language"),o(n.noticePrivacyPolicyUrl)||e.push("notice_privacy_policy_url"),e}export function bindsRequiredUserDetails(n){return n===void 0?!1:n.hasGivenNames&&n.hasLastName&&(n.hasEmail||n.hasPhoneNumber)}function s(n){if(n===void 0)return;const e={...n.granted===!0?{granted:!0}:{},...n.granted_at!==void 0?{grantedAt:n.granted_at}:{},...n.notice_language!==void 0?{noticeLanguage:n.notice_language}:{},...n.notice_privacy_policy_url!==void 0?{noticePrivacyPolicyUrl:n.notice_privacy_policy_url}:{}};return Object.keys(e).length>0?e:void 0}function r(n){return n!==void 0&&n.length>0}function o(n){return n!==void 0&&n.trim().length>0}
@@ -1 +1 @@
1
- "use strict";import{failure as s,success as m}from"../../core/models/result";import{JobType as a}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=(t,n)=>{n!==void 0&&n>0&&e.push({name:t,value:String(n)})};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),e}export class UseSmileIDRepositoryImpl{_api;_retry;constructor(e,i){this._api=e,this._retry=i}async submit(e,i){if(e.jobType===a.bvn)return s(new f({fieldName:"jobType",reason:"BVN submission is not yet supported"}));let t=e;try{const n=await this._compressPayload(e,i),o=n?_(n):[];o.length>0&&(t={...e,metadata:e.metadata.withAdditionalEntries(o)})}catch{}try{const n=await l(this._retry,o=>this._dispatch(t,o));return m(n)}catch(n){return n instanceof p?s(n):s(this._mapError(n))}}async _compressPayload(e,i){const t=JSON.stringify(e.metadata.toEntries()).length;switch(e.jobType){case a.smartSelfieEnrollment:case a.smartSelfieAuthentication:case a.biometricKyc:return c.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,metadataBytes:t,sdkConfig:i});case a.documentVerification:case a.enhancedDocumentVerification:return c.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,backDocument:e.backDocument,metadataBytes:t,sdkConfig:i});default:return}}_dispatch(e,i){const t=i>0?{...e,metadata:e.metadata.withAdditionalEntries([{name:"network_retries",value:String(i)}])}:e;switch(t.jobType){case a.smartSelfieEnrollment:return this._api.smartSelfieEnrollment(t);case a.smartSelfieAuthentication:return this._api.smartSelfieAuthentication(t);case a.enhancedKyc:return this._api.enhancedKyc(t);case a.biometricKyc:return this._api.biometricKyc(t);case a.documentVerification:return this._api.documentVerification(t);case a.enhancedDocumentVerification:return this._api.enhancedDocumentVerification(t)}}_mapError(e){return d(e)}}
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)}}
@@ -0,0 +1,29 @@
1
+ import type { BoundingBox } from "@smileid/usesmileid_bridge";
2
+ import type { DebugRect, DebugSize } from "./selfie_preview_debug_geometry";
3
+ /**
4
+ * Where the capture debug overlay draws a detected box. The detector normalises
5
+ * each box against the upright analysis frame, and the preview covers its view
6
+ * with that frame, centred, so a box lands where the same cover fit puts it.
7
+ *
8
+ * `frameAspect` is the upright frame's width over its height: kamera binds the
9
+ * analysis stream to the preview's aspect ratio, so the preview's ratio is the
10
+ * frame's. `mirrored` flips the box across the view's vertical centre, for a
11
+ * preview mirrored against the analysed frame. `null` when either size is
12
+ * degenerate.
13
+ */
14
+ export declare function captureDebugViewRect(box: Pick<BoundingBox, "left" | "top" | "width" | "height">, frameAspect: number, viewSize: DebugSize, mirrored: boolean): DebugRect | null;
15
+ /**
16
+ * Whether the front lens's boxes must be flipped to land on the preview. The
17
+ * preview shows the front lens mirrored on both platforms. On iOS the Vision
18
+ * plugin analyses the frame in its mirrored orientation, so the boxes are
19
+ * already in the preview's space; on Android the detector sees the unmirrored
20
+ * frame, as the Android SDK's overlay assumes, so its boxes are flipped.
21
+ */
22
+ export declare function captureDebugBoxesMirrored(frontLens: boolean, isAndroid: boolean): boolean;
23
+ /**
24
+ * The upright analysis frame's width over its height on the document capture
25
+ * screen, whose `streamAspect` is the camera stream's ratio (either way up).
26
+ * The frame is upright in the orientation the screen is locked to, so its long
27
+ * side runs along the view's long side. `null` when either is degenerate.
28
+ */
29
+ export declare function documentDebugFrameAspect(streamAspect: number, viewSize: DebugSize): number | null;
@@ -0,0 +1 @@
1
+ "use strict";export function captureDebugViewRect(t,n,h,d){if(n<=0||h.width<=0||h.height<=0)return null;const u=Math.max(h.width/n,h.height),o=n*u,r=u,i=(h.width-o)/2,l=(h.height-r)/2,g=i+t.left*o,c=t.width*o;return{left:d?h.width-(g+c):g,top:l+t.top*r,width:c,height:t.height*r}}export function captureDebugBoxesMirrored(t,n){return t&&n}export function documentDebugFrameAspect(t,n){if(t<=0||n.width<=0||n.height<=0)return null;const h=Math.max(t,1/t);return n.width>=n.height?h:1/h}
@@ -0,0 +1,36 @@
1
+ import type { DebugRect, DebugSize } from "./selfie_preview_debug_geometry";
2
+ /** The saved image and its interior, in the card's coordinate space. */
3
+ export type DocumentPreviewInPlaceGeometry = {
4
+ readonly frameRect: DebugRect;
5
+ readonly plannedInteriorRect: DebugRect;
6
+ };
7
+ /** The saved image, its interior and the card window the user sees, in panel coordinates. */
8
+ export type DocumentPreviewPanelGeometry = DocumentPreviewInPlaceGeometry & {
9
+ readonly visibleCardRect: DebugRect;
10
+ };
11
+ /**
12
+ * Pure geometry for the document preview screen's debug overlay, the document's
13
+ * twin of `SelfiePreviewDebugGeometry`.
14
+ *
15
+ * The preview draws a document at `DocumentInteriorCrop.cardScale` into its
16
+ * rounded card: the interior inside the border it was saved with, grown by the
17
+ * auto-capture slack, fits the card, so the rest of the border never reaches
18
+ * the user. A document saved with no border covers the card whole. These helpers compute where the saved image, the card
19
+ * the user sees and the interior land: in the card's own coordinates for the
20
+ * in-place marking (`inPlace`), and in a fit-scaled thumbnail's for the
21
+ * full-image panel (`panel`).
22
+ */
23
+ export declare const DocumentPreviewDebugGeometry: {
24
+ /**
25
+ * Maps the saved image onto a `cardSize` card the way the production layout
26
+ * does, at `DocumentInteriorCrop.cardScale`, centred. `null` when either size
27
+ * is degenerate.
28
+ */
29
+ readonly inPlace: (imageSize: DebugSize, cardSize: DebugSize, borderFraction: number) => DocumentPreviewInPlaceGeometry | null;
30
+ /**
31
+ * Lays the whole saved image out inside a `panelSize` panel the way `contain`
32
+ * would, and maps the interior and the card window the preview shows into the
33
+ * same panel coordinates. `null` when any size is degenerate.
34
+ */
35
+ readonly panel: (imageSize: DebugSize, cardSize: DebugSize, panelSize: DebugSize, borderFraction: number) => DocumentPreviewPanelGeometry | null;
36
+ };
@@ -0,0 +1 @@
1
+ "use strict";import{DocumentInteriorCrop as u}from"../presentation/components/helpers/document_interior_crop";export const DocumentPreviewDebugGeometry={inPlace(t,h,n){if(c(t)||c(h))return null;const r=u.cardScale(t,h,n),o=f(h.width/2,h.height/2,t.width*r,t.height*r);return{frameRect:o,plannedInteriorRect:d(o,w(t,n)*r)}},panel(t,h,n,r){if(c(t)||c(h)||c(n))return null;const o=Math.min(n.width/t.width,n.height/t.height),e=f(n.width/2,n.height/2,t.width*o,t.height*o),l=u.cardScale(t,h,r);return{frameRect:e,plannedInteriorRect:d(e,w(t,r)*o),visibleCardRect:f(n.width/2,n.height/2,h.width/l*o,h.height/l*o)}}};function w(t,h){return Math.min(Math.max(h,0),u.fullBorderFraction)*Math.min(t.width,t.height)}function f(t,h,n,r){return{left:t-n/2,top:h-r/2,width:n,height:r}}function d(t,h){return{left:t.left+h,top:t.top+h,width:t.width-2*h,height:t.height-2*h}}function c(t){return t.width<=0||t.height<=0}
@@ -0,0 +1,51 @@
1
+ import type { DebugRect } from "./selfie_preview_debug_geometry";
2
+ /** Dashed outline of the planned interior. */
3
+ export declare function PlannedBoundary(props: {
4
+ readonly rect: DebugRect;
5
+ }): import("react").JSX.Element;
6
+ export declare function rectPath(rect: DebugRect): string;
7
+ /** The border ring: the frame with the planned interior punched out (even-odd). */
8
+ export declare function ringPath(frameRect: DebugRect, plannedInteriorRect: DebugRect): string;
9
+ export declare const PANEL_WIDTH = 120;
10
+ export declare const BOUNDARY_STROKE = 1.5;
11
+ /** Red A400 at ~35%: captured pixels the planned interior crop would hide. */
12
+ export declare const BORDER_RING_FILL = "rgba(255, 23, 68, 0.35)";
13
+ export declare const PLANNED_BOUNDARY_COLOR = "#FF1744";
14
+ /** Green A400: the boundary of what the user can see. */
15
+ export declare const VISIBLE_BOUNDARY_COLOR = "#00E676";
16
+ /** Pixels the preview screen never shows (outside the visible region / crop off-cuts). */
17
+ export declare const HIDDEN_TODAY_DIM = "rgba(0, 0, 0, 0.4)";
18
+ export declare const debugPreviewStyles: {
19
+ hudAnchor: {
20
+ position: "absolute";
21
+ start: number;
22
+ };
23
+ hud: {
24
+ backgroundColor: string;
25
+ borderRadius: number;
26
+ paddingHorizontal: number;
27
+ paddingVertical: number;
28
+ gap: number;
29
+ alignItems: "flex-start";
30
+ };
31
+ hudHeader: {
32
+ flexDirection: "row";
33
+ alignItems: "center";
34
+ gap: number;
35
+ };
36
+ hudChevron: {
37
+ paddingHorizontal: number;
38
+ paddingVertical: number;
39
+ borderRadius: number;
40
+ };
41
+ hudText: {
42
+ color: string;
43
+ fontSize: number;
44
+ fontFamily: string;
45
+ };
46
+ panel: {
47
+ borderWidth: number;
48
+ borderColor: string;
49
+ overflow: "hidden";
50
+ };
51
+ };
@@ -0,0 +1 @@
1
+ "use strict";import{jsx as i}from"react/jsx-runtime";import{Platform as d,StyleSheet as c}from"react-native";import{Rect as l}from"react-native-svg";export function PlannedBoundary(t){return i(l,{x:t.rect.left,y:t.rect.top,width:t.rect.width,height:t.rect.height,fill:"none",stroke:PLANNED_BOUNDARY_COLOR,strokeWidth:BOUNDARY_STROKE,strokeDasharray:`${r},${r}`})}export function rectPath(t){const{left:e,top:n,width:o,height:a}=t;return`M${e},${n} h${o} v${a} h${-o} Z`}export function ringPath(t,e){return`${rectPath(t)} ${rectPath(e)}`}export const PANEL_WIDTH=120,BOUNDARY_STROKE=1.5;const r=5;export const BORDER_RING_FILL="rgba(255, 23, 68, 0.35)",PLANNED_BOUNDARY_COLOR="#FF1744",VISIBLE_BOUNDARY_COLOR="#00E676",HIDDEN_TODAY_DIM="rgba(0, 0, 0, 0.4)",debugPreviewStyles=c.create({hudAnchor:{position:"absolute",start:8},hud:{backgroundColor:"rgba(0, 0, 0, 0.55)",borderRadius:6,paddingHorizontal:8,paddingVertical:4,gap:6,alignItems:"flex-start"},hudHeader:{flexDirection:"row",alignItems:"center",gap:8},hudChevron:{paddingHorizontal:6,paddingVertical:2,borderRadius:4},hudText:{color:"#FFFFFF",fontSize:11,fontFamily:d.select({ios:"Menlo",default:"monospace"})},panel:{borderWidth:1,borderColor:"rgba(255, 255, 255, 0.7)",overflow:"hidden"}});
@@ -0,0 +1,55 @@
1
+ /** A width × height in layout units (or image pixels). */
2
+ export type DebugSize = {
3
+ readonly width: number;
4
+ readonly height: number;
5
+ };
6
+ /** An axis-aligned rect: top-left corner plus size. */
7
+ export type DebugRect = {
8
+ readonly left: number;
9
+ readonly top: number;
10
+ readonly width: number;
11
+ readonly height: number;
12
+ };
13
+ /** Frame and planned-interior rects in the oval box's coordinate space. */
14
+ export type SelfiePreviewInPlaceGeometry = {
15
+ readonly frameRect: DebugRect;
16
+ readonly plannedInteriorRect: DebugRect;
17
+ };
18
+ /** Frame, planned-interior, and user-visible ellipse bounds in panel coordinates. */
19
+ export type SelfiePreviewPanelGeometry = {
20
+ readonly frameRect: DebugRect;
21
+ readonly plannedInteriorRect: DebugRect;
22
+ readonly visibleOvalRect: DebugRect;
23
+ };
24
+ /**
25
+ * Pure geometry for the selfie preview-screen debug overlay.
26
+ *
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
31
+ * the user actually sees, and the interior crop land — in the oval box's
32
+ * coordinate space for the in-place marking (`inPlace`) and in a fit-scaled
33
+ * thumbnail's coordinate space for the full-frame panel (`panel`).
34
+ *
35
+ * Only the debug overlay uses it; it never shapes what users see.
36
+ */
37
+ export declare const SelfiePreviewDebugGeometry: {
38
+ readonly plannedBorderFraction: 0.15;
39
+ /**
40
+ * 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
+ */
45
+ readonly inPlace: (imageSize: DebugSize, ovalRect: DebugRect) => SelfiePreviewInPlaceGeometry | null;
46
+ /**
47
+ * Lays the full frame out inside a panel the way contain scaling would
48
+ * (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.
53
+ */
54
+ readonly panel: (imageSize: DebugSize, ovalSize: DebugSize, panelSize: DebugSize) => SelfiePreviewPanelGeometry | null;
55
+ };
@@ -0,0 +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}
@@ -0,0 +1,61 @@
1
+ type Listener = () => void;
2
+ /**
3
+ * Debug hook state the SDK's screens subscribe to, so toggling a switch
4
+ * redraws a screen that is already on display. Not part of the public API.
5
+ */
6
+ export declare const UseSmileIDDebugHooks: {
7
+ /** Whether the selfie capture and preview screens draw their debug overlays. */
8
+ readonly isSelfiePreviewOverlayEnabled: () => boolean;
9
+ /** Sets the flag and notifies subscribers; ungated, for tests and the public setter. */
10
+ readonly setSelfiePreviewOverlayEnabled: (enabled: boolean) => void;
11
+ /** Whether the document capture and preview screens draw their debug overlays. */
12
+ readonly isDocumentPreviewOverlayEnabled: () => boolean;
13
+ /** Sets the flag and notifies subscribers; ungated, for tests and the public setter. */
14
+ readonly setDocumentPreviewOverlayEnabled: (enabled: boolean) => void;
15
+ readonly subscribe: (listener: Listener) => () => void;
16
+ };
17
+ /**
18
+ * Developer-only switches for the SDK's debug visualisations.
19
+ *
20
+ * Inert in release builds: the setter returns early unless the bundler's
21
+ * `__DEV__` is true, so the overlays can never be switched on in a shipped app.
22
+ */
23
+ export declare const UseSmileIDDebug: {
24
+ /**
25
+ * Installs or clears the selfie debug overlays. When enabled, the selfie
26
+ * capture screen draws the detector's face boxes over the preview with a
27
+ * detection HUD (the detector's frame rate, face count, brightness), as on
28
+ * Android, and the post-capture selfie preview screen marks which parts of
29
+ * the captured frame the user cannot see, plus the planned interior-crop
30
+ * boundary, in red.
31
+ *
32
+ * Safe to call at any time (e.g. from a switch); the change applies to the
33
+ * next — and any currently shown — selfie capture or preview screen.
34
+ */
35
+ readonly setSelfieDebugOverlaysEnabled: (enabled: boolean) => void;
36
+ /** Whether the selfie debug overlays are currently installed. */
37
+ readonly isSelfieDebugOverlaysEnabled: () => boolean;
38
+ /**
39
+ * Installs or clears the document debug overlays. When enabled, the document
40
+ * capture screen draws the detected document boxes over the preview (the one
41
+ * the capture gate judges in green) with a detection HUD (detections per
42
+ * second, document count, the gated box's score and brightness), as on
43
+ * Android, and the document preview screen marks the saved document's hidden
44
+ * border in red, the interior the card shows as a dashed outline and the
45
+ * card in green, with a full-image panel behind the chevron. The card shows
46
+ * the interior alone, so no red should appear inside it.
47
+ *
48
+ * Safe to call at any time; the change applies to the next, and any
49
+ * currently shown, document capture or preview screen.
50
+ */
51
+ readonly setDocumentDebugOverlaysEnabled: (enabled: boolean) => void;
52
+ /** Whether the document debug overlays are currently installed. */
53
+ readonly isDocumentDebugOverlaysEnabled: () => boolean;
54
+ };
55
+ /** Subscribes a component to the selfie preview overlay flag. */
56
+ export declare function useSelfiePreviewOverlayEnabled(): boolean;
57
+ /** Subscribes a component to the document overlays flag. */
58
+ 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
+ export {};
@@ -0,0 +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__}
@@ -0,0 +1,25 @@
1
+ import type { DocumentDetectorOutput, FaceDetectorOutput } from "@smileid/usesmileid_bridge";
2
+ type FaceListener = (output: FaceDetectorOutput) => void;
3
+ type DocumentListener = (output: DocumentDetectorOutput) => void;
4
+ /**
5
+ * What the capture debug overlays read off the capture loop in a dev build:
6
+ * the detector output of each analysed frame. Android's counterpart is
7
+ * `UseSmileIDDebugHooks.sink`. Not part of the public API.
8
+ *
9
+ * The frame stream calls `analyzedFace` and `analyzedDocument` only in a dev
10
+ * build; with nothing listening each costs a null check.
11
+ */
12
+ export declare const UseSmileIDDebugTap: {
13
+ /**
14
+ * Installs `listener`, replacing any earlier one, and returns the function
15
+ * that stops it. An overlay leaving the screen can then only clear its own
16
+ * listener, never one a newer overlay installed before the old one's cleanup
17
+ * ran.
18
+ */
19
+ readonly listenToFaces: (listener: FaceListener) => () => void;
20
+ /** The document twin of `listenToFaces`, for the document capture overlay. */
21
+ readonly listenToDocuments: (listener: DocumentListener) => () => void;
22
+ readonly analyzedFace: (output: FaceDetectorOutput) => void;
23
+ readonly analyzedDocument: (output: DocumentDetectorOutput) => void;
24
+ };
25
+ export {};
@@ -0,0 +1 @@
1
+ "use strict";let n=null,l=null;export const UseSmileIDDebugTap={listenToFaces(e){return n=e,()=>{n===e&&(n=null)}},listenToDocuments(e){return l=e,()=>{l===e&&(l=null)}},analyzedFace(e){n?.(e)},analyzedDocument(e){l?.(e)}};
@@ -0,0 +1,56 @@
1
+ import type { DocumentDetectorOutput } from "@smileid/usesmileid_bridge";
2
+ export type DocumentCaptureDebugOverlayProps = {
3
+ /** The camera stream's ratio, either way up; see `documentDebugFrameAspect`. */
4
+ readonly streamAspect: number;
5
+ /** Whether each box is flipped to land on the preview; see `captureDebugBoxesMirrored`. */
6
+ readonly mirrorBoxes: boolean;
7
+ /** The overlay's clock, in milliseconds, read at mount; `Date.now` when omitted. */
8
+ readonly now?: () => number;
9
+ };
10
+ /** One detection the overlay shows, and when it arrived on the overlay's clock. */
11
+ export type DocumentCaptureDebugFrame = {
12
+ readonly output: DocumentDetectorOutput;
13
+ readonly detectedAtMs: number;
14
+ };
15
+ /** How often the detection rate is sampled, as on Android. */
16
+ export declare const DOCUMENT_CAPTURE_DEBUG_SAMPLE_INTERVAL_MS = 500;
17
+ /**
18
+ * How long a detection stays up without a new one: longer than the native
19
+ * document analyzers' 350 ms detection interval, between which they report
20
+ * empty outputs.
21
+ */
22
+ export declare const DOCUMENT_CAPTURE_DEBUG_HOLD_MS = 500;
23
+ /**
24
+ * The frame the overlay shows after `output` arrives at `nowMs`. A detection
25
+ * replaces it; an empty output, which the analyzer reports between its
26
+ * detection runs, clears it only once the shown detection is older than
27
+ * `DOCUMENT_CAPTURE_DEBUG_HOLD_MS`, so the box stays up between runs and goes
28
+ * once the document is lost.
29
+ */
30
+ export declare function nextDocumentCaptureDebugFrame(previous: DocumentCaptureDebugFrame | null, output: DocumentDetectorOutput, nowMs: number): DocumentCaptureDebugFrame | null;
31
+ /**
32
+ * `frame` while it is younger than `DOCUMENT_CAPTURE_DEBUG_HOLD_MS` at
33
+ * `nowMs`, else `null`. The sampler applies it too, so a detection expires
34
+ * when the analyzer stops reporting, as when the capture finishes.
35
+ */
36
+ export declare function documentCaptureDebugFrameStillShown(frame: DocumentCaptureDebugFrame | null, nowMs: number): DocumentCaptureDebugFrame | null;
37
+ /**
38
+ * Draws `UseSmileIDDocumentCaptureDebugOverlay` over the document capture
39
+ * screen while `UseSmileIDDebug.setDocumentDebugOverlaysEnabled(true)` is in
40
+ * effect, and nothing otherwise. Subscribes to the hook state, so the switch
41
+ * applies to a screen already on display.
42
+ */
43
+ export declare function DocumentCaptureDebugOverlayHost(props: DocumentCaptureDebugOverlayProps): import("react").JSX.Element | null;
44
+ /**
45
+ * Developer overlay over the document capture screen, the document's twin of
46
+ * `UseSmileIDSelfieCaptureDebugOverlay`, as on Android: the detected document
47
+ * boxes drawn where the preview shows them (the first, the one the capture
48
+ * gate judges, in green; any others in yellow), and a HUD with the detections
49
+ * per second, the document count, the gated box's score and its brightness.
50
+ * Display-only: it reads the capture loop's own output through
51
+ * `UseSmileIDDebugTap`.
52
+ *
53
+ * Android's HUD also shows the camera's frame rate. kamera keeps every frame
54
+ * native, so JavaScript never sees one to count.
55
+ */
56
+ export declare function UseSmileIDDocumentCaptureDebugOverlay(props: DocumentCaptureDebugOverlayProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use strict";import{jsx as t,jsxs as y}from"react/jsx-runtime";import{useEffect as P,useRef as E,useState as b}from"react";import{Platform as R,Pressable as L,StyleSheet as w,Text as u,View as h}from"react-native";import{useSafeAreaInsets as U}from"react-native-safe-area-context";import{captureDebugViewRect as M,documentDebugFrameAspect as H}from"./capture_debug_geometry";import{useDocumentPreviewOverlayEnabled as N}from"./use_smile_id_debug";import{UseSmileIDDebugTap as V}from"./use_smile_id_debug_tap";import{SelfiePreviewDebugHud as j}from"./use_smile_id_selfie_preview_debug_overlay";export const DOCUMENT_CAPTURE_DEBUG_SAMPLE_INTERVAL_MS=500,DOCUMENT_CAPTURE_DEBUG_HOLD_MS=500;export function nextDocumentCaptureDebugFrame(e,n,i){return n.documents.length>0?{output:n,detectedAtMs:i}:documentCaptureDebugFrameStillShown(e,i)}export function documentCaptureDebugFrameStillShown(e,n){return e!==null&&n-e.detectedAtMs<DOCUMENT_CAPTURE_DEBUG_HOLD_MS?e:null}export function DocumentCaptureDebugOverlayHost(e){return N()?t(UseSmileIDDocumentCaptureDebugOverlay,{...e}):null}export function UseSmileIDDocumentCaptureDebugOverlay(e){const n=U(),[i,g]=b(null),[v,A]=b(0),[x,O]=b(!0),[a,B]=b(null),p=E(0),S=E(e.now??Date.now);P(()=>{const s=V.listenToDocuments(l=>{l.documents.length>0&&(p.current+=1);const m=S.current();g(_=>nextDocumentCaptureDebugFrame(_,l,m))});let r=Date.now();const c=setInterval(()=>{const l=Date.now(),m=(l-r)/1e3;r=l;const _=p.current;p.current=0,A(m>0?_/m:0);const I=S.current();g(T=>documentCaptureDebugFrameStillShown(T,I))},DOCUMENT_CAPTURE_DEBUG_SAMPLE_INTERVAL_MS);return()=>{s(),clearInterval(c)}},[]);const C=s=>{const{width:r,height:c}=s.nativeEvent.layout;B({width:r,height:c})},D=i?.output??null,d=D?.documents??[],f=a?H(e.streamAspect,a):null;return y(h,{pointerEvents:"box-none",style:w.absoluteFill,onLayout:C,testID:"si_debug_document_capture_overlay",children:[x&&a&&f!==null?d.map((s,r)=>{const c=M(s,f,a,e.mirrorBoxes);return c?t(h,{pointerEvents:"none",testID:"si_debug_document_capture_box",style:[o.box,r===0?o.gatedBox:o.otherBox,c]},r):null}):null,t(h,{style:[o.hudAnchor,{bottom:n.bottom+8}],testID:"si_debug_document_capture_hud",children:t(j,{title:"DETECTION",children:y(h,{children:[t(u,{style:o.stat,children:`Detections/s: ${v.toFixed(1)}`}),t(u,{style:o.stat,children:`Documents: ${d.length}`}),t(u,{style:o.stat,children:`Score: ${d[0]?d[0].score.toFixed(2):"\u2014"}`}),t(u,{style:o.stat,children:`Brightness: ${D?D.brightness.toFixed(0):"\u2014"}`}),t(L,{accessibilityRole:"button",testID:"si_debug_document_capture_box_toggle",onPress:()=>O(s=>!s),hitSlop:8,children:t(u,{style:[o.stat,o.toggle],children:x?"Hide box":"Show box"})})]})})})]})}const F="#00E676",$="#FFEA00",o=w.create({hudAnchor:{position:"absolute",start:8},box:{position:"absolute",borderWidth:2},gatedBox:{borderColor:F},otherBox:{borderColor:$},stat:{color:"#FFFFFF",fontSize:11,fontFamily:R.select({ios:"Menlo",default:"monospace"})},toggle:{color:F,marginTop:4}});
@@ -0,0 +1,32 @@
1
+ import type { DebugSize } from "./selfie_preview_debug_geometry";
2
+ export type UseSmileIDDocumentPreviewDebugOverlayProps = {
3
+ /** Local file URI of the captured document the preview screen shows. */
4
+ readonly uri: string;
5
+ /** The saved document's upright pixel size; the panel waits for it. */
6
+ readonly imageSize: DebugSize | null;
7
+ /** The card's laid-out size, in the preview screen's layout units. */
8
+ readonly cardSize: DebugSize;
9
+ /** The hidden border the document was saved with, as a fraction of its short side. */
10
+ readonly borderFraction: number;
11
+ };
12
+ /**
13
+ * Developer overlay for the document preview screen: the saved document's
14
+ * hidden border, the interior the card shows and the card itself, over a
15
+ * full-image panel behind the chevron (top start). Drawn by the preview screen
16
+ * while `UseSmileIDDebug.setDocumentDebugOverlaysEnabled(true)` is in effect;
17
+ * the card draws the in-place marking (`DocumentPreviewHiddenRegionsInPlace`).
18
+ *
19
+ * Display-only: it reads the already-captured file and the card's laid-out
20
+ * size; it never alters what the preview shows or what is uploaded.
21
+ */
22
+ export declare function UseSmileIDDocumentPreviewDebugOverlay(props: UseSmileIDDocumentPreviewDebugOverlayProps): import("react").JSX.Element;
23
+ /**
24
+ * In-place marking over the card: red on any visible pixel the hidden border
25
+ * covers (there should be none), the planned interior dashed and the card, the
26
+ * boundary of what the user sees, in green.
27
+ */
28
+ export declare function DocumentPreviewHiddenRegionsInPlace(props: {
29
+ readonly imageSize: DebugSize;
30
+ readonly cardSize: DebugSize;
31
+ readonly borderFraction: number;
32
+ }): import("react").JSX.Element | null;
@@ -0,0 +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;
@@ -0,0 +1,27 @@
1
+ export type SelfieCaptureDebugOverlayProps = {
2
+ /** The upright analysis frame's width over its height. */
3
+ readonly frameAspect: number;
4
+ /** Whether each box is flipped to land on the mirrored preview; see `captureDebugBoxesMirrored`. */
5
+ readonly mirrorBoxes: boolean;
6
+ };
7
+ /** How often the frame rate is sampled, as on Android. */
8
+ export declare const SELFIE_CAPTURE_DEBUG_SAMPLE_INTERVAL_MS = 500;
9
+ /**
10
+ * Draws `UseSmileIDSelfieCaptureDebugOverlay` over the selfie capture screen
11
+ * while `UseSmileIDDebug.setSelfieDebugOverlaysEnabled(true)` is in effect, and
12
+ * nothing otherwise. Subscribes to the hook state, so the switch applies to a
13
+ * screen already on display.
14
+ */
15
+ export declare function SelfieCaptureDebugOverlayHost(props: SelfieCaptureDebugOverlayProps): import("react").JSX.Element | null;
16
+ /**
17
+ * Developer overlay over the selfie capture screen, as on Android: the
18
+ * detector's face boxes drawn where the preview shows those faces, and a HUD
19
+ * with the detector's frame rate, the face count and the largest face's
20
+ * brightness. Display-only: it reads the capture loop's own output through
21
+ * `UseSmileIDDebugTap`.
22
+ *
23
+ * Android's HUD also shows the camera's frame rate. kamera keeps every frame
24
+ * native, so JavaScript never sees one to count; the detector rate is the rate
25
+ * the capture loop analyses at.
26
+ */
27
+ export declare function UseSmileIDSelfieCaptureDebugOverlay(props: SelfieCaptureDebugOverlayProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use strict";import{jsx as e,jsxs as _}from"react/jsx-runtime";import{useEffect as w,useRef as L,useState as i}from"react";import{Platform as A,Pressable as P,StyleSheet as g,Text as c,View as a}from"react-native";import{useSafeAreaInsets as T}from"react-native-safe-area-context";import{captureDebugViewRect as B}from"./capture_debug_geometry";import{useSelfiePreviewOverlayEnabled as O}from"./use_smile_id_debug";import{UseSmileIDDebugTap as R}from"./use_smile_id_debug_tap";import{SelfiePreviewDebugHud as C}from"./use_smile_id_selfie_preview_debug_overlay";export const SELFIE_CAPTURE_DEBUG_SAMPLE_INTERVAL_MS=500;export function SelfieCaptureDebugOverlayHost(r){return O()?e(UseSmileIDSelfieCaptureDebugOverlay,{...r}):null}export function UseSmileIDSelfieCaptureDebugOverlay(r){const f=T(),[u,x]=i(null),[y,D]=i(0),[b,F]=i(!0),[h,E]=i(null),d=L(0);w(()=>{const t=R.listenToFaces(l=>{d.current+=1,x(l)});let o=Date.now();const n=setInterval(()=>{const l=Date.now(),m=(l-o)/1e3;o=l;const I=d.current;d.current=0,D(m>0?I/m:0)},SELFIE_CAPTURE_DEBUG_SAMPLE_INTERVAL_MS);return()=>{t(),clearInterval(n)}},[]);const v=t=>{const{width:o,height:n}=t.nativeEvent.layout;E({width:o,height:n})},p=u?.faces??[];return _(a,{pointerEvents:"box-none",style:g.absoluteFill,onLayout:v,testID:"si_debug_selfie_capture_overlay",children:[b&&h?p.map((t,o)=>{const n=B(t,r.frameAspect,h,r.mirrorBoxes);return n?e(a,{pointerEvents:"none",testID:"si_debug_selfie_capture_box",style:[s.box,n]},o):null}):null,e(a,{style:[s.hudAnchor,{top:f.top+8}],testID:"si_debug_selfie_capture_hud",children:e(C,{title:"DETECTION",children:_(a,{children:[e(c,{style:s.stat,children:`Detector FPS: ${y.toFixed(1)}`}),e(c,{style:s.stat,children:`Faces: ${p.length}`}),e(c,{style:s.stat,children:`Brightness: ${u&&p.length>0?u.brightness.toFixed(0):"\u2014"}`}),e(P,{accessibilityRole:"button",testID:"si_debug_selfie_capture_box_toggle",onPress:()=>F(t=>!t),hitSlop:8,children:e(c,{style:[s.stat,s.toggle],children:b?"Hide box":"Show box"})})]})})})]})}const S="#00E676",s=g.create({hudAnchor:{position:"absolute",end:8},box:{position:"absolute",borderWidth:2,borderColor:S},stat:{color:"#FFFFFF",fontSize:11,fontFamily:A.select({ios:"Menlo",default:"monospace"})},toggle:{color:S,marginTop:4}});
@@ -0,0 +1,60 @@
1
+ import { type ReactNode } from "react";
2
+ import { type DebugRect, type DebugSize } from "./selfie_preview_debug_geometry";
3
+ export type UseSmileIDSelfiePreviewDebugOverlayProps = {
4
+ /** Local file URI of the captured selfie the preview screen shows. */
5
+ readonly uri: string;
6
+ /** The laid-out oval box, in this overlay's coordinate space. */
7
+ readonly ovalRect: DebugRect;
8
+ };
9
+ /**
10
+ * Developer overlay for the post-capture selfie preview screen: shows which
11
+ * parts of the captured frame the user cannot see, and where the interior crop
12
+ * lands. The preview shows the interior alone, so no red should appear inside
13
+ * the oval. Drawn by the preview screen while
14
+ * `UseSmileIDDebug.setSelfieDebugOverlaysEnabled(true)` is in effect.
15
+ *
16
+ * Two synchronised views share `SelfiePreviewDebugGeometry`:
17
+ * - In place, over the production preview: red tint on every visible pixel
18
+ * that falls inside the planned border ring, a dashed outline where the
19
+ * planned interior crop lands (drawn unclipped so its full extent shows
20
+ * against the surrounding wash), and a green stroke on the oval — the
21
+ * boundary of what the user can see.
22
+ * - A full-frame panel (top start, collapsible behind the chevron): the whole
23
+ * captured file fit-scaled, with the regions the preview never shows dimmed,
24
+ * the planned border ring tinted red, and the same dashed / green
25
+ * boundaries.
26
+ *
27
+ * Display-only: it reads the already-captured file and the screen's laid-out
28
+ * oval geometry; it never alters what the production preview shows or what is
29
+ * uploaded. The frame's pixel size is read once from the file, so the in-place
30
+ * marking stays correct while the panel is collapsed.
31
+ */
32
+ export declare function UseSmileIDSelfiePreviewDebugOverlay(props: UseSmileIDSelfiePreviewDebugOverlayProps): import("react").JSX.Element;
33
+ /**
34
+ * In-place marking drawn over the production preview image: red on the
35
+ * visible pixels the planned crop would remove, dashed planned-crop boundary,
36
+ * green visible-boundary stroke. Coordinates match the preview's oval box.
37
+ */
38
+ export declare function SelfiePreviewHiddenRegionsInPlace(props: {
39
+ readonly imageSize: DebugSize;
40
+ readonly ovalRect: DebugRect;
41
+ }): import("react").JSX.Element | null;
42
+ /**
43
+ * Markings over the full-frame panel: everything outside the user-visible
44
+ * ellipse dimmed, the planned border ring tinted red, and the two boundary
45
+ * strokes.
46
+ */
47
+ export declare function SelfiePreviewPanelMarkings(props: {
48
+ readonly imageSize: DebugSize;
49
+ readonly ovalSize: DebugSize;
50
+ readonly panelSize: DebugSize;
51
+ }): import("react").JSX.Element | null;
52
+ /**
53
+ * Collapsible heads-up display chrome: dark rounded background, the title
54
+ * header and an expand/collapse chevron. `children` render only while
55
+ * expanded.
56
+ */
57
+ export declare function SelfiePreviewDebugHud(props: {
58
+ readonly title: string;
59
+ readonly children: ReactNode;
60
+ }): import("react").JSX.Element;
@@ -0,0 +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;
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  */
9
9
  export { UseSmileIDBuilder } from "./presentation/flow/dsl/use_smile_id_builder";
10
10
  export { UseSmileIDFlowBuilder } from "./presentation/flow/dsl/use_smile_id_flow_builder";
11
+ export type { FlowBuildResult, FlowBuildResultInvalid, FlowBuildResultSuccess, } from "./presentation/flow/dsl/flow_build_result";
11
12
  export { ValidationState, type ValidationIssue, type ValidationStateInvalid, } from "./presentation/flow/validation/validation_state";
12
13
  export { AgentModeWithEnhancedLivenessException, BuilderValidationException, isNetworkErrorCode, isRetryableErrorCode, ConsentDeniedException, DuplicateScreenTypeException, EmptyScreensBlockException, InvalidConsentConfigException, InvalidConsentInformationException, InvalidDocumentCaptureConfigException, InvalidFieldValueException, InvalidJobTypeScreenConfigException, InvalidMissingCaptureTypeException, InvalidScreenOrderException, InvalidSelfieCaptureConfigException, LivenessFrameCountException, IssueSeverity, MissingCaptureForPreviewException, MissingJobTypeException, MissingMLConfigurationBlockException, MissingNetworkConfigurationBlockException, MissingPartnerConfigException, MissingRequiredFieldException, ProcessingCancelledException, UseSmileIDAttestationException, UseSmileIDBuilderException, UseSmileIDCameraException, UseSmileIDCameraSurfaceException, UseSmileIDErrorCode, UseSmileIDException, UseSmileIDGeneralException, UnusedJobParamsException, UseSmileIDNetworkException, type UseSmileIDNetworkErrorCode, UseSmileIDValidationException, } from "./core/exception";
13
14
  export { UseSmileIDConfigBuilder } from "./presentation/flow/dsl/builder/config_builder";
@@ -30,6 +31,7 @@ export { OrchestratedCaptureScreen, type OrchestratedCaptureScreenProps, } from
30
31
  export { OrchestratedPreviewScreen, type OrchestratedPreviewScreenProps, } from "./presentation/flow/screen/orchestrated_preview_screen";
31
32
  export { OrchestratedProcessingScreen, type OrchestratedProcessingScreenProps, } from "./presentation/flow/screen/orchestrated_processing_screen";
32
33
  export { UseSmileIDCrashReporting } from "@smileid/usesmileid_bridge";
34
+ export { DocumentSide } from "@smileid/usesmileid_bridge";
33
35
  export { UseSmileIDLocalizations } from "./l10n/use_smile_id_localizations";
34
36
  export { UseSmileIDLocalizationsProvider, useStableLocalizations, useUseSmileIDLocalizations, type UseSmileIDLocalizationsProviderProps, } from "./l10n/use_smile_id_localizations_provider";
35
37
  export { enStrings, type SiKey } from "./l10n/strings_en";
@@ -51,8 +53,12 @@ export { GlobalPrivacyControl, GLOBAL_PRIVACY_CONTROL_WIRE_KEY, globalPrivacyCon
51
53
  export { LogLevel } from "./core/models/network_config";
52
54
  export type { CacheConfig, Interceptor, NetworkConfig, NetworkInnerConfig, NetworkLoggingConfig, PartnerConfig, RetryConfig, TimeoutConfig, } from "./core/models/network_config";
53
55
  export type { AnalyzerRegistry, MLConfig } from "./core/models/ml_config";
54
- export type { BiometricKYCParams, ConsentInformation, DocumentVerificationParams, EnhancedDocumentVerificationParams, EnhancedKYCParams, Timestamp, UserDetails, } from "./core/models/params";
56
+ export type { BiometricKYCParams, ConsentInformation, DocumentVerificationParams, EnhancedDocumentVerificationParams, ResidencyDocumentVerificationParams, EnhancedKYCParams, Timestamp, UserDetails, } from "./core/models/params";
57
+ export { RESIDENCY_ID_TYPE } from "./core/models/params";
55
58
  export type { UseSmileIDAnalyticsEvent, UseSmileIDAnalyticsEventType, } from "./presentation/flow/analytics/use_smile_id_analytics_event";
59
+ export { UseSmileIDAnalyticsEventTypes, UseSmileIDAnalyticsParamKeys, UseSmileIDAnalyticsReasons, UseSmileIDAnalyticsScreenNames, } from "./presentation/flow/analytics/use_smile_id_analytics_event";
60
+ export { analyticsEventKind } from "./presentation/flow/analytics/use_smile_id_analytics_event_kind";
61
+ export type { UseSmileIDAnalyticsEventKind, UseSmileIDAnalyticsScreenName, } from "./presentation/flow/analytics/use_smile_id_analytics_event_kind";
56
62
  export type { JobSubmissionResponse } from "./data/model/job_submission_response";
57
63
  export { type DecodedToken, decodeSmileIDToken, } from "./data/util/use_smile_id_token_decoder";
58
64
  export { type SdkConfig, numSamplesOverride } from "./data/model/sdk_config";
@@ -111,3 +117,4 @@ export { UseSmileIDPreviewScreen, type UseSmileIDPreviewScreenProps, } from "./p
111
117
  export { UseSmileIDProcessingScreen, type ProcessingState, type UseSmileIDProcessingScreenProps, } from "./presentation/screen/use_smile_id_processing_screen";
112
118
  export { ForceMaxBrightness } from "./presentation/utils/force_max_brightness";
113
119
  export type { ForceMaxBrightnessProps } from "./presentation/utils/force_max_brightness";
120
+ export { UseSmileIDDebug } from "./debug/use_smile_id_debug";