@smileid/usesmileid 12.0.0-beta01 → 12.0.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 (403) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +701 -89
  3. package/dist/camera/camera_defaults.d.ts +14 -9
  4. package/dist/camera/camera_defaults.d.ts.map +1 -1
  5. package/dist/camera/camera_defaults.js +11 -9
  6. package/dist/camera/camera_defaults.js.map +1 -1
  7. package/dist/camera/orientation_degrees.d.ts +5 -6
  8. package/dist/camera/orientation_degrees.d.ts.map +1 -1
  9. package/dist/camera/orientation_degrees.js +5 -6
  10. package/dist/camera/orientation_degrees.js.map +1 -1
  11. package/dist/core/models/document_capture_mode.d.ts +8 -0
  12. package/dist/core/models/document_capture_mode.d.ts.map +1 -1
  13. package/dist/core/models/document_capture_mode.js +24 -0
  14. package/dist/core/models/document_capture_mode.js.map +1 -1
  15. package/dist/core/models/document_type.d.ts +2 -2
  16. package/dist/core/models/document_type.d.ts.map +1 -1
  17. package/dist/core/models/document_type.js +6 -4
  18. package/dist/core/models/document_type.js.map +1 -1
  19. package/dist/core/models/result.d.ts +9 -8
  20. package/dist/core/models/result.d.ts.map +1 -1
  21. package/dist/core/models/result.js +5 -6
  22. package/dist/core/models/result.js.map +1 -1
  23. package/dist/data/api/interceptors/smile_auth_interceptor.js +1 -1
  24. package/dist/data/api/interceptors/smile_auth_interceptor.js.map +1 -1
  25. package/dist/data/api/interceptors/smile_integrity_interceptor.d.ts +11 -0
  26. package/dist/data/api/interceptors/smile_integrity_interceptor.d.ts.map +1 -1
  27. package/dist/data/api/interceptors/smile_integrity_interceptor.js +13 -0
  28. package/dist/data/api/interceptors/smile_integrity_interceptor.js.map +1 -1
  29. package/dist/data/api/interceptors/smile_request_mac_interceptor.d.ts +8 -0
  30. package/dist/data/api/interceptors/smile_request_mac_interceptor.d.ts.map +1 -1
  31. package/dist/data/api/interceptors/smile_request_mac_interceptor.js +11 -0
  32. package/dist/data/api/interceptors/smile_request_mac_interceptor.js.map +1 -1
  33. package/dist/data/compression/compressible_asset.d.ts +41 -0
  34. package/dist/data/compression/compressible_asset.d.ts.map +1 -0
  35. package/dist/data/compression/compressible_asset.js +34 -0
  36. package/dist/data/compression/compressible_asset.js.map +1 -0
  37. package/dist/data/compression/payload_compression_config.d.ts +36 -0
  38. package/dist/data/compression/payload_compression_config.d.ts.map +1 -0
  39. package/dist/data/compression/payload_compression_config.js +52 -0
  40. package/dist/data/compression/payload_compression_config.js.map +1 -0
  41. package/dist/data/compression/payload_compression_runner.d.ts +29 -0
  42. package/dist/data/compression/payload_compression_runner.d.ts.map +1 -0
  43. package/dist/data/compression/payload_compression_runner.js +55 -0
  44. package/dist/data/compression/payload_compression_runner.js.map +1 -0
  45. package/dist/data/compression/payload_compressor.d.ts +40 -0
  46. package/dist/data/compression/payload_compressor.d.ts.map +1 -0
  47. package/dist/data/compression/payload_compressor.js +126 -0
  48. package/dist/data/compression/payload_compressor.js.map +1 -0
  49. package/dist/data/compression/uri_compressible_asset.d.ts +23 -0
  50. package/dist/data/compression/uri_compressible_asset.d.ts.map +1 -0
  51. package/dist/data/compression/uri_compressible_asset.js +77 -0
  52. package/dist/data/compression/uri_compressible_asset.js.map +1 -0
  53. package/dist/data/dsl/builder/cache_config_builder.d.ts +2 -1
  54. package/dist/data/dsl/builder/cache_config_builder.d.ts.map +1 -1
  55. package/dist/data/dsl/builder/cache_config_builder.js +2 -1
  56. package/dist/data/dsl/builder/cache_config_builder.js.map +1 -1
  57. package/dist/data/dsl/builder/config_builder.d.ts +40 -14
  58. package/dist/data/dsl/builder/config_builder.d.ts.map +1 -1
  59. package/dist/data/dsl/builder/config_builder.js +40 -14
  60. package/dist/data/dsl/builder/config_builder.js.map +1 -1
  61. package/dist/data/dsl/builder/interceptor_config_builder.d.ts +9 -7
  62. package/dist/data/dsl/builder/interceptor_config_builder.d.ts.map +1 -1
  63. package/dist/data/dsl/builder/interceptor_config_builder.js +9 -7
  64. package/dist/data/dsl/builder/interceptor_config_builder.js.map +1 -1
  65. package/dist/data/dsl/builder/retry_config_builder.d.ts +16 -1
  66. package/dist/data/dsl/builder/retry_config_builder.d.ts.map +1 -1
  67. package/dist/data/dsl/builder/retry_config_builder.js +16 -1
  68. package/dist/data/dsl/builder/retry_config_builder.js.map +1 -1
  69. package/dist/data/dsl/builder/timeout_config_builder.d.ts +4 -0
  70. package/dist/data/dsl/builder/timeout_config_builder.d.ts.map +1 -1
  71. package/dist/data/dsl/builder/timeout_config_builder.js +4 -0
  72. package/dist/data/dsl/builder/timeout_config_builder.js.map +1 -1
  73. package/dist/data/dsl/use_smile_id_network_builder.d.ts +17 -0
  74. package/dist/data/dsl/use_smile_id_network_builder.d.ts.map +1 -1
  75. package/dist/data/dsl/use_smile_id_network_builder.js +17 -0
  76. package/dist/data/dsl/use_smile_id_network_builder.js.map +1 -1
  77. package/dist/data/factory/networking_factory.d.ts.map +1 -1
  78. package/dist/data/factory/networking_factory.js +11 -1
  79. package/dist/data/factory/networking_factory.js.map +1 -1
  80. package/dist/data/repository/use_smile_id_repository_impl.d.ts +6 -0
  81. package/dist/data/repository/use_smile_id_repository_impl.d.ts.map +1 -1
  82. package/dist/data/repository/use_smile_id_repository_impl.js +82 -8
  83. package/dist/data/repository/use_smile_id_repository_impl.js.map +1 -1
  84. package/dist/data/security/request_canonicalizer.d.ts.map +1 -1
  85. package/dist/data/security/request_canonicalizer.js +14 -2
  86. package/dist/data/security/request_canonicalizer.js.map +1 -1
  87. package/dist/data/security/security_policy.d.ts +43 -0
  88. package/dist/data/security/security_policy.d.ts.map +1 -0
  89. package/dist/data/security/security_policy.js +61 -0
  90. package/dist/data/security/security_policy.js.map +1 -0
  91. package/dist/data/util/retry_util.d.ts +1 -1
  92. package/dist/data/util/retry_util.d.ts.map +1 -1
  93. package/dist/data/util/retry_util.js +2 -1
  94. package/dist/data/util/retry_util.js.map +1 -1
  95. package/dist/data/util/use_smile_id_token_decoder.d.ts +22 -0
  96. package/dist/data/util/use_smile_id_token_decoder.d.ts.map +1 -1
  97. package/dist/data/util/use_smile_id_token_decoder.js +58 -24
  98. package/dist/data/util/use_smile_id_token_decoder.js.map +1 -1
  99. package/dist/l10n/strings_en.d.ts +2 -1
  100. package/dist/l10n/strings_en.d.ts.map +1 -1
  101. package/dist/l10n/strings_en.js +2 -1
  102. package/dist/l10n/strings_en.js.map +1 -1
  103. package/dist/l10n/use_smile_id_localizations.d.ts +1 -0
  104. package/dist/l10n/use_smile_id_localizations.d.ts.map +1 -1
  105. package/dist/l10n/use_smile_id_localizations.js +3 -0
  106. package/dist/l10n/use_smile_id_localizations.js.map +1 -1
  107. package/dist/metadata/builder_configuration.d.ts +39 -0
  108. package/dist/metadata/builder_configuration.d.ts.map +1 -0
  109. package/dist/metadata/builder_configuration.js +34 -0
  110. package/dist/metadata/builder_configuration.js.map +1 -0
  111. package/dist/metadata/captured_image_properties.d.ts +11 -0
  112. package/dist/metadata/captured_image_properties.d.ts.map +1 -0
  113. package/dist/metadata/captured_image_properties.js +11 -0
  114. package/dist/metadata/captured_image_properties.js.map +1 -0
  115. package/dist/metadata/device_orientation_collector.d.ts +17 -0
  116. package/dist/metadata/device_orientation_collector.d.ts.map +1 -0
  117. package/dist/metadata/device_orientation_collector.js +66 -0
  118. package/dist/metadata/device_orientation_collector.js.map +1 -0
  119. package/dist/metadata/geolocation.d.ts +20 -0
  120. package/dist/metadata/geolocation.d.ts.map +1 -0
  121. package/dist/metadata/geolocation.js +40 -0
  122. package/dist/metadata/geolocation.js.map +1 -0
  123. package/dist/metadata/metadata_configuration_mappers.d.ts +48 -0
  124. package/dist/metadata/metadata_configuration_mappers.d.ts.map +1 -0
  125. package/dist/metadata/metadata_configuration_mappers.js +171 -0
  126. package/dist/metadata/metadata_configuration_mappers.js.map +1 -0
  127. package/dist/metadata/model_configuration.d.ts +41 -0
  128. package/dist/metadata/model_configuration.d.ts.map +1 -0
  129. package/dist/metadata/model_configuration.js +44 -0
  130. package/dist/metadata/model_configuration.js.map +1 -0
  131. package/dist/metadata/optional_native_modules.d.ts +79 -3
  132. package/dist/metadata/optional_native_modules.d.ts.map +1 -1
  133. package/dist/metadata/optional_native_modules.js +108 -3
  134. package/dist/metadata/optional_native_modules.js.map +1 -1
  135. package/dist/metadata/use_smile_id_metadata.d.ts +45 -7
  136. package/dist/metadata/use_smile_id_metadata.d.ts.map +1 -1
  137. package/dist/metadata/use_smile_id_metadata.js +85 -13
  138. package/dist/metadata/use_smile_id_metadata.js.map +1 -1
  139. package/dist/metadata/use_smile_id_metadata_factory.d.ts +37 -0
  140. package/dist/metadata/use_smile_id_metadata_factory.d.ts.map +1 -1
  141. package/dist/metadata/use_smile_id_metadata_factory.js +184 -12
  142. package/dist/metadata/use_smile_id_metadata_factory.js.map +1 -1
  143. package/dist/presentation/assets/svg/document_shimmer_strings.d.ts +14 -0
  144. package/dist/presentation/assets/svg/document_shimmer_strings.d.ts.map +1 -0
  145. package/dist/presentation/assets/svg/document_shimmer_strings.js +14 -0
  146. package/dist/presentation/assets/svg/document_shimmer_strings.js.map +1 -0
  147. package/dist/presentation/assets/svg/instruction_svg_strings.d.ts +12 -4
  148. package/dist/presentation/assets/svg/instruction_svg_strings.d.ts.map +1 -1
  149. package/dist/presentation/assets/svg/instruction_svg_strings.js +19 -8
  150. package/dist/presentation/assets/svg/instruction_svg_strings.js.map +1 -1
  151. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts +0 -5
  152. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts.map +1 -1
  153. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js +0 -5
  154. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js.map +1 -1
  155. package/dist/presentation/components/icons/index.d.ts +24 -8
  156. package/dist/presentation/components/icons/index.d.ts.map +1 -1
  157. package/dist/presentation/components/icons/index.js +31 -11
  158. package/dist/presentation/components/icons/index.js.map +1 -1
  159. package/dist/presentation/components/use_smile_id_attribution.d.ts +1 -1
  160. package/dist/presentation/components/use_smile_id_button.d.ts +2 -2
  161. package/dist/presentation/components/use_smile_id_button.js +4 -4
  162. package/dist/presentation/components/use_smile_id_button.js.map +1 -1
  163. package/dist/presentation/components/use_smile_id_capture_content.d.ts +13 -0
  164. package/dist/presentation/components/use_smile_id_capture_content.d.ts.map +1 -1
  165. package/dist/presentation/components/use_smile_id_capture_content.js +15 -9
  166. package/dist/presentation/components/use_smile_id_capture_content.js.map +1 -1
  167. package/dist/presentation/components/use_smile_id_captured_document_image.d.ts +2 -3
  168. package/dist/presentation/components/use_smile_id_captured_document_image.d.ts.map +1 -1
  169. package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -3
  170. package/dist/presentation/components/use_smile_id_captured_document_image.js.map +1 -1
  171. package/dist/presentation/components/use_smile_id_document_capture_controls.d.ts +12 -6
  172. package/dist/presentation/components/use_smile_id_document_capture_controls.d.ts.map +1 -1
  173. package/dist/presentation/components/use_smile_id_document_capture_controls.js +59 -16
  174. package/dist/presentation/components/use_smile_id_document_capture_controls.js.map +1 -1
  175. package/dist/presentation/components/use_smile_id_document_overlay.d.ts +22 -5
  176. package/dist/presentation/components/use_smile_id_document_overlay.d.ts.map +1 -1
  177. package/dist/presentation/components/use_smile_id_document_overlay.js +80 -40
  178. package/dist/presentation/components/use_smile_id_document_overlay.js.map +1 -1
  179. package/dist/presentation/components/use_smile_id_document_shimmer.d.ts +15 -0
  180. package/dist/presentation/components/use_smile_id_document_shimmer.d.ts.map +1 -0
  181. package/dist/presentation/components/use_smile_id_document_shimmer.js +23 -0
  182. package/dist/presentation/components/use_smile_id_document_shimmer.js.map +1 -0
  183. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts.map +1 -1
  184. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +1 -3
  185. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js.map +1 -1
  186. package/dist/presentation/components/use_smile_id_guideline_thumbnail.d.ts.map +1 -1
  187. package/dist/presentation/components/use_smile_id_guideline_thumbnail.js +84 -1
  188. package/dist/presentation/components/use_smile_id_guideline_thumbnail.js.map +1 -1
  189. package/dist/presentation/components/use_smile_id_instructions_hero.d.ts +3 -1
  190. package/dist/presentation/components/use_smile_id_instructions_hero.d.ts.map +1 -1
  191. package/dist/presentation/components/use_smile_id_instructions_hero.js +18 -3
  192. package/dist/presentation/components/use_smile_id_instructions_hero.js.map +1 -1
  193. package/dist/presentation/components/use_smile_id_oval_border_palette.d.ts.map +1 -1
  194. package/dist/presentation/components/use_smile_id_oval_border_palette.js +7 -3
  195. package/dist/presentation/components/use_smile_id_oval_border_palette.js.map +1 -1
  196. package/dist/presentation/flow/dsl/builder/config_builder.d.ts +12 -0
  197. package/dist/presentation/flow/dsl/builder/config_builder.d.ts.map +1 -1
  198. package/dist/presentation/flow/dsl/builder/config_builder.js +12 -0
  199. package/dist/presentation/flow/dsl/builder/config_builder.js.map +1 -1
  200. package/dist/presentation/flow/dsl/builder/ml_builder.d.ts +13 -4
  201. package/dist/presentation/flow/dsl/builder/ml_builder.d.ts.map +1 -1
  202. package/dist/presentation/flow/dsl/builder/ml_builder.js +3 -2
  203. package/dist/presentation/flow/dsl/builder/ml_builder.js.map +1 -1
  204. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +111 -10
  205. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts.map +1 -1
  206. package/dist/presentation/flow/dsl/builder/screens_builder.js +111 -10
  207. package/dist/presentation/flow/dsl/builder/screens_builder.js.map +1 -1
  208. package/dist/presentation/flow/dsl/builder/theme_builder.d.ts +15 -2
  209. package/dist/presentation/flow/dsl/builder/theme_builder.d.ts.map +1 -1
  210. package/dist/presentation/flow/dsl/builder/theme_builder.js +15 -2
  211. package/dist/presentation/flow/dsl/builder/theme_builder.js.map +1 -1
  212. package/dist/presentation/flow/dsl/use_smile_id_builder.d.ts +6 -4
  213. package/dist/presentation/flow/dsl/use_smile_id_builder.d.ts.map +1 -1
  214. package/dist/presentation/flow/dsl/use_smile_id_builder.js.map +1 -1
  215. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +62 -36
  216. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts.map +1 -1
  217. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +62 -36
  218. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js.map +1 -1
  219. package/dist/presentation/flow/flow_navigation_manager.d.ts +39 -4
  220. package/dist/presentation/flow/flow_navigation_manager.d.ts.map +1 -1
  221. package/dist/presentation/flow/flow_navigation_manager.js +141 -10
  222. package/dist/presentation/flow/flow_navigation_manager.js.map +1 -1
  223. package/dist/presentation/flow/model/instruction_guideline.d.ts +9 -0
  224. package/dist/presentation/flow/model/instruction_guideline.d.ts.map +1 -1
  225. package/dist/presentation/flow/model/instructions_defaults.d.ts +11 -2
  226. package/dist/presentation/flow/model/instructions_defaults.d.ts.map +1 -1
  227. package/dist/presentation/flow/model/instructions_defaults.js +73 -25
  228. package/dist/presentation/flow/model/instructions_defaults.js.map +1 -1
  229. package/dist/presentation/flow/screen/orchestrated_capture_screen.d.ts.map +1 -1
  230. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +36 -12
  231. package/dist/presentation/flow/screen/orchestrated_capture_screen.js.map +1 -1
  232. package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
  233. package/dist/presentation/flow/screen/orchestrated_instructions_screen.js.map +1 -1
  234. package/dist/presentation/flow/screen/orchestrated_preview_screen.d.ts.map +1 -1
  235. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +3 -1
  236. package/dist/presentation/flow/screen/orchestrated_preview_screen.js.map +1 -1
  237. package/dist/presentation/flow/screen/orchestrated_processing_screen.js +3 -3
  238. package/dist/presentation/flow/screen/orchestrated_processing_screen.js.map +1 -1
  239. package/dist/presentation/flow/validation/job_type_validator.d.ts.map +1 -1
  240. package/dist/presentation/flow/validation/job_type_validator.js +14 -0
  241. package/dist/presentation/flow/validation/job_type_validator.js.map +1 -1
  242. package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts.map +1 -1
  243. package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -0
  244. package/dist/presentation/screen/helpers/capture_screen_helpers.js.map +1 -1
  245. package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts +1 -0
  246. package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts.map +1 -1
  247. package/dist/presentation/screen/helpers/instructions_screen_helpers.js +6 -2
  248. package/dist/presentation/screen/helpers/instructions_screen_helpers.js.map +1 -1
  249. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js +3 -0
  250. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js.map +1 -1
  251. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +17 -1
  252. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts.map +1 -1
  253. package/dist/presentation/screen/use_smile_id_capture_screen.js +153 -18
  254. package/dist/presentation/screen/use_smile_id_capture_screen.js.map +1 -1
  255. package/dist/presentation/screen/use_smile_id_document_preview_screen.d.ts +2 -3
  256. package/dist/presentation/screen/use_smile_id_document_preview_screen.d.ts.map +1 -1
  257. package/dist/presentation/screen/use_smile_id_document_preview_screen.js +16 -11
  258. package/dist/presentation/screen/use_smile_id_document_preview_screen.js.map +1 -1
  259. package/dist/presentation/screen/use_smile_id_instructions_screen.d.ts +2 -0
  260. package/dist/presentation/screen/use_smile_id_instructions_screen.d.ts.map +1 -1
  261. package/dist/presentation/screen/use_smile_id_instructions_screen.js +2 -1
  262. package/dist/presentation/screen/use_smile_id_instructions_screen.js.map +1 -1
  263. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +7 -6
  264. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts.map +1 -1
  265. package/dist/presentation/screen/use_smile_id_preview_screen.js +9 -8
  266. package/dist/presentation/screen/use_smile_id_preview_screen.js.map +1 -1
  267. package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +2 -3
  268. package/dist/presentation/screen/use_smile_id_processing_screen.d.ts.map +1 -1
  269. package/dist/presentation/screen/use_smile_id_processing_screen.js +3 -2
  270. package/dist/presentation/screen/use_smile_id_processing_screen.js.map +1 -1
  271. package/dist/presentation/theme/theme.d.ts +1 -1
  272. package/dist/presentation/theme/theme.d.ts.map +1 -1
  273. package/dist/presentation/theme/theme.js +20 -18
  274. package/dist/presentation/theme/theme.js.map +1 -1
  275. package/dist/presentation/utils/force_max_brightness.d.ts +4 -4
  276. package/package.json +26 -6
  277. package/src/__tests__/react_native_mock.tsx +8 -0
  278. package/src/camera/camera_defaults.ts +11 -9
  279. package/src/camera/orientation_degrees.ts +5 -6
  280. package/src/core/models/__tests__/document_type.test.ts +6 -4
  281. package/src/core/models/document_capture_mode.ts +27 -0
  282. package/src/core/models/document_type.ts +6 -4
  283. package/src/core/models/result.ts +9 -8
  284. package/src/data/api/interceptors/__tests__/smile_integrity_interceptor.test.ts +37 -1
  285. package/src/data/api/interceptors/__tests__/smile_request_mac_interceptor.test.ts +59 -1
  286. package/src/data/api/interceptors/smile_auth_interceptor.ts +1 -1
  287. package/src/data/api/interceptors/smile_integrity_interceptor.ts +20 -0
  288. package/src/data/api/interceptors/smile_request_mac_interceptor.ts +13 -0
  289. package/src/data/compression/__tests__/payload_compressor.test.ts +200 -0
  290. package/src/data/compression/__tests__/payload_compressor_real_jpeg.test.ts +222 -0
  291. package/src/data/compression/compressible_asset.ts +62 -0
  292. package/src/data/compression/payload_compression_config.ts +53 -0
  293. package/src/data/compression/payload_compression_runner.ts +102 -0
  294. package/src/data/compression/payload_compressor.ts +164 -0
  295. package/src/data/compression/uri_compressible_asset.ts +85 -0
  296. package/src/data/dsl/builder/cache_config_builder.ts +3 -1
  297. package/src/data/dsl/builder/config_builder.ts +45 -14
  298. package/src/data/dsl/builder/interceptor_config_builder.ts +9 -7
  299. package/src/data/dsl/builder/retry_config_builder.ts +22 -1
  300. package/src/data/dsl/builder/timeout_config_builder.ts +7 -0
  301. package/src/data/dsl/use_smile_id_network_builder.ts +17 -0
  302. package/src/data/factory/networking_factory.ts +14 -1
  303. package/src/data/repository/__tests__/network_retries.test.ts +117 -0
  304. package/src/data/repository/use_smile_id_repository_impl.ts +96 -9
  305. package/src/data/security/__tests__/security_policy.test.ts +96 -0
  306. package/src/data/security/__tests__/smile_id_crypto_manager.test.ts +18 -0
  307. package/src/data/security/request_canonicalizer.ts +15 -2
  308. package/src/data/security/security_policy.ts +70 -0
  309. package/src/data/util/__tests__/use_smile_id_token_decoder.test.ts +57 -1
  310. package/src/data/util/retry_util.ts +3 -2
  311. package/src/data/util/use_smile_id_token_decoder.ts +76 -28
  312. package/src/l10n/strings_en.ts +2 -1
  313. package/src/l10n/use_smile_id_localizations.ts +3 -0
  314. package/src/metadata/__tests__/device_orientation_collector.test.ts +15 -0
  315. package/src/metadata/__tests__/metadata_configuration_mappers.test.ts +215 -0
  316. package/src/metadata/__tests__/metadata_configuration_objects.test.ts +85 -0
  317. package/src/metadata/__tests__/metadata_device_context.test.ts +147 -0
  318. package/src/metadata/__tests__/metadata_golden_fixture.json +13 -0
  319. package/src/metadata/__tests__/metadata_payload_golden.test.ts +193 -0
  320. package/src/metadata/__tests__/use_smile_id_metadata.test.ts +18 -24
  321. package/src/metadata/builder_configuration.ts +63 -0
  322. package/src/metadata/captured_image_properties.ts +10 -0
  323. package/src/metadata/device_orientation_collector.ts +75 -0
  324. package/src/metadata/geolocation.ts +46 -0
  325. package/src/metadata/metadata_configuration_mappers.ts +234 -0
  326. package/src/metadata/model_configuration.ts +68 -0
  327. package/src/metadata/optional_native_modules.ts +157 -3
  328. package/src/metadata/use_smile_id_metadata.ts +140 -17
  329. package/src/metadata/use_smile_id_metadata_factory.ts +195 -11
  330. package/src/presentation/assets/si_ess_animation_all_directions.json +1300 -286
  331. package/src/presentation/assets/svg/document_shimmer_strings.ts +17 -0
  332. package/src/presentation/assets/svg/instruction_svg_strings.ts +34 -12
  333. package/src/presentation/components/__tests__/enhanced_smart_selfie_animation_frames.test.ts +1 -3
  334. package/src/presentation/components/__tests__/use_smile_id_enhanced_smart_selfie_clip.test.tsx +3 -4
  335. package/src/presentation/components/__tests__/use_smile_id_guideline_thumbnail.test.tsx +34 -1
  336. package/src/presentation/components/__tests__/use_smile_id_instructions_hero.test.tsx +27 -0
  337. package/src/presentation/components/__tests__/use_smile_id_oval_border_palette.test.ts +7 -5
  338. package/src/presentation/components/helpers/enhanced_smart_selfie_animation_frames.ts +0 -6
  339. package/src/presentation/components/icons/index.tsx +65 -18
  340. package/src/presentation/components/use_smile_id_attribution.tsx +1 -1
  341. package/src/presentation/components/use_smile_id_button.tsx +4 -4
  342. package/src/presentation/components/use_smile_id_capture_content.tsx +36 -12
  343. package/src/presentation/components/use_smile_id_captured_document_image.tsx +3 -8
  344. package/src/presentation/components/use_smile_id_document_capture_controls.tsx +103 -39
  345. package/src/presentation/components/use_smile_id_document_overlay.tsx +134 -44
  346. package/src/presentation/components/use_smile_id_document_shimmer.tsx +51 -0
  347. package/src/presentation/components/use_smile_id_enhanced_smart_selfie_clip.tsx +0 -3
  348. package/src/presentation/components/use_smile_id_guideline_thumbnail.tsx +131 -2
  349. package/src/presentation/components/use_smile_id_instructions_hero.tsx +100 -59
  350. package/src/presentation/components/use_smile_id_oval_border_palette.ts +7 -3
  351. package/src/presentation/flow/__tests__/document_flow_navigation.test.ts +200 -1
  352. package/src/presentation/flow/dsl/builder/config_builder.ts +14 -0
  353. package/src/presentation/flow/dsl/builder/ml_builder.ts +13 -4
  354. package/src/presentation/flow/dsl/builder/screens_builder.ts +111 -10
  355. package/src/presentation/flow/dsl/builder/theme_builder.ts +19 -2
  356. package/src/presentation/flow/dsl/use_smile_id_builder.tsx +6 -4
  357. package/src/presentation/flow/dsl/use_smile_id_flow_builder.ts +62 -36
  358. package/src/presentation/flow/flow_navigation_manager.ts +179 -13
  359. package/src/presentation/flow/model/instruction_guideline.ts +6 -0
  360. package/src/presentation/flow/model/instructions_defaults.ts +103 -25
  361. package/src/presentation/flow/screen/orchestrated_capture_screen.tsx +43 -10
  362. package/src/presentation/flow/screen/orchestrated_instructions_screen.tsx +1 -0
  363. package/src/presentation/flow/screen/orchestrated_preview_screen.tsx +4 -2
  364. package/src/presentation/flow/screen/orchestrated_processing_screen.tsx +3 -3
  365. package/src/presentation/flow/validation/__tests__/job_type_validator.test.ts +64 -0
  366. package/src/presentation/flow/validation/job_type_validator.ts +24 -0
  367. package/src/presentation/screen/__tests__/instructions_screen_helpers.test.ts +15 -3
  368. package/src/presentation/screen/__tests__/use_smile_id_capture_screen.test.tsx +6 -4
  369. package/src/presentation/screen/helpers/capture_screen_helpers.ts +1 -0
  370. package/src/presentation/screen/helpers/instructions_screen_helpers.ts +7 -2
  371. package/src/presentation/screen/helpers/scan_state_to_overlay_state.ts +3 -0
  372. package/src/presentation/screen/use_smile_id_capture_screen.tsx +234 -36
  373. package/src/presentation/screen/use_smile_id_document_preview_screen.tsx +32 -22
  374. package/src/presentation/screen/use_smile_id_instructions_screen.tsx +4 -0
  375. package/src/presentation/screen/use_smile_id_preview_screen.tsx +9 -9
  376. package/src/presentation/screen/use_smile_id_processing_screen.tsx +7 -7
  377. package/src/presentation/theme/theme.ts +21 -19
  378. package/src/presentation/utils/force_max_brightness.tsx +4 -4
  379. package/dist/camera/frame_processor_plugin.d.ts +0 -28
  380. package/dist/camera/frame_processor_plugin.d.ts.map +0 -1
  381. package/dist/camera/frame_processor_plugin.js +0 -21
  382. package/dist/camera/frame_processor_plugin.js.map +0 -1
  383. package/dist/camera/use_smile_id_frame_processor.d.ts +0 -38
  384. package/dist/camera/use_smile_id_frame_processor.d.ts.map +0 -1
  385. package/dist/camera/use_smile_id_frame_processor.js +0 -2
  386. package/dist/camera/use_smile_id_frame_processor.js.map +0 -1
  387. package/dist/presentation/components/use_smile_id_document_frame_glyph.d.ts +0 -17
  388. package/dist/presentation/components/use_smile_id_document_frame_glyph.d.ts.map +0 -1
  389. package/dist/presentation/components/use_smile_id_document_frame_glyph.js +0 -17
  390. package/dist/presentation/components/use_smile_id_document_frame_glyph.js.map +0 -1
  391. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.d.ts +0 -24
  392. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.d.ts.map +0 -1
  393. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.js +0 -91
  394. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.js.map +0 -1
  395. package/dist/presentation/components/use_smile_id_instruction_card.d.ts +0 -31
  396. package/dist/presentation/components/use_smile_id_instruction_card.d.ts.map +0 -1
  397. package/dist/presentation/components/use_smile_id_instruction_card.js +0 -43
  398. package/dist/presentation/components/use_smile_id_instruction_card.js.map +0 -1
  399. package/dist/presentation/screen/helpers/use_document_orientation_lock.d.ts +0 -24
  400. package/dist/presentation/screen/helpers/use_document_orientation_lock.d.ts.map +0 -1
  401. package/dist/presentation/screen/helpers/use_document_orientation_lock.js +0 -47
  402. package/dist/presentation/screen/helpers/use_document_orientation_lock.js.map +0 -1
  403. package/src/presentation/components/use_smile_id_document_frame_glyph.tsx +0 -162
