@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
@@ -7,8 +7,10 @@ import type { CacheConfig } from "../../../core/models/network_config";
7
7
  * verification flows without aggressive trimming.
8
8
  */
9
9
  export class CacheConfigBuilder {
10
+ /** Whether HTTP response caching is on. */
10
11
  enabled = true;
11
- /** Bytes — default 50 MB. */
12
+
13
+ /** Maximum on-disk cache size, in bytes. Default 50 MB. */
12
14
  maxSize = 50 * 1024 * 1024;
13
15
 
14
16
  /**
@@ -18,13 +18,25 @@ import {
18
18
  * identity.
19
19
  */
20
20
  export class PartnerConfigBuilder {
21
+ /** Your Smile ID partner id. Required. */
21
22
  partnerId = "";
23
+
24
+ /**
25
+ * Optional webhook URL Smile ID calls with the job result. It is also
26
+ * sent as a per-request identity header.
27
+ */
22
28
  callbackUrl = "";
29
+
30
+ /**
31
+ * When true, routes requests to the sandbox environment instead of
32
+ * production. Defaults to false.
33
+ */
23
34
  useSandbox = false;
35
+
24
36
  /**
25
- * Optional partner-supplied key/value metadata. Forwarded onto the
26
- * `partner_params` JSON multipart form part on Enhanced / Biometric
27
- * KYC submissions when set.
37
+ * Optional free-form key/value pairs echoed back on the job result for
38
+ * your own correlation. Forwarded onto the `partner_params` JSON
39
+ * multipart form part on Enhanced / Biometric KYC submissions when set.
28
40
  */
29
41
  partnerParams?: Record<string, string>;
30
42
 
@@ -49,8 +61,16 @@ export class PartnerConfigBuilder {
49
61
  * redaction list with their own proxy-injected headers.
50
62
  */
51
63
  export class LoggingConfigBuilder {
64
+ /** Whether HTTP request/response logging is on. */
52
65
  enabled = true;
66
+
67
+ /** Logging verbosity: none, basic, headers, or full body. */
53
68
  level: LogLevel = LogLevel.basic;
69
+
70
+ /**
71
+ * Header names whose values are masked in logs (for example
72
+ * authorization headers).
73
+ */
54
74
  redactHeaders: readonly string[] = [];
55
75
 
56
76
  build(): NetworkLoggingConfig {
@@ -75,35 +95,46 @@ export class LoggingConfigBuilder {
75
95
  * omit blocks they don't need.
76
96
  */
77
97
  export class ConfigBuilder {
98
+ /**
99
+ * The verification job this flow submits (for example smart-selfie
100
+ * enrollment, biometric KYC, or document verification). Required.
101
+ */
78
102
  jobType?: JobType;
79
103
 
80
104
  /**
81
- * Short-lived v3 auth token issued by `/v3/token`. The partner
82
- * application is responsible for exchanging its API key for a token
83
- * before invoking the SDK and supplying the result here. Stamped as
84
- * the `smileid-token` HTTP header on every authenticated request.
105
+ * The short-lived authentication token for this session. Your backend
106
+ * obtains it from Smile ID's `/v3/token` endpoint using your API key;
107
+ * the SDK never exchanges credentials itself. Stamped as the
108
+ * `smileid-token` HTTP header on every authenticated request.
85
109
  */
86
110
  token = "";
87
111
 
88
112
  /**
89
- * Optional async refresh callback invoked when the server responds
90
- * with `401 Unauthorized`. The callback receives the token the SDK
91
- * was using and returns a fresh token; the SDK then retries the
92
- * original request once with the new value. Concurrent 401s collapse
93
- * to a single callback invocation via the SDK's shared `TokenHolder`.
94
- * If the callback throws or the retry also returns 401, the original
95
- * 401 response is surfaced unchanged.
113
+ * Optional async callback invoked when the server rejects the token
114
+ * with a 401. Return a fresh token and the SDK retries the request
115
+ * once with it. The callback receives the token the SDK was using.
116
+ * Concurrent 401s collapse to a single callback invocation via the
117
+ * SDK's shared `TokenHolder`. If the callback throws or the retry also
118
+ * returns 401, the original 401 response is surfaced unchanged.
96
119
  */
97
120
  onTokenExpired?: (previousToken: string) => Promise<string>;
98
121
 
99
122
  private _partnerConfig?: PartnerConfigBuilder;
100
123
  private _logging?: LoggingConfigBuilder;
101
124
 
125
+ /**
126
+ * Configures partner identity and per-job environment settings —
127
+ * partner id, callback URL, sandbox mode, and partner params.
128
+ */
102
129
  partnerConfig(block: (p: PartnerConfigBuilder) => void): void {
103
130
  if (!this._partnerConfig) this._partnerConfig = new PartnerConfigBuilder();
104
131
  block(this._partnerConfig);
105
132
  }
106
133
 
134
+ /**
135
+ * Configures HTTP request/response logging: on/off, verbosity, and
136
+ * redacted headers.
137
+ */
107
138
  logging(block: (l: LoggingConfigBuilder) => void): void {
108
139
  if (!this._logging) this._logging = new LoggingConfigBuilder();
109
140
  block(this._logging);
@@ -28,24 +28,26 @@ import {
28
28
  export class InterceptorConfigBuilder {
29
29
  private _interceptors: Interceptor[] = [];
30
30
 
31
+ /** Appends a custom interceptor to the chain. */
31
32
  add(interceptor: Interceptor): void {
32
33
  this._interceptors.push(interceptor);
33
34
  }
34
35
 
35
36
  /**
36
- * Returns an interceptor that stamps `Accept-Encoding: gzip` on
37
- * every outbound request. Hint-only — the SDK does not pull in a
38
- * JS gzip dependency to compress request bodies. See
39
- * [createGzipInterceptor] for the rationale.
37
+ * Adds the gzip interceptor, which requests compressed responses by
38
+ * stamping `Accept-Encoding: gzip` on every outbound request.
39
+ * Hint-only — the SDK does not pull in a JS gzip dependency to
40
+ * compress request bodies. See [createGzipInterceptor] for the
41
+ * rationale.
40
42
  */
41
43
  gzip(): Interceptor {
42
44
  return createGzipInterceptor();
43
45
  }
44
46
 
45
47
  /**
46
- * Returns a logging interceptor configured by `configure`. The
47
- * partner block mutates a `LoggingInterceptorBuilder` carrying
48
- * `level` and `redactHeaders`.
48
+ * Adds the logging interceptor with the given verbosity. The partner
49
+ * block mutates a `LoggingInterceptorBuilder` carrying `level` and
50
+ * `redactHeaders`.
49
51
  */
50
52
  logging(
51
53
  configure?: (builder: LoggingInterceptorBuilder) => void,
@@ -13,13 +13,34 @@ export { RetryStrategy };
13
13
  * `strategy = RetryStrategy.fixed`.
14
14
  */
15
15
  export class RetryConfigBuilder {
16
+ /** Maximum number of attempts per request, including the first. */
16
17
  maxAttempts = 3;
18
+
19
+ /** Backoff strategy between attempts: exponential, fixed, or none. */
17
20
  strategy: RetryStrategy = RetryStrategy.exponential;
21
+
22
+ /** Delay before the first retry, in seconds (exponential strategy). */
18
23
  initialDelay = 1;
24
+
25
+ /**
26
+ * Factor by which the delay grows after each attempt (exponential
27
+ * strategy).
28
+ */
19
29
  multiplier = 2;
30
+
31
+ /**
32
+ * Upper bound on the delay between attempts, in seconds (exponential
33
+ * strategy).
34
+ */
20
35
  maxDelay = 32;
21
- /** ±20% randomized jitter on the computed delay, in [0, 1]. */
36
+
37
+ /**
38
+ * Random fraction between 0 and 1 added to each delay to avoid
39
+ * synchronized retry storms.
40
+ */
22
41
  jitter = 0.2;
42
+
43
+ /** Constant delay between attempts, in seconds (fixed strategy). */
23
44
  fixedDelay = 1;
24
45
 
25
46
  build(): RetryConfig {
@@ -8,9 +8,16 @@ import type { TimeoutConfig } from "../../../core/models/network_config";
8
8
  * the end-to-end ceiling for the whole request including retries.
9
9
  */
10
10
  export class TimeoutConfigBuilder {
11
+ /** Maximum time to establish a connection, in seconds. */
11
12
  connect = 60;
13
+
14
+ /** Maximum time to wait for response data, in seconds. */
12
15
  read = 60;
16
+
17
+ /** Maximum time to send request data, in seconds. */
13
18
  write = 60;
19
+
20
+ /** Maximum total time for the entire request, in seconds. */
14
21
  call = 120;
15
22
 
16
23
  build(): TimeoutConfig {
@@ -29,26 +29,43 @@ export class UseSmileIDNetworkBuilder {
29
29
  private _retry?: RetryConfigBuilder;
30
30
  private _interceptors?: InterceptorConfigBuilder;
31
31
 
32
+ /**
33
+ * Configures the job type, authentication token, token-refresh
34
+ * callback, and partner identity for this flow.
35
+ */
32
36
  config(block: (c: ConfigBuilder) => void): void {
33
37
  if (!this._config) this._config = new ConfigBuilder();
34
38
  block(this._config);
35
39
  }
36
40
 
41
+ /**
42
+ * Configures per-phase HTTP timeouts — connect, read, write, and
43
+ * overall call — in seconds.
44
+ */
37
45
  timeouts(block: (t: TimeoutConfigBuilder) => void): void {
38
46
  if (!this._timeouts) this._timeouts = new TimeoutConfigBuilder();
39
47
  block(this._timeouts);
40
48
  }
41
49
 
50
+ /** Configures the HTTP response cache (on/off and maximum size). */
42
51
  cache(block: (c: CacheConfigBuilder) => void): void {
43
52
  if (!this._cache) this._cache = new CacheConfigBuilder();
44
53
  block(this._cache);
45
54
  }
46
55
 
56
+ /**
57
+ * Configures automatic retry of failed requests: strategy, attempt
58
+ * count, and backoff.
59
+ */
47
60
  retry(block: (r: RetryConfigBuilder) => void): void {
48
61
  if (!this._retry) this._retry = new RetryConfigBuilder();
49
62
  block(this._retry);
50
63
  }
51
64
 
65
+ /**
66
+ * Registers additional request/response interceptors, appended to the
67
+ * SDK's interceptor chain.
68
+ */
52
69
  interceptors(block: (i: InterceptorConfigBuilder) => void): void {
53
70
  if (!this._interceptors)
54
71
  this._interceptors = new InterceptorConfigBuilder();
@@ -12,6 +12,8 @@ import { SmileIntegrityInterceptor } from "../api/interceptors/smile_integrity_i
12
12
  import { TokenRefreshInterceptor } from "../api/interceptors/token_refresh_interceptor";
13
13
  import { TokenHolder } from "../api/auth/token_holder";
14
14
  import { SmileIDCryptoManager } from "../security/smile_id_crypto_manager";
15
+ import { SecurityPolicy } from "../security/security_policy";
16
+ import { decodeSecurityPolicyBitCode } from "../util/use_smile_id_token_decoder";
15
17
  import { Attestation, SigningSecretProvider } from "@smileid/usesmileid_bridge";
16
18
  import { MissingPartnerConfigException } from "../../core/exception";
17
19
  import { UseSmileIDMetadataFactory } from "../../metadata/use_smile_id_metadata_factory";
@@ -107,6 +109,14 @@ export function makeNetworkingFactory(
107
109
  // next outgoing request automatically picks up the new token.
108
110
  const tokenHolder = new TokenHolder(config.config?.token ?? "");
109
111
 
112
+ // Server-driven security policy for this flow, decoded from the top-level `policy` claim on the
113
+ // JWT (a sibling of `sdk_config`). Decoding is fail-secure: a malformed token, an absent claim, or an absent
114
+ // bit-code all resolve to "every behaviour active", so a token without the claim keeps the
115
+ // pre-policy behaviour rather than dropping security.
116
+ const securityPolicy = SecurityPolicy.fromBitCode(
117
+ decodeSecurityPolicyBitCode(config.config?.token ?? ""),
118
+ );
119
+
110
120
  // 1. Gzip is registered FIRST so it runs LAST on the request path
111
121
  // (innermost / closest to the wire). It stamps `Accept-Encoding:
112
122
  // gzip` so the server can compress responses (axios
@@ -128,7 +138,9 @@ export function makeNetworkingFactory(
128
138
  // request path — it reads the `smileid-request-mac` header the MAC
129
139
  // interceptor sets and binds the per-request token to it. Per-request
130
140
  // opt-in via `config._useSmileIDAttestation`; pass-through otherwise.
131
- const integrityInterceptor = new SmileIntegrityInterceptor();
141
+ const integrityInterceptor = new SmileIntegrityInterceptor({
142
+ securityPolicy,
143
+ });
132
144
  integrityInterceptor.attach(client);
133
145
 
134
146
  // 4. Request MAC runs after auth (so the `smileid-*` headers + timestamp it
@@ -138,6 +150,7 @@ export function makeNetworkingFactory(
138
150
  const cryptoManager = new SmileIDCryptoManager(signingSecret());
139
151
  const requestMacInterceptor = new SmileRequestMacInterceptor({
140
152
  cryptoManager,
153
+ securityPolicy,
141
154
  });
142
155
  requestMacInterceptor.attach(client);
143
156
 
@@ -0,0 +1,117 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { AxiosError, AxiosHeaders } from "axios";
3
+
4
+ import { UseSmileIDRepositoryImpl } from "../use_smile_id_repository_impl";
5
+ import {
6
+ RetryStrategy,
7
+ type RetryConfig,
8
+ } from "../../dsl/builder/retry_config_builder";
9
+ import { JobType } from "../../../core/models/job_type";
10
+ import { UseSmileIDMetadata } from "../../../metadata/use_smile_id_metadata";
11
+ import type { EnhancedKycRequest } from "../../model/job_request";
12
+ import type { JobSubmissionResponse } from "../../model/job_submission_response";
13
+ import type { ConsentInformation } from "../../../core/models/params";
14
+
15
+ // This file intentionally does NOT mock axios: it exercises the real retry
16
+ // classifier (`isRetryable` → `axios.isAxiosError`) so a genuine retry happens
17
+ // and `network_retries` can be observed on the stamped metadata.
18
+
19
+ const FIXED_RETRY: RetryConfig = Object.freeze({
20
+ maxAttempts: 2,
21
+ strategy: RetryStrategy.fixed,
22
+ initialDelay: 0,
23
+ multiplier: 1,
24
+ maxDelay: 0,
25
+ jitter: 0,
26
+ fixedDelay: 0,
27
+ });
28
+
29
+ const CONSENT: ConsentInformation = Object.freeze({
30
+ granted: true,
31
+ grantedAt: "2026-04-30T00:00:00.000Z",
32
+ noticeLanguage: "EN",
33
+ noticePrivacyPolicyUrl: "https://example.test/p",
34
+ });
35
+
36
+ const METADATA = new UseSmileIDMetadata({
37
+ sdkVersion: "12.0.0",
38
+ sdk: "react-native-expo",
39
+ deviceOs: "",
40
+ deviceModel: "",
41
+ appName: "",
42
+ sessionId: "test-session-id",
43
+ clientIp: "",
44
+ networkConnection: "",
45
+ carrierInfo: "",
46
+ proxy: false,
47
+ timezone: "",
48
+ locale: "",
49
+ screenResolution: "",
50
+ memoryInfoMb: 0,
51
+ deviceOrientation: "",
52
+ systemArchitecture: "",
53
+ numberOfCameras: 0,
54
+ hostApplication: "",
55
+ proximitySensor: false,
56
+ securityPolicyVersion: "12.0.0",
57
+ });
58
+
59
+ function makeAxiosError(status: number): AxiosError {
60
+ return new AxiosError("boom", `${status}`, undefined, undefined, {
61
+ status,
62
+ statusText: "X",
63
+ data: undefined,
64
+ headers: {},
65
+ config: { headers: new AxiosHeaders() },
66
+ });
67
+ }
68
+
69
+ function networkRetriesOf(metadata: UseSmileIDMetadata): string | undefined {
70
+ return metadata.toEntries().find((e) => e.name === "network_retries")?.value;
71
+ }
72
+
73
+ describe("UseSmileIDRepositoryImpl — network_retries", () => {
74
+ it("stamps network_retries on the retried attempt and omits it on the first", async () => {
75
+ const response: JobSubmissionResponse = {
76
+ jobId: "job-1",
77
+ userId: "user-1",
78
+ status: "approved",
79
+ message: "ok",
80
+ };
81
+ const seenRetries: (string | undefined)[] = [];
82
+ const enhancedKyc = vi
83
+ .fn()
84
+ .mockImplementation((request: EnhancedKycRequest) => {
85
+ seenRetries.push(networkRetriesOf(request.metadata));
86
+ // Fail the first attempt with a retryable 500, succeed on the retry.
87
+ if (enhancedKyc.mock.calls.length === 1) {
88
+ return Promise.reject(makeAxiosError(500));
89
+ }
90
+ return Promise.resolve(response);
91
+ });
92
+ const stubApi = {
93
+ enhancedKyc,
94
+ biometricKyc: vi.fn(),
95
+ smartSelfieEnrollment: vi.fn(),
96
+ smartSelfieAuthentication: vi.fn(),
97
+ documentVerification: vi.fn(),
98
+ enhancedDocumentVerification: vi.fn(),
99
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
+ } as any;
101
+ const repository = new UseSmileIDRepositoryImpl(stubApi, FIXED_RETRY);
102
+ const request: EnhancedKycRequest = {
103
+ jobType: JobType.enhancedKyc,
104
+ params: { idType: "NIN", idNumber: "1", country: "NG" },
105
+ userDetails: { givenNames: "A", lastName: "B", email: "a@b.test" },
106
+ consent: CONSENT,
107
+ metadata: METADATA,
108
+ };
109
+
110
+ const result = await repository.submit(request);
111
+
112
+ expect(result.status).toBe("success");
113
+ expect(enhancedKyc).toHaveBeenCalledTimes(2);
114
+ // First attempt: no retries yet. Retry attempt: one retry preceded it.
115
+ expect(seenRetries).toEqual([undefined, "1"]);
116
+ });
117
+ });
@@ -12,6 +12,29 @@ import {
12
12
  mapAxiosError,
13
13
  type UseSmileIDNetworkException,
14
14
  } from "../../core/exception/use_smile_id_network_exception";
15
+ import {
16
+ PayloadCompressionRunner,
17
+ type PayloadCompressionSizes,
18
+ } from "../compression/payload_compression_runner";
19
+ import type { MetadataEntry } from "../../metadata/use_smile_id_metadata";
20
+
21
+ /** Builds the per-image original/final compressed size wire entries, omitting absent/zero sizes. */
22
+ function compressionSizeEntries(
23
+ sizes: PayloadCompressionSizes,
24
+ ): MetadataEntry[] {
25
+ const entries: MetadataEntry[] = [];
26
+ const push = (name: string, value: number | undefined) => {
27
+ if (value !== undefined && value > 0)
28
+ entries.push({ name, value: String(value) });
29
+ };
30
+ push("selfie_original_size", sizes.selfieOriginalMb);
31
+ push("selfie_final_compressed_size", sizes.selfieFinalMb);
32
+ push("document_front_original_size", sizes.frontOriginalMb);
33
+ push("document_front_final_compressed_size", sizes.frontFinalMb);
34
+ push("document_back_original_size", sizes.backOriginalMb);
35
+ push("document_back_final_compressed_size", sizes.backFinalMb);
36
+ return entries;
37
+ }
15
38
 
16
39
  /**
17
40
  * Concrete repository.
@@ -45,9 +68,28 @@ export class UseSmileIDRepositoryImpl {
45
68
  }),
46
69
  );
47
70
  }
71
+
72
+ // Pre-flight payload compression — best effort: shrink the captured JPEGs under the Lambda
73
+ // cap before transmission (runs once, outside the retry loop), but never block submission, so
74
+ // any compression failure ships the captured images as-is and lets the server validate. The
75
+ // per-image original + final compressed sizes are stamped onto the metadata for telemetry.
76
+ let submitted = request;
48
77
  try {
49
- const response = await withRetry(this._retry, () =>
50
- this._dispatch(request),
78
+ const sizes = await this._compressPayload(request);
79
+ const sizeEntries = sizes ? compressionSizeEntries(sizes) : [];
80
+ if (sizeEntries.length > 0) {
81
+ submitted = {
82
+ ...request,
83
+ metadata: request.metadata.withAdditionalEntries(sizeEntries),
84
+ };
85
+ }
86
+ } catch {
87
+ // Best-effort: compression is an optimization; submit the captured images as-is on failure.
88
+ }
89
+
90
+ try {
91
+ const response = await withRetry(this._retry, (attempt) =>
92
+ this._dispatch(submitted, attempt),
51
93
  );
52
94
  return success(response);
53
95
  } catch (error) {
@@ -62,22 +104,67 @@ export class UseSmileIDRepositoryImpl {
62
104
  }
63
105
  }
64
106
 
107
+ /**
108
+ * Pre-flight payload compression entry — extracts the captured image URIs for the job type and
109
+ * compresses the bundle under the transmission cap (metadata is measured, never modified).
110
+ * No-op for job types that carry no captured images (Enhanced KYC / BVN).
111
+ */
112
+ private async _compressPayload(
113
+ request: JobRequest,
114
+ ): Promise<PayloadCompressionSizes | undefined> {
115
+ const metadataBytes = JSON.stringify(request.metadata.toEntries()).length;
116
+ switch (request.jobType) {
117
+ case JobType.smartSelfieEnrollment:
118
+ case JobType.smartSelfieAuthentication:
119
+ case JobType.biometricKyc:
120
+ return PayloadCompressionRunner.compress({
121
+ selfieImage: request.selfieImage,
122
+ livenessImages: request.livenessImages,
123
+ metadataBytes,
124
+ });
125
+ case JobType.documentVerification:
126
+ case JobType.enhancedDocumentVerification:
127
+ return PayloadCompressionRunner.compress({
128
+ selfieImage: request.selfieImage,
129
+ livenessImages: request.livenessImages,
130
+ frontDocument: request.frontDocument,
131
+ backDocument: request.backDocument,
132
+ metadataBytes,
133
+ });
134
+ default:
135
+ return undefined;
136
+ }
137
+ }
138
+
65
139
  private _dispatch(
66
140
  request: Exclude<JobRequest, { jobType: typeof JobType.bvn }>,
141
+ attempt: number,
67
142
  ): Promise<JobSubmissionResponse> {
68
- switch (request.jobType) {
143
+ // Stamp how many network retries preceded this attempt onto the metadata
144
+ // (re-evaluated per attempt inside the retry loop). Appended as a flow-state
145
+ // entry so the already-appended document / config entries are preserved.
146
+ const stamped: typeof request =
147
+ attempt > 0
148
+ ? {
149
+ ...request,
150
+ metadata: request.metadata.withAdditionalEntries([
151
+ { name: "network_retries", value: String(attempt) },
152
+ ]),
153
+ }
154
+ : request;
155
+ switch (stamped.jobType) {
69
156
  case JobType.smartSelfieEnrollment:
70
- return this._api.smartSelfieEnrollment(request);
157
+ return this._api.smartSelfieEnrollment(stamped);
71
158
  case JobType.smartSelfieAuthentication:
72
- return this._api.smartSelfieAuthentication(request);
159
+ return this._api.smartSelfieAuthentication(stamped);
73
160
  case JobType.enhancedKyc:
74
- return this._api.enhancedKyc(request);
161
+ return this._api.enhancedKyc(stamped);
75
162
  case JobType.biometricKyc:
76
- return this._api.biometricKyc(request);
163
+ return this._api.biometricKyc(stamped);
77
164
  case JobType.documentVerification:
78
- return this._api.documentVerification(request);
165
+ return this._api.documentVerification(stamped);
79
166
  case JobType.enhancedDocumentVerification:
80
- return this._api.enhancedDocumentVerification(request);
167
+ return this._api.enhancedDocumentVerification(stamped);
81
168
  }
82
169
  }
83
170
 
@@ -0,0 +1,96 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { SecurityPolicy } from "../security_policy";
4
+
5
+ /**
6
+ * Pins down the server-driven security policy bit-code mapping (the contract lives in
7
+ * `docs/Policy.md`):
8
+ * - `undefined` is fail-secure (every behaviour active).
9
+ * - bit 0 -> payload signing, bit 1 -> payload encryption, bit 2 -> rooted-device check,
10
+ * bit 3 -> device attestation (least-significant bit first).
11
+ * - Unrelated bits never cross-activate a behaviour.
12
+ */
13
+ describe("SecurityPolicy", () => {
14
+ it("undefined bit-code is fail-secure and enables every behaviour", () => {
15
+ const policy = SecurityPolicy.fromBitCode(undefined);
16
+
17
+ expect(policy.isPayloadSigningActive()).toBe(true);
18
+ expect(policy.isPayloadEncryptionActive()).toBe(true);
19
+ expect(policy.isRootedDeviceCheckActive()).toBe(true);
20
+ expect(policy.isPlayIntegrityCheckActive()).toBe(true);
21
+ });
22
+
23
+ it("zero bit-code disables every behaviour", () => {
24
+ const policy = SecurityPolicy.fromBitCode(0);
25
+
26
+ expect(policy.isPayloadSigningActive()).toBe(false);
27
+ expect(policy.isPayloadEncryptionActive()).toBe(false);
28
+ expect(policy.isRootedDeviceCheckActive()).toBe(false);
29
+ expect(policy.isPlayIntegrityCheckActive()).toBe(false);
30
+ });
31
+
32
+ it("bit 0 maps to payload signing only", () => {
33
+ const policy = SecurityPolicy.fromBitCode(0b0001);
34
+
35
+ expect(policy.isPayloadSigningActive()).toBe(true);
36
+ expect(policy.isPayloadEncryptionActive()).toBe(false);
37
+ expect(policy.isRootedDeviceCheckActive()).toBe(false);
38
+ expect(policy.isPlayIntegrityCheckActive()).toBe(false);
39
+ });
40
+
41
+ it("bit 1 maps to payload encryption only", () => {
42
+ const policy = SecurityPolicy.fromBitCode(0b0010);
43
+
44
+ expect(policy.isPayloadSigningActive()).toBe(false);
45
+ expect(policy.isPayloadEncryptionActive()).toBe(true);
46
+ expect(policy.isRootedDeviceCheckActive()).toBe(false);
47
+ expect(policy.isPlayIntegrityCheckActive()).toBe(false);
48
+ });
49
+
50
+ it("bit 2 maps to rooted-device check only", () => {
51
+ const policy = SecurityPolicy.fromBitCode(0b0100);
52
+
53
+ expect(policy.isPayloadSigningActive()).toBe(false);
54
+ expect(policy.isPayloadEncryptionActive()).toBe(false);
55
+ expect(policy.isRootedDeviceCheckActive()).toBe(true);
56
+ expect(policy.isPlayIntegrityCheckActive()).toBe(false);
57
+ });
58
+
59
+ it("bit 3 maps to play integrity check only", () => {
60
+ const policy = SecurityPolicy.fromBitCode(0b1000);
61
+
62
+ expect(policy.isPayloadSigningActive()).toBe(false);
63
+ expect(policy.isPayloadEncryptionActive()).toBe(false);
64
+ expect(policy.isRootedDeviceCheckActive()).toBe(false);
65
+ expect(policy.isPlayIntegrityCheckActive()).toBe(true);
66
+ });
67
+
68
+ it("combined bit-code activates exactly the set behaviours", () => {
69
+ // 0b1001 = payload signing (bit 0) + device attestation (bit 3).
70
+ const policy = SecurityPolicy.fromBitCode(0b1001);
71
+
72
+ expect(policy.isPayloadSigningActive()).toBe(true);
73
+ expect(policy.isPayloadEncryptionActive()).toBe(false);
74
+ expect(policy.isRootedDeviceCheckActive()).toBe(false);
75
+ expect(policy.isPlayIntegrityCheckActive()).toBe(true);
76
+ });
77
+
78
+ it("all four bits set activates every behaviour", () => {
79
+ const policy = SecurityPolicy.fromBitCode(0b1111);
80
+
81
+ expect(policy.isPayloadSigningActive()).toBe(true);
82
+ expect(policy.isPayloadEncryptionActive()).toBe(true);
83
+ expect(policy.isRootedDeviceCheckActive()).toBe(true);
84
+ expect(policy.isPlayIntegrityCheckActive()).toBe(true);
85
+ });
86
+
87
+ it("higher unmapped bits do not affect the known behaviours", () => {
88
+ // bit 4 (0b10000) is beyond the defined mapping; the four known behaviours stay off.
89
+ const policy = SecurityPolicy.fromBitCode(0b10000);
90
+
91
+ expect(policy.isPayloadSigningActive()).toBe(false);
92
+ expect(policy.isPayloadEncryptionActive()).toBe(false);
93
+ expect(policy.isRootedDeviceCheckActive()).toBe(false);
94
+ expect(policy.isPlayIntegrityCheckActive()).toBe(false);
95
+ });
96
+ });
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
2
2
 
3
3
  import { SigningCrypto, base64Encode, utf8Bytes } from "../signing_crypto";
4
4
  import { SmileIDCryptoManager } from "../smile_id_crypto_manager";
5
+ import { SmileIDCryptoError } from "../smile_id_crypto_error";
5
6
 
6
7
  /**
7
8
  * Pins down the request-signing contract:
@@ -58,6 +59,23 @@ describe("SmileIDCryptoManager", () => {
58
59
  expect(mac).toBe("nW0HenQOrKH/qAwQBveK+TSXvg70oVWfD0aGGtfI1bk=");
59
60
  });
60
61
 
62
+ it("wraps an unreadable file part as SmileIDCryptoError (signing fails open, never crashes)", async () => {
63
+ // A `file://` URI the runtime's `fetch` rejects — the same rejection a
64
+ // captured-image local read produces when the device is offline (Expo vs
65
+ // bare-RN runtimes differ here). The canonicalizer must surface it as a
66
+ // SmileIDCryptoError so the request-MAC interceptor's narrow catch fails
67
+ // open instead of letting the rejection escape and crash the submission.
68
+ await expect(
69
+ subject.sign(
70
+ TIMESTAMP,
71
+ KAT_HEADERS,
72
+ formData([
73
+ { fieldName: "selfie_image", uri: "file:///nonexistent/selfie.jpg" },
74
+ ]),
75
+ ),
76
+ ).rejects.toBeInstanceOf(SmileIDCryptoError);
77
+ });
78
+
61
79
  it("multipart body MAC is independent of part order", async () => {
62
80
  const headers = { "smileid-request-timestamp": TIMESTAMP };
63
81
  const first = await subject.sign(