@@ -453,6 +453,30 @@ export class DocumentJobTypeValidator implements JobTypeValidator {
453
453
 
454
454
  this.appendParamsRule(input, issues);
455
455
 
456
+ // The South Africa Green Book carries no usable data on its back and is not
457
+ // supported by the Enhanced Document Verification ID-authority cross check.
458
+ // Reject it for that job type so the partner sees a build-time error instead
459
+ // of a server-side job failure.
460
+ if (
461
+ this.jobType === JobType.enhancedDocumentVerification &&
462
+ documentCaptures.some(
463
+ (s) =>
464
+ s.kind === "capture" &&
465
+ s.config.documentConfig?.documentType?.kind ===
466
+ "southAfricaGreenBook",
467
+ )
468
+ ) {
469
+ issues.push(
470
+ InvalidJobTypeScreenConfigException.forJob({
471
+ jobType: this.jobType,
472
+ reason:
473
+ "South Africa Green Book is not supported for Enhanced Document Verification",
474
+ suggestedFix:
475
+ "Use JobType.documentVerification for the Green Book, or choose a supported document type for Enhanced Document Verification",
476
+ }),
477
+ );
478
+ }
479
+
456
480
  if (input.userDetails === undefined) {
457
481
  issues.push(
458
482
  InvalidJobTypeScreenConfigException.forJob({
@@ -6,7 +6,9 @@ import {
6
6
  } from "../helpers/instructions_screen_helpers";
7
7
  import {
8
8
  DOCUMENT_GREEN_BOOK_HERO_ANIMATION,
9
- DOCUMENT_GUIDELINES,
9
+ DOCUMENT_GUIDELINES_GENERIC,
10
+ DOCUMENT_GUIDELINES_GREEN_BOOK,
11
+ DOCUMENT_GUIDELINES_PASSPORT,
10
12
  DOCUMENT_HERO_ANIMATION,
11
13
  ESS_HERO_ANIMATION,
12
14
  SELFIE_GUIDELINES,
@@ -85,7 +87,7 @@ describe("resolveInstructionsConfig — document variant", () => {
85
87
  loc,
86
88
  );
87
89
  expect(resolved.titleEmphasis).toBe("Green Book");
88
- expect(resolved.guidelines).toBe(DOCUMENT_GUIDELINES);
90
+ expect(resolved.guidelines).toBe(DOCUMENT_GUIDELINES_GREEN_BOOK);
89
91
  expect(resolved.heroSource).toBe(DOCUMENT_GREEN_BOOK_HERO_ANIMATION);
90
92
  expect(resolved.showHeroOval).toBe(false);
91
93
  expect(resolved.showHeroOvalProgress).toBe(false);
@@ -93,7 +95,7 @@ describe("resolveInstructionsConfig — document variant", () => {
93
95
  expect(resolved.titlePrefix).toBe(loc.siInstructionsTitlePrefix);
94
96
  });
95
97
 
96
- it("falls back to the generic document hero for GenericDocument", () => {
98
+ it("falls back to the generic document hero + guidelines for GenericDocument", () => {
97
99
  const resolved = resolveInstructionsConfig(
98
100
  {},
99
101
  {
@@ -103,9 +105,19 @@ describe("resolveInstructionsConfig — document variant", () => {
103
105
  loc,
104
106
  );
105
107
  expect(resolved.heroSource).toBe(DOCUMENT_HERO_ANIMATION);
108
+ expect(resolved.guidelines).toBe(DOCUMENT_GUIDELINES_GENERIC);
106
109
  expect(resolved.titleEmphasis).toBe("Document");
107
110
  });
108
111
 
112
+ it("uses the passport guidelines for a passport document", () => {
113
+ const resolved = resolveInstructionsConfig(
114
+ {},
115
+ { enhancedLivenessEnabled: false, documentType: DocumentType.Passport },
116
+ loc,
117
+ );
118
+ expect(resolved.guidelines).toBe(DOCUMENT_GUIDELINES_PASSPORT);
119
+ });
120
+
109
121
  it("respects partner title/guideline overrides but always hides the oval for documents", () => {
110
122
  const customGuidelines: InstructionGuideline[] = [SELFIE_GUIDELINES[0]!];
111
123
  const resolved = resolveInstructionsConfig(
@@ -534,10 +534,12 @@ describe("UseSmileIDCaptureScreen", () => {
534
534
 
535
535
  expect(onCaptureComplete).toHaveBeenCalledTimes(1);
536
536
  // Only the post-reset events survive — the stale ones were cleared.
537
- expect(onCaptureComplete).toHaveBeenCalledWith([
538
- freshSelfie,
539
- freshLiveness,
540
- ]);
537
+ // The capture telemetry (duration + retries) travels as a second argument;
538
+ // the retry count reflects the one unsatisfied reset observed above.
539
+ expect(onCaptureComplete).toHaveBeenCalledWith(
540
+ [freshSelfie, freshLiveness],
541
+ expect.objectContaining({ retries: 1 }),
542
+ );
541
543
  });
542
544
  });
543
545
 
@@ -93,6 +93,7 @@ const FEEDBACK_COPY: Partial<
93
93
  si_ess_look_left: (loc) => loc.siEssLookLeft,
94
94
  si_ess_look_right: (loc) => loc.siEssLookRight,
95
95
  si_ess_look_up: (loc) => loc.siEssLookUp,
96
+ si_ess_look_at_camera: (loc) => loc.siEssLookAtCamera,
96
97
  si_document_initial_instruction: (loc) => loc.siDocumentInitialInstruction,
97
98
  si_document_ready: (loc) => loc.siDocumentReady,
98
99
  si_document_capturing: (loc) => loc.siDocumentCapturing,
@@ -2,7 +2,7 @@ import type { UseSmileIDLocalizations } from "../../../l10n/use_smile_id_localiz
2
2
  import type { DocumentType } from "../../../core/models/document_type";
3
3
  import type { InstructionGuideline } from "../../flow/model/instruction_guideline";
4
4
  import {
5
- DOCUMENT_GUIDELINES,
5
+ documentGuidelines,
6
6
  documentHeroAnimation,
7
7
  ESS_HERO_ANIMATION,
8
8
  SELFIE_GUIDELINES,
@@ -33,6 +33,7 @@ export type ResolvedInstructions = {
33
33
  readonly showAttribution: boolean;
34
34
  readonly showHeroOval: boolean;
35
35
  readonly showHeroOvalProgress: boolean;
36
+ readonly showCardBackground: boolean;
36
37
  };
37
38
 
38
39
  /**
@@ -74,7 +75,7 @@ export function resolveInstructionsConfig(
74
75
  const isDocument = documentType !== undefined;
75
76
 
76
77
  const defaultGuidelines = isDocument
77
- ? DOCUMENT_GUIDELINES
78
+ ? documentGuidelines(documentType)
78
79
  : SELFIE_GUIDELINES;
79
80
  const guidelines =
80
81
  config.guidelines && config.guidelines.length > 0
@@ -104,5 +105,9 @@ export function resolveInstructionsConfig(
104
105
  // selfie flows only (a document has no face to frame).
105
106
  showHeroOval: isDocument ? false : (config.showHeroOval ?? true),
106
107
  showHeroOvalProgress: isDocument ? false : !enhanced,
108
+ // Card hero is the document variant only — tied to the flow type, not to
109
+ // `!showHeroOval` (a selfie flow can disable the oval without becoming a
110
+ // document, and must not then render the document card).
111
+ showCardBackground: isDocument,
107
112
  };
108
113
  }
@@ -104,6 +104,9 @@ function resolveFeedbackOverlay(
104
104
  return UseSmileIDOverlayState.MOVE_UP;
105
105
  case "si_capture_move_lower":
106
106
  return UseSmileIDOverlayState.MOVE_DOWN;
107
+ // ESS re-center gate between head-turn directions
108
+ case "si_ess_look_at_camera":
109
+ return UseSmileIDOverlayState.FACE_NOT_CENTERED;
107
110
  default:
108
111
  return fallback;
109
112
  }
@@ -6,7 +6,13 @@ import {
6
6
  useState,
7
7
  type ReactNode,
8
8
  } from "react";
9
- import { StatusBar, StyleSheet, View } from "react-native";
9
+ import {
10
+ Platform,
11
+ StatusBar,
12
+ StyleSheet,
13
+ View,
14
+ useWindowDimensions,
15
+ } from "react-native";
10
16
  import {
11
17
  Camera,
12
18
  useCameraDevice,
@@ -30,17 +36,24 @@ import {
30
36
  } from "@smileid/usesmileid_bridge";
31
37
  import { CaptureType } from "../../core/models/ml_config";
32
38
  import type { DocumentCaptureMode } from "../../core/models/document_capture_mode";
39
+ import type { DocumentType } from "../../core/models/document_type";
33
40
  import { resampleImportedDocument } from "../../camera/document_image_pipeline";
34
41
  import { defaultLightTheme, type UseSmileIDTheme } from "../theme/theme";
42
+ import { UseSmileIDThemeProvider } from "../theme/theme_context";
35
43
  import {
36
44
  UseSmileIDCaptureContent,
37
45
  type UseSmileIDCaptureContentState,
38
46
  } from "../components/use_smile_id_capture_content";
47
+ import {
48
+ DEFAULT_DOCUMENT_ASPECT,
49
+ documentGuideRect,
50
+ } from "../components/use_smile_id_document_overlay";
39
51
  import { UseSmileIDCrashReporting } from "@smileid/usesmileid_bridge";
40
52
  import { ForceMaxBrightness } from "../utils/force_max_brightness";
41
53
  import { CameraDefaults } from "../../camera/camera_defaults";
42
54
  import type { SiKey } from "../../l10n/strings_en";
43
55
  import { DocumentImageOrigin } from "../../metadata/document_image_origin";
56
+ import { DeviceOrientationCollector } from "../../metadata/device_orientation_collector";
44
57
  import { useSmileIDFrameStream } from "../../camera/use_smile_id_frame_stream";
45
58
  import { orientationToDegrees } from "../../camera/orientation_degrees";
46
59
  import {
@@ -55,6 +68,20 @@ import {
55
68
  triggerEssTurnHaptic,
56
69
  } from "./helpers/ess_turn_haptics";
57
70
 
71
+ /** Capture telemetry surfaced alongside the captured artifacts at completion. */
72
+ export interface CaptureTelemetry {
73
+ /** Elapsed time in ms from the first analyzed frame to completion. */
74
+ readonly durationMs: number;
75
+ /** Count of face/document-lost resets (rising edges of `unsatisfied`). */
76
+ readonly retries: number;
77
+ /** Identifier of the camera used for capture (`CameraDevice.id`); `undefined` until resolved. */
78
+ readonly cameraName?: string;
79
+ /** Accelerometer `"epochMs,x,y,z"` samples collected during capture. */
80
+ readonly devicePositions: readonly string[];
81
+ /** Span of movement magnitude during capture; `null` when no samples ran. */
82
+ readonly deviceMovementDetected: number | null;
83
+ }
84
+
58
85
  export type UseSmileIDCaptureScreenProps = {
59
86
  readonly captureType: CaptureType;
60
87
 
@@ -73,7 +100,10 @@ export type UseSmileIDCaptureScreenProps = {
73
100
  * settled, with every `CaptureReadyEvent` of the session (one
74
101
  * `"selfie"` + N `"liveness"` for face, one `"document"` for document).
75
102
  */
76
- readonly onCaptureComplete?: (events: readonly CaptureReadyEvent[]) => void;
103
+ readonly onCaptureComplete?: (
104
+ events: readonly CaptureReadyEvent[],
105
+ telemetry: CaptureTelemetry,
106
+ ) => void;
77
107
  /** Records how a document side was produced (auto/manual/gallery), fired just before delivery. */
78
108
  readonly onDocumentOrigin?: (origin: DocumentImageOrigin) => void;
79
109
 
@@ -106,6 +136,8 @@ export type UseSmileIDCaptureScreenProps = {
106
136
  * `document_front.jpg` / `document_back.jpg`. Ignored for selfie capture.
107
137
  */
108
138
  readonly documentSide?: DocumentSide;
139
+ /** Document type — selects the in-frame capture shimmer skeleton. Ignored for selfie. */
140
+ readonly documentTypeKind?: DocumentType["kind"];
109
141
  /** Close/cancel the whole flow (document capture chrome — top-right X). */
110
142
  readonly onClose?: () => void;
111
143
 
@@ -140,15 +172,24 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
140
172
  const cameraRef = useRef<Camera>(null);
141
173
  // Pin an explicit format: the "highest supported" heuristic can pick 4K,
142
174
  // stalling the frame-processor thread and stuttering the pipeline.
175
+ // Document capture uses 16:9 so the analysis FOV matches the visible 16:9
176
+ // preview — keeping the guide-rectangle crop aligned with the framed document
177
+ // (mirrors the native Android SDK's RATIO_16_9 + 1280×720 pin). Selfie keeps
178
+ // 4:3: the face geometry + smile heuristic are calibrated on 4:3, and 16:9
179
+ // skews normalized landmark ratios and shifts every threshold.
180
+ const isDocumentFormat = props.captureType === CaptureType.document;
143
181
  const format = useCameraFormat(device, [
144
- // 4:3 first: geometry checks + smile heuristic are calibrated on 4:3;
145
- // 16:9 skews normalized landmark ratios and shifts every threshold.
146
- { videoAspectRatio: 4 / 3 },
182
+ { videoAspectRatio: isDocumentFormat ? 16 / 9 : 4 / 3 },
147
183
  {
148
- videoResolution: {
149
- width: CameraDefaults.analysisTargetResolution.width,
150
- height: CameraDefaults.analysisTargetResolution.height,
151
- },
184
+ videoResolution: isDocumentFormat
185
+ ? {
186
+ width: CameraDefaults.documentAnalysisResolution.width,
187
+ height: CameraDefaults.documentAnalysisResolution.height,
188
+ }
189
+ : {
190
+ width: CameraDefaults.analysisTargetResolution.width,
191
+ height: CameraDefaults.analysisTargetResolution.height,
192
+ },
152
193
  },
153
194
  { fps: CameraDefaults.analysisTargetFps },
154
195
  ]);
@@ -193,6 +234,33 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
193
234
  // transitions the 1% bucketing skips committing are still seen.
194
235
  const previousScanStateRef = useRef<ScanState | null>(null);
195
236
 
237
+ // Capture telemetry: wall-clock start (first analyzed frame) and a
238
+ // rising-edge `unsatisfied` retry counter, surfaced via onCaptureComplete.
239
+ const captureStartedAtRef = useRef<number | null>(null);
240
+ const captureRetriesRef = useRef<number>(0);
241
+ const wasUnsatisfiedRef = useRef<boolean>(false);
242
+ // Accelerometer sampler for device_positions / device_movement_detected, run only
243
+ // while the live capture loop is streaming.
244
+ const deviceOrientationRef = useRef<DeviceOrientationCollector>(
245
+ new DeviceOrientationCollector(),
246
+ );
247
+
248
+ const captureTelemetry = useCallback(
249
+ (): CaptureTelemetry => ({
250
+ durationMs:
251
+ captureStartedAtRef.current === null
252
+ ? 0
253
+ : Date.now() - captureStartedAtRef.current,
254
+ retries: captureRetriesRef.current,
255
+ devicePositions: deviceOrientationRef.current.devicePositions,
256
+ deviceMovementDetected:
257
+ deviceOrientationRef.current.deviceMovementDetected,
258
+ // Omit (not set to undefined) when unresolved — exactOptionalPropertyTypes.
259
+ ...(device?.id !== undefined ? { cameraName: device.id } : {}),
260
+ }),
261
+ [device],
262
+ );
263
+
196
264
  const onStartCapture = props.onStartCapture;
197
265
  const handleStart = useCallback(() => {
198
266
  if (streaming || completedRef.current) return;
@@ -213,6 +281,23 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
213
281
  const documentSide = props.documentSide ?? DocumentSide.Front;
214
282
  const isDocumentCapture = props.captureType === CaptureType.document;
215
283
 
284
+ // Reset capture telemetry per side so each document side reports its own
285
+ // duration / retries (the screen instance is reused across the front→back flip).
286
+ useEffect(() => {
287
+ captureStartedAtRef.current = null;
288
+ captureRetriesRef.current = 0;
289
+ wasUnsatisfiedRef.current = false;
290
+ }, [documentSide]);
291
+
292
+ // Sample the accelerometer only while the live capture loop streams. start()
293
+ // clears prior samples, so a retry / side switch begins fresh; cleanup stops it.
294
+ useEffect(() => {
295
+ if (streaming) {
296
+ deviceOrientationRef.current.start();
297
+ }
298
+ return () => deviceOrientationRef.current.stop();
299
+ }, [streaming]);
300
+
216
301
  // Reveal the fallback shutter after the delay once streaming begins.
217
302
  // Bound to `streaming`: any capture clears it, tearing this effect down
218
303
  // and clearing the timer, so it can't fire post-capture or leak.
@@ -249,13 +334,16 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
249
334
  documentSide,
250
335
  );
251
336
  onDocumentOrigin?.(DocumentImageOrigin.CameraManualCapture);
252
- onCaptureComplete?.([
253
- {
254
- kind: CaptureReadyEventKind.Document,
255
- captureIndex: 0,
256
- url: processedUri,
257
- },
258
- ]);
337
+ onCaptureComplete?.(
338
+ [
339
+ {
340
+ kind: CaptureReadyEventKind.Document,
341
+ captureIndex: 0,
342
+ url: processedUri,
343
+ },
344
+ ],
345
+ captureTelemetry(),
346
+ );
259
347
  } catch (error) {
260
348
  UseSmileIDCrashReporting.captureException(
261
349
  error instanceof Error ? error : new Error(String(error)),
@@ -318,13 +406,16 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
318
406
  documentSide,
319
407
  );
320
408
  onDocumentOrigin?.(DocumentImageOrigin.Gallery);
321
- onCaptureComplete?.([
322
- {
323
- kind: CaptureReadyEventKind.Document,
324
- captureIndex: 0,
325
- url: processedUri,
326
- },
327
- ]);
409
+ onCaptureComplete?.(
410
+ [
411
+ {
412
+ kind: CaptureReadyEventKind.Document,
413
+ captureIndex: 0,
414
+ url: processedUri,
415
+ },
416
+ ],
417
+ captureTelemetry(),
418
+ );
328
419
  } catch (error) {
329
420
  UseSmileIDCrashReporting.captureException(
330
421
  error instanceof Error ? error : new Error(String(error)),
@@ -379,6 +470,17 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
379
470
  // Unsatisfied = native session reset; drop accumulated events so it
380
471
  // can't leak stale liveness frames. `else` so a reset frame can't
381
472
  // re-add its own stale events on the same pass.
473
+ // Capture telemetry: anchor the start clock on the first analyzed frame,
474
+ // and count rising edges of `unsatisfied` as retries.
475
+ if (captureStartedAtRef.current === null) {
476
+ captureStartedAtRef.current = Date.now();
477
+ }
478
+ const isUnsatisfied = result.state.type === ScanStateType.Unsatisfied;
479
+ if (isUnsatisfied && !wasUnsatisfiedRef.current) {
480
+ captureRetriesRef.current += 1;
481
+ }
482
+ wasUnsatisfiedRef.current = isUnsatisfied;
483
+
382
484
  if (result.state.type === ScanStateType.Unsatisfied) {
383
485
  capturedEventsRef.current = [];
384
486
  } else if (result.capturedEvents.length > 0) {
@@ -420,7 +522,7 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
420
522
  .finally(() => {
421
523
  // No-op for selfie (callback unset).
422
524
  onDocumentOrigin?.(DocumentImageOrigin.CameraAutoCapture);
423
- onCaptureComplete?.(capturedEventsRef.current);
525
+ onCaptureComplete?.(capturedEventsRef.current, captureTelemetry());
424
526
  });
425
527
  } else if (result.state.type === ScanStateType.Timeout) {
426
528
  if (completedRef.current) return;
@@ -487,21 +589,99 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
487
589
  });
488
590
  }, [onFrameResult]);
489
591
 
490
- // Physical device tilt (0 = upright portrait) for the native
491
- // session's upright validator and head-pose math; gravity-sourced,
492
- // never derived from `frame.orientation` (fixed sensor mounting).
592
+ // Physical device tilt (0 = portrait) for the native upright validator /
593
+ // head-pose math; gravity-sourced, never from `frame.orientation`.
493
594
  const [deviceOrientationDegrees, setDeviceOrientationDegrees] = useState(0);
494
595
 
596
+ // Locked INTERFACE orientation (0/90/270) — the axis the preview and guide are
597
+ // drawn against, from VisionCamera's preview orientation, NOT device gravity
598
+ // (the document screen locks the interface while the phone is held flat, so
599
+ // gravity disagrees). iOS uprights the saved document to this axis.
600
+ const [previewOrientationDegrees, setPreviewOrientationDegrees] = useState(0);
601
+
602
+ // VisionCamera derives its iOS preview orientation from the interface
603
+ // orientation but only refreshes it on a PHYSICAL rotation — a programmatic
604
+ // lock (the document landscape lock) never fires that, so the preview stays
605
+ // portrait in the rotated UI. Remounting <Camera> when the interface flips
606
+ // forces a re-read. iOS only — Android's preview already tracks the lock.
607
+ const { width: windowWidth, height: windowHeight } = useWindowDimensions();
608
+ const cameraOrientationKey =
609
+ Platform.OS === "ios"
610
+ ? windowWidth > windowHeight
611
+ ? "landscape"
612
+ : "portrait"
613
+ : "camera";
614
+
615
+ // Camera viewport size, reported by the capture content on layout. Drives the
616
+ // document crop region so the saved capture matches the on-screen frame.
617
+ const [viewport, setViewport] = useState({ width: 0, height: 0 });
618
+ const handleViewportLayout = useCallback(
619
+ (size: { width: number; height: number }) => {
620
+ setViewport((previous) =>
621
+ previous.width === size.width && previous.height === size.height
622
+ ? previous
623
+ : size,
624
+ );
625
+ },
626
+ [],
627
+ );
628
+
629
+ // The document guide rectangle as a native crop region — viewport fractions +
630
+ // size + cover scale — derived from the SAME geometry the overlay draws
631
+ // (documentGuideRect), so the native side crops the saved document to exactly
632
+ // the framed rectangle. undefined for selfie or before the viewport is known.
633
+ const documentCropRegion = useMemo(() => {
634
+ if (!isDocumentCapture || viewport.width <= 0 || viewport.height <= 0) {
635
+ return undefined;
636
+ }
637
+ const aspect = props.knownIdAspectRatio ?? DEFAULT_DOCUMENT_ASPECT;
638
+ const rect = documentGuideRect(aspect, viewport.width, viewport.height);
639
+ return {
640
+ left: rect.left / viewport.width,
641
+ top: rect.top / viewport.height,
642
+ width: rect.width / viewport.width,
643
+ height: rect.height / viewport.height,
644
+ viewportWidthPx: Math.round(viewport.width),
645
+ viewportHeightPx: Math.round(viewport.height),
646
+ };
647
+ }, [isDocumentCapture, viewport, props.knownIdAspectRatio]);
648
+
495
649
  const frameProcessor = useFrameProcessor(
496
650
  (frame) => {
497
651
  "worklet";
498
652
  if (!plugin) return;
653
+ // Crop region passed as FLAT numeric args — frame-processor arguments
654
+ // don't reliably accept nested objects. cropWidth <= 0 signals "no region"
655
+ // (selfie, or before the viewport is measured), leaving it uncropped.
656
+ //
657
+ // The saved document uprights to the LOCKED INTERFACE axis (what the
658
+ // preview and guide are drawn against), never device gravity (the phone is
659
+ // held flat): iOS uprights to `viewportOrientation`, Android via its
660
+ // display-tracking `rotationDegrees`. `rotation` stays the RAW gravity
661
+ // tilt the native upright validator needs.
499
662
  const rawResult = plugin.call(frame, {
500
663
  rotation: deviceOrientationDegrees,
664
+ viewportOrientation: previewOrientationDegrees,
665
+ cropLeft: documentCropRegion ? documentCropRegion.left : 0,
666
+ cropTop: documentCropRegion ? documentCropRegion.top : 0,
667
+ cropWidth: documentCropRegion ? documentCropRegion.width : 0,
668
+ cropHeight: documentCropRegion ? documentCropRegion.height : 0,
669
+ cropViewportWidth: documentCropRegion
670
+ ? documentCropRegion.viewportWidthPx
671
+ : 0,
672
+ cropViewportHeight: documentCropRegion
673
+ ? documentCropRegion.viewportHeightPx
674
+ : 0,
501
675
  });
502
676
  onFrameResultRunOnJS(rawResult);
503
677
  },
504
- [plugin, onFrameResultRunOnJS, deviceOrientationDegrees],
678
+ [
679
+ plugin,
680
+ onFrameResultRunOnJS,
681
+ deviceOrientationDegrees,
682
+ previewOrientationDegrees,
683
+ documentCropRegion,
684
+ ],
505
685
  );
506
686
 
507
687
  // Document controls — live capture only (after Start). Manual shutter
@@ -530,9 +710,13 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
530
710
  showCaptureButton: isInstructionState && !isDocumentCapture,
531
711
  showManualShutter: manualShutterAvailable,
532
712
  showGalleryUpload: galleryAvailable,
713
+ isBackSide: documentSide === DocumentSide.Back,
533
714
  ...(props.knownIdAspectRatio !== undefined
534
715
  ? { knownIdAspectRatio: props.knownIdAspectRatio }
535
716
  : {}),
717
+ ...(props.documentTypeKind !== undefined
718
+ ? { documentTypeKind: props.documentTypeKind }
719
+ : {}),
536
720
  }),
537
721
  [
538
722
  props.captureType,
@@ -546,7 +730,9 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
546
730
  isDocumentCapture,
547
731
  manualShutterAvailable,
548
732
  galleryAvailable,
733
+ documentSide,
549
734
  props.knownIdAspectRatio,
735
+ props.documentTypeKind,
550
736
  ],
551
737
  );
552
738
 
@@ -554,14 +740,14 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
554
740
  const cameraPreview = useMemo(
555
741
  () => (
556
742
  <>
557
- {/* Selfie uses the front camera with the screen as fill light, so
558
- force max brightness on selfie only; document uses the rear
559
- camera, where screen brightness doesn't help. */}
560
- <ForceMaxBrightness
561
- enabled={props.captureType === CaptureType.selfie}
562
- />
743
+ {/* Every capture screen runs at max brightness with the screen kept
744
+ awake, restored on unmount. Selfie uses the front camera with the
745
+ screen as fill light; document capture keeps the same guarantee so
746
+ the preview never dims or auto-locks mid-scan. */}
747
+ <ForceMaxBrightness />
563
748
  {previewReady ? (
564
749
  <Camera
750
+ key={cameraOrientationKey}
565
751
  ref={cameraRef}
566
752
  style={StyleSheet.absoluteFill}
567
753
  device={device}
@@ -576,6 +762,11 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
576
762
  onOutputOrientationChanged={(orientation) => {
577
763
  setDeviceOrientationDegrees(orientationToDegrees(orientation));
578
764
  }}
765
+ // The LOCKED interface orientation (follows the screen even under a
766
+ // programmatic lock) — the axis the saved document uprights to.
767
+ onPreviewOrientationChanged={(orientation) => {
768
+ setPreviewOrientationDegrees(orientationToDegrees(orientation));
769
+ }}
579
770
  // Attach the frame processor only while streaming (skip pre-Start
580
771
  // preview). Spread idiom because exactOptionalPropertyTypes rejects
581
772
  // passing `undefined` to an optional prop directly.
@@ -602,11 +793,17 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
602
793
  streaming,
603
794
  frameProcessor,
604
795
  theme.colors.offBlack,
796
+ cameraOrientationKey,
605
797
  ],
606
798
  );
607
799
 
608
800
  return (
609
- <>
801
+ // The capture screen is always light, regardless of the system dark-mode
802
+ // setting (every other screen tracks the system). Providing the light theme
803
+ // here forces every descendant that reads it from context — not only the
804
+ // ones passing `forceLightMode` — to render light; partner colours still
805
+ // apply because `theme` carries the partner's resolved light tokens.
806
+ <UseSmileIDThemeProvider value={theme}>
610
807
  {/* Translucent status bar → full-bleed window so the oval's reference
611
808
  frame is the full screen (no-op on iOS). On-device-pending: residual
612
809
  inset also depends on the host SafeAreaProvider config. */}
@@ -620,6 +817,7 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
620
817
  onBack={props.onBack}
621
818
  showAttribution={showAttribution}
622
819
  startButton={props.startButton}
820
+ onViewportLayout={handleViewportLayout}
623
821
  {...(isDocumentCapture
624
822
  ? {
625
823
  onManualShutter: handleManualShutter,
@@ -630,6 +828,6 @@ export function UseSmileIDCaptureScreen(props: UseSmileIDCaptureScreenProps) {
630
828
  : {})}
631
829
  theme={theme}
632
830
  />
633
- </>
831
+ </UseSmileIDThemeProvider>
634
832
  );
635
833
  }