@smileid/usesmileid 12.0.0 → 12.0.2

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 (365) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -1
  3. package/dist/camera/camera_defaults.d.ts +2 -0
  4. package/dist/camera/camera_defaults.d.ts.map +1 -1
  5. package/dist/camera/camera_defaults.js +2 -0
  6. package/dist/camera/camera_defaults.js.map +1 -1
  7. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts +11 -0
  8. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts.map +1 -0
  9. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js +17 -0
  10. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js.map +1 -0
  11. package/dist/core/exception/capture_validation_exceptions.d.ts +1 -0
  12. package/dist/core/exception/capture_validation_exceptions.d.ts.map +1 -1
  13. package/dist/core/exception/capture_validation_exceptions.js +1 -0
  14. package/dist/core/exception/capture_validation_exceptions.js.map +1 -1
  15. package/dist/core/exception/flow_exceptions.d.ts +4 -2
  16. package/dist/core/exception/flow_exceptions.d.ts.map +1 -1
  17. package/dist/core/exception/flow_exceptions.js +3 -2
  18. package/dist/core/exception/flow_exceptions.js.map +1 -1
  19. package/dist/core/exception/index.d.ts +1 -0
  20. package/dist/core/exception/index.d.ts.map +1 -1
  21. package/dist/core/exception/index.js +1 -0
  22. package/dist/core/exception/index.js.map +1 -1
  23. package/dist/core/exception/missing_job_type_exception.d.ts +1 -3
  24. package/dist/core/exception/missing_job_type_exception.d.ts.map +1 -1
  25. package/dist/core/exception/missing_job_type_exception.js +2 -4
  26. package/dist/core/exception/missing_job_type_exception.js.map +1 -1
  27. package/dist/core/exception/use_smile_id_error_code.d.ts +1 -0
  28. package/dist/core/exception/use_smile_id_error_code.d.ts.map +1 -1
  29. package/dist/core/exception/use_smile_id_error_code.js +1 -0
  30. package/dist/core/exception/use_smile_id_error_code.js.map +1 -1
  31. package/dist/core/models/config.d.ts +19 -0
  32. package/dist/core/models/config.d.ts.map +1 -1
  33. package/dist/core/models/document_type.d.ts +1 -1
  34. package/dist/core/models/document_type.d.ts.map +1 -1
  35. package/dist/core/models/document_type.js +2 -2
  36. package/dist/core/models/document_type.js.map +1 -1
  37. package/dist/core/models/global_privacy_control.d.ts +36 -0
  38. package/dist/core/models/global_privacy_control.d.ts.map +1 -0
  39. package/dist/core/models/global_privacy_control.js +39 -0
  40. package/dist/core/models/global_privacy_control.js.map +1 -0
  41. package/dist/core/models/network_config.d.ts +2 -2
  42. package/dist/core/models/network_config.js +2 -2
  43. package/dist/data/api/interceptors/smile_auth_interceptor.d.ts.map +1 -1
  44. package/dist/data/api/interceptors/smile_auth_interceptor.js +3 -17
  45. package/dist/data/api/interceptors/smile_auth_interceptor.js.map +1 -1
  46. package/dist/data/api/smile_id_api.d.ts +5 -1
  47. package/dist/data/api/smile_id_api.d.ts.map +1 -1
  48. package/dist/data/api/smile_id_api.js +30 -27
  49. package/dist/data/api/smile_id_api.js.map +1 -1
  50. package/dist/data/dsl/builder/retry_config_builder.d.ts +4 -4
  51. package/dist/data/dsl/builder/retry_config_builder.d.ts.map +1 -1
  52. package/dist/data/dsl/builder/retry_config_builder.js +6 -6
  53. package/dist/data/dsl/builder/retry_config_builder.js.map +1 -1
  54. package/dist/data/model/job_request.d.ts +28 -12
  55. package/dist/data/model/job_request.d.ts.map +1 -1
  56. package/dist/data/model/token_payload.d.ts +78 -0
  57. package/dist/data/model/token_payload.d.ts.map +1 -0
  58. package/dist/data/model/token_payload.js +132 -0
  59. package/dist/data/model/token_payload.js.map +1 -0
  60. package/dist/data/model/user_details_payload.d.ts +39 -0
  61. package/dist/data/model/user_details_payload.d.ts.map +1 -0
  62. package/dist/data/model/user_details_payload.js +53 -0
  63. package/dist/data/model/user_details_payload.js.map +1 -0
  64. package/dist/data/smile_id_urls.d.ts +1 -1
  65. package/dist/data/smile_id_urls.js +1 -1
  66. package/dist/data/smile_id_urls.js.map +1 -1
  67. package/dist/data/util/retry_util.d.ts +1 -1
  68. package/dist/data/util/retry_util.d.ts.map +1 -1
  69. package/dist/data/util/retry_util.js +5 -0
  70. package/dist/data/util/retry_util.js.map +1 -1
  71. package/dist/data/util/use_smile_id_token_decoder.d.ts +15 -3
  72. package/dist/data/util/use_smile_id_token_decoder.d.ts.map +1 -1
  73. package/dist/data/util/use_smile_id_token_decoder.js +20 -8
  74. package/dist/data/util/use_smile_id_token_decoder.js.map +1 -1
  75. package/dist/index.d.ts +2 -2
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +2 -2
  78. package/dist/index.js.map +1 -1
  79. package/dist/l10n/strings_en.d.ts +4 -1
  80. package/dist/l10n/strings_en.d.ts.map +1 -1
  81. package/dist/l10n/strings_en.js +4 -1
  82. package/dist/l10n/strings_en.js.map +1 -1
  83. package/dist/l10n/use_smile_id_localizations.d.ts +10 -0
  84. package/dist/l10n/use_smile_id_localizations.d.ts.map +1 -1
  85. package/dist/l10n/use_smile_id_localizations.js +59 -3
  86. package/dist/l10n/use_smile_id_localizations.js.map +1 -1
  87. package/dist/metadata/builder_configuration.d.ts +5 -0
  88. package/dist/metadata/builder_configuration.d.ts.map +1 -1
  89. package/dist/metadata/builder_configuration.js +1 -0
  90. package/dist/metadata/builder_configuration.js.map +1 -1
  91. package/dist/metadata/metadata_configuration_mappers.d.ts +2 -3
  92. package/dist/metadata/metadata_configuration_mappers.d.ts.map +1 -1
  93. package/dist/metadata/metadata_configuration_mappers.js +13 -6
  94. package/dist/metadata/metadata_configuration_mappers.js.map +1 -1
  95. package/dist/metadata/use_smile_id_metadata_factory.d.ts.map +1 -1
  96. package/dist/metadata/use_smile_id_metadata_factory.js +14 -5
  97. package/dist/metadata/use_smile_id_metadata_factory.js.map +1 -1
  98. package/dist/presentation/components/use_smile_id_button.d.ts.map +1 -1
  99. package/dist/presentation/components/use_smile_id_button.js +8 -2
  100. package/dist/presentation/components/use_smile_id_button.js.map +1 -1
  101. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +5 -2
  102. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts.map +1 -1
  103. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +8 -3
  104. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js.map +1 -1
  105. package/dist/presentation/components/use_smile_id_instructions_hero.d.ts.map +1 -1
  106. package/dist/presentation/components/use_smile_id_instructions_hero.js +8 -3
  107. package/dist/presentation/components/use_smile_id_instructions_hero.js.map +1 -1
  108. package/dist/presentation/flow/dsl/builder/config_builder.d.ts +16 -0
  109. package/dist/presentation/flow/dsl/builder/config_builder.d.ts.map +1 -1
  110. package/dist/presentation/flow/dsl/builder/config_builder.js +18 -0
  111. package/dist/presentation/flow/dsl/builder/config_builder.js.map +1 -1
  112. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +6 -12
  113. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts.map +1 -1
  114. package/dist/presentation/flow/dsl/builder/screens_builder.js +7 -12
  115. package/dist/presentation/flow/dsl/builder/screens_builder.js.map +1 -1
  116. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts.map +1 -1
  117. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +23 -1
  118. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js.map +1 -1
  119. package/dist/presentation/flow/flow_navigation_manager.d.ts +22 -3
  120. package/dist/presentation/flow/flow_navigation_manager.d.ts.map +1 -1
  121. package/dist/presentation/flow/flow_navigation_manager.js +84 -46
  122. package/dist/presentation/flow/flow_navigation_manager.js.map +1 -1
  123. package/dist/presentation/flow/model/instructions_defaults.d.ts.map +1 -1
  124. package/dist/presentation/flow/model/instructions_defaults.js +1 -1
  125. package/dist/presentation/flow/model/instructions_defaults.js.map +1 -1
  126. package/dist/presentation/flow/screen/orchestrated_capture_screen.d.ts.map +1 -1
  127. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +13 -3
  128. package/dist/presentation/flow/screen/orchestrated_capture_screen.js.map +1 -1
  129. package/dist/presentation/flow/screen/orchestrated_consent_screen.d.ts.map +1 -1
  130. package/dist/presentation/flow/screen/orchestrated_consent_screen.js +3 -1
  131. package/dist/presentation/flow/screen/orchestrated_consent_screen.js.map +1 -1
  132. package/dist/presentation/flow/screen/orchestrated_preview_screen.d.ts.map +1 -1
  133. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +0 -8
  134. package/dist/presentation/flow/screen/orchestrated_preview_screen.js.map +1 -1
  135. package/dist/presentation/flow/screen/orchestrated_processing_screen.d.ts.map +1 -1
  136. package/dist/presentation/flow/screen/orchestrated_processing_screen.js +25 -5
  137. package/dist/presentation/flow/screen/orchestrated_processing_screen.js.map +1 -1
  138. package/dist/presentation/flow/use_smile_id_flow.d.ts.map +1 -1
  139. package/dist/presentation/flow/use_smile_id_flow.js +45 -12
  140. package/dist/presentation/flow/use_smile_id_flow.js.map +1 -1
  141. package/dist/presentation/flow/validation/flow_validator.d.ts +16 -2
  142. package/dist/presentation/flow/validation/flow_validator.d.ts.map +1 -1
  143. package/dist/presentation/flow/validation/flow_validator.js +61 -47
  144. package/dist/presentation/flow/validation/flow_validator.js.map +1 -1
  145. package/dist/presentation/flow/validation/job_type_validator.d.ts +7 -0
  146. package/dist/presentation/flow/validation/job_type_validator.d.ts.map +1 -1
  147. package/dist/presentation/flow/validation/job_type_validator.js +52 -27
  148. package/dist/presentation/flow/validation/job_type_validator.js.map +1 -1
  149. package/dist/presentation/flow/validation/user_details_validator.d.ts +9 -0
  150. package/dist/presentation/flow/validation/user_details_validator.d.ts.map +1 -0
  151. package/dist/presentation/flow/validation/user_details_validator.js +40 -0
  152. package/dist/presentation/flow/validation/user_details_validator.js.map +1 -0
  153. package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts.map +1 -1
  154. package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -0
  155. package/dist/presentation/screen/helpers/capture_screen_helpers.js.map +1 -1
  156. package/dist/presentation/screen/helpers/capture_ui_state.d.ts +6 -6
  157. package/dist/presentation/screen/helpers/capture_ui_state.d.ts.map +1 -1
  158. package/dist/presentation/screen/helpers/capture_ui_state.js +19 -19
  159. package/dist/presentation/screen/helpers/capture_ui_state.js.map +1 -1
  160. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -2
  161. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts.map +1 -1
  162. package/dist/presentation/screen/helpers/ess_turn_haptics.js +6 -6
  163. package/dist/presentation/screen/helpers/ess_turn_haptics.js.map +1 -1
  164. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts +5 -5
  165. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts.map +1 -1
  166. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js +11 -11
  167. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js.map +1 -1
  168. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +13 -3
  169. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts.map +1 -1
  170. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +48 -6
  171. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js.map +1 -1
  172. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +4 -2
  173. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts.map +1 -1
  174. package/dist/presentation/screen/use_smile_id_capture_screen.js +90 -23
  175. package/dist/presentation/screen/use_smile_id_capture_screen.js.map +1 -1
  176. package/dist/presentation/screen/use_smile_id_consent_screen.d.ts +7 -0
  177. package/dist/presentation/screen/use_smile_id_consent_screen.d.ts.map +1 -1
  178. package/dist/presentation/screen/use_smile_id_consent_screen.js +6 -5
  179. package/dist/presentation/screen/use_smile_id_consent_screen.js.map +1 -1
  180. package/dist/presentation/screen/use_smile_id_document_back_instructions.d.ts.map +1 -1
  181. package/dist/presentation/screen/use_smile_id_document_back_instructions.js +5 -13
  182. package/dist/presentation/screen/use_smile_id_document_back_instructions.js.map +1 -1
  183. package/dist/presentation/screen/use_smile_id_document_preview_screen.js +1 -1
  184. package/dist/presentation/screen/use_smile_id_document_preview_screen.js.map +1 -1
  185. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +1 -21
  186. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts.map +1 -1
  187. package/dist/presentation/screen/use_smile_id_preview_screen.js +3 -14
  188. package/dist/presentation/screen/use_smile_id_preview_screen.js.map +1 -1
  189. package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
  190. package/dist/presentation/screen/use_smile_id_processing_screen.js.map +1 -1
  191. package/dist/presentation/utils/locale_utils.d.ts +10 -4
  192. package/dist/presentation/utils/locale_utils.d.ts.map +1 -1
  193. package/dist/presentation/utils/locale_utils.js +13 -5
  194. package/dist/presentation/utils/locale_utils.js.map +1 -1
  195. package/dist/testing/fake_face_analyzer.d.ts +43 -0
  196. package/dist/testing/fake_face_analyzer.d.ts.map +1 -0
  197. package/dist/testing/fake_face_analyzer.js +52 -0
  198. package/dist/testing/fake_face_analyzer.js.map +1 -0
  199. package/dist/testing/index.d.ts +5 -0
  200. package/dist/testing/index.d.ts.map +1 -0
  201. package/dist/testing/index.js +7 -0
  202. package/dist/testing/index.js.map +1 -0
  203. package/dist/testing/scenario_expectations.d.ts +28 -0
  204. package/dist/testing/scenario_expectations.d.ts.map +1 -0
  205. package/dist/testing/scenario_expectations.js +65 -0
  206. package/dist/testing/scenario_expectations.js.map +1 -0
  207. package/dist/testing/scenario_loader.d.ts +11 -0
  208. package/dist/testing/scenario_loader.d.ts.map +1 -0
  209. package/dist/testing/scenario_loader.js +64 -0
  210. package/dist/testing/scenario_loader.js.map +1 -0
  211. package/dist/testing/scripted_scan_results.d.ts +17 -0
  212. package/dist/testing/scripted_scan_results.d.ts.map +1 -0
  213. package/dist/testing/scripted_scan_results.js +52 -0
  214. package/dist/testing/scripted_scan_results.js.map +1 -0
  215. package/package.json +11 -4
  216. package/src/__tests__/dsl.test.ts +51 -1
  217. package/src/__tests__/react_native_mock.tsx +22 -0
  218. package/src/__tests__/setup_react_native.ts +17 -2
  219. package/src/__tests__/snapshots/__snapshots__/screens_structural.test.tsx.snap +5267 -0
  220. package/src/__tests__/snapshots/screens_structural.test.tsx +230 -0
  221. package/src/camera/__tests__/camera_permissions.test.ts +72 -0
  222. package/src/camera/__tests__/camera_system_focus.test.ts +173 -0
  223. package/src/camera/__tests__/use_smile_id_camera_preview.test.tsx +461 -0
  224. package/src/camera/__tests__/use_smile_id_camera_system.test.ts +103 -0
  225. package/src/camera/__tests__/use_smile_id_frame_stream_hook.test.tsx +229 -0
  226. package/src/camera/camera_defaults.ts +3 -0
  227. package/src/core/exception/__tests__/builder_validation_exceptions.test.ts +81 -0
  228. package/src/core/exception/agent_mode_with_enhanced_liveness_exception.ts +20 -0
  229. package/src/core/exception/capture_validation_exceptions.ts +1 -0
  230. package/src/core/exception/flow_exceptions.test.ts +10 -0
  231. package/src/core/exception/flow_exceptions.ts +3 -2
  232. package/src/core/exception/index.ts +1 -0
  233. package/src/core/exception/missing_job_type_exception.ts +2 -4
  234. package/src/core/exception/use_smile_id_error_code.ts +1 -0
  235. package/src/core/models/__tests__/document_type.test.ts +2 -2
  236. package/src/core/models/__tests__/flow_models.test.ts +26 -0
  237. package/src/core/models/config.ts +22 -0
  238. package/src/core/models/document_type.ts +2 -2
  239. package/src/core/models/global_privacy_control.ts +45 -0
  240. package/src/core/models/network_config.ts +2 -2
  241. package/src/data/api/__tests__/smile_id_api.test.ts +275 -1
  242. package/src/data/api/interceptors/__tests__/credential_header_redaction.test.ts +144 -0
  243. package/src/data/api/interceptors/__tests__/gzip_interceptor.test.ts +83 -0
  244. package/src/data/api/interceptors/smile_auth_interceptor.ts +3 -18
  245. package/src/data/api/smile_id_api.ts +36 -49
  246. package/src/data/compression/__tests__/payload_compressor_real_jpeg.test.ts +6 -2
  247. package/src/data/dsl/__tests__/use_smile_id_network_builder.test.ts +157 -0
  248. package/src/data/dsl/builder/__tests__/config_builders.test.ts +62 -0
  249. package/src/data/dsl/builder/retry_config_builder.ts +6 -6
  250. package/src/data/factory/__tests__/retry_gzip_mac_composition.test.ts +163 -0
  251. package/src/data/factory/__tests__/signed_job_submission_headers.test.ts +1 -1
  252. package/src/data/model/__tests__/token_payload.test.ts +224 -0
  253. package/src/data/model/__tests__/user_details_payload.test.ts +100 -0
  254. package/src/data/model/job_request.ts +27 -12
  255. package/src/data/model/token_payload.ts +182 -0
  256. package/src/data/model/user_details_payload.ts +84 -0
  257. package/src/data/repository/__tests__/network_retries.test.ts +1 -1
  258. package/src/data/repository/__tests__/repository_dispatch.test.ts +366 -0
  259. package/src/data/repository/__tests__/use_smile_id_repository.test.ts +1 -1
  260. package/src/data/security/__tests__/request_canonicalizer.test.ts +132 -0
  261. package/src/data/security/__tests__/signing_crypto.test.ts +203 -0
  262. package/src/data/smile_id_urls.ts +1 -1
  263. package/src/data/util/__tests__/retry_util.test.ts +25 -0
  264. package/src/data/util/__tests__/use_smile_id_token_decoder.test.ts +179 -0
  265. package/src/data/util/retry_util.ts +9 -1
  266. package/src/data/util/use_smile_id_token_decoder.ts +29 -8
  267. package/src/index.ts +6 -4
  268. package/src/l10n/__tests__/use_smile_id_localizations.test.ts +122 -5
  269. package/src/l10n/__tests__/use_smile_id_localizations_provider.test.tsx +112 -0
  270. package/src/l10n/__tests__/use_smile_id_localizations_surface.test.ts +231 -0
  271. package/src/l10n/strings_en.ts +5 -1
  272. package/src/l10n/use_smile_id_localizations.ts +80 -3
  273. package/src/metadata/__tests__/device_orientation_sampling.test.ts +165 -0
  274. package/src/metadata/__tests__/geolocation.test.ts +150 -0
  275. package/src/metadata/__tests__/metadata_configuration_mappers.test.ts +39 -0
  276. package/src/metadata/__tests__/metadata_configuration_objects.test.ts +2 -1
  277. package/src/metadata/__tests__/metadata_device_context.test.ts +3 -3
  278. package/src/metadata/__tests__/metadata_device_os_android.test.ts +18 -0
  279. package/src/metadata/__tests__/metadata_golden_fixture.json +1 -1
  280. package/src/metadata/__tests__/metadata_payload_golden.test.ts +4 -4
  281. package/src/metadata/__tests__/optional_native_modules.test.ts +234 -0
  282. package/src/metadata/__tests__/use_smile_id_metadata.test.ts +1 -1
  283. package/src/metadata/__tests__/use_smile_id_metadata_factory.test.ts +615 -0
  284. package/src/metadata/builder_configuration.ts +6 -0
  285. package/src/metadata/metadata_configuration_mappers.ts +21 -7
  286. package/src/metadata/use_smile_id_metadata_factory.ts +17 -6
  287. package/src/presentation/assets/si_document_green_book_hero_animation.json +1 -4668
  288. package/src/presentation/assets/si_document_hero_animation.json +1 -4799
  289. package/src/presentation/assets/si_document_hero_back_animation.json +1 -2039
  290. package/src/presentation/assets/si_document_passport_hero_animation.json +1 -14491
  291. package/src/presentation/assets/si_ess_all_directions_hero_animation.json +1 -0
  292. package/src/presentation/assets/si_ess_animation_all_directions.json +1 -6589
  293. package/src/presentation/assets/si_selfie_hero_animation.json +1 -1439
  294. package/src/presentation/assets/si_smile_to_capture_animation.json +1 -1439
  295. package/src/presentation/components/__tests__/use_smile_id_content_card.test.tsx +112 -0
  296. package/src/presentation/components/__tests__/use_smile_id_enhanced_smart_selfie_clip.test.tsx +9 -4
  297. package/src/presentation/components/__tests__/use_smile_id_face_oval_image.test.tsx +190 -0
  298. package/src/presentation/components/__tests__/use_smile_id_instructions_hero.test.tsx +44 -0
  299. package/src/presentation/components/__tests__/use_smile_id_learn_more_content.test.tsx +159 -0
  300. package/src/presentation/components/__tests__/use_smile_id_processing_center_indicator.test.tsx +88 -0
  301. package/src/presentation/components/use_smile_id_button.tsx +7 -2
  302. package/src/presentation/components/use_smile_id_enhanced_smart_selfie_clip.tsx +9 -3
  303. package/src/presentation/components/use_smile_id_instructions_hero.tsx +10 -0
  304. package/src/presentation/flow/__tests__/document_flow_navigation.test.ts +3 -3
  305. package/src/presentation/flow/__tests__/flow_navigation_manager.test.ts +372 -1
  306. package/src/presentation/flow/__tests__/use_smile_id_flow.test.tsx +651 -0
  307. package/src/presentation/flow/dsl/__tests__/use_smile_id_builder_component.test.tsx +120 -0
  308. package/src/presentation/flow/dsl/builder/__tests__/config_builder.test.ts +25 -0
  309. package/src/presentation/flow/dsl/builder/config_builder.ts +21 -0
  310. package/src/presentation/flow/dsl/builder/screens_builder.ts +7 -12
  311. package/src/presentation/flow/dsl/use_smile_id_flow_builder.ts +27 -1
  312. package/src/presentation/flow/flow_navigation_manager.ts +108 -93
  313. package/src/presentation/flow/graph/__tests__/screen_renderer.test.tsx +102 -0
  314. package/src/presentation/flow/model/instructions_defaults.ts +1 -1
  315. package/src/presentation/flow/screen/__tests__/orchestrated_capture_screen.test.tsx +836 -0
  316. package/src/presentation/flow/screen/__tests__/orchestrated_consent_screen.test.tsx +165 -0
  317. package/src/presentation/flow/screen/__tests__/orchestrated_instructions_screen.test.tsx +163 -0
  318. package/src/presentation/flow/screen/__tests__/orchestrated_preview_screen.test.tsx +224 -0
  319. package/src/presentation/flow/screen/__tests__/orchestrated_processing_screen.test.tsx +338 -0
  320. package/src/presentation/flow/screen/orchestrated_capture_screen.tsx +18 -3
  321. package/src/presentation/flow/screen/orchestrated_consent_screen.tsx +5 -0
  322. package/src/presentation/flow/screen/orchestrated_preview_screen.tsx +0 -12
  323. package/src/presentation/flow/screen/orchestrated_processing_screen.tsx +42 -5
  324. package/src/presentation/flow/use_smile_id_flow.tsx +63 -12
  325. package/src/presentation/flow/validation/__tests__/flow_validator.test.ts +219 -1
  326. package/src/presentation/flow/validation/__tests__/job_type_validator.test.ts +200 -0
  327. package/src/presentation/flow/validation/flow_validator.ts +85 -57
  328. package/src/presentation/flow/validation/job_type_validator.ts +73 -39
  329. package/src/presentation/flow/validation/user_details_validator.ts +53 -0
  330. package/src/presentation/screen/__tests__/capture_ui_state.test.ts +66 -47
  331. package/src/presentation/screen/__tests__/ess_turn_haptics.test.ts +15 -11
  332. package/src/presentation/screen/__tests__/feedback_copy_table.test.ts +145 -0
  333. package/src/presentation/screen/__tests__/scan_state_to_overlay_state.test.ts +47 -27
  334. package/src/presentation/screen/__tests__/use_smile_id_capture_screen.test.tsx +524 -14
  335. package/src/presentation/screen/__tests__/use_smile_id_document_back_instructions.test.tsx +4 -2
  336. package/src/presentation/screen/__tests__/use_smile_id_permission_screen.test.tsx +68 -0
  337. package/src/presentation/screen/__tests__/use_smile_id_preview_screen.test.tsx +10 -40
  338. package/src/presentation/screen/helpers/__tests__/use_capture_orientation_lock.test.tsx +212 -0
  339. package/src/presentation/screen/helpers/capture_screen_helpers.ts +1 -0
  340. package/src/presentation/screen/helpers/capture_ui_state.ts +27 -27
  341. package/src/presentation/screen/helpers/ess_turn_haptics.ts +9 -9
  342. package/src/presentation/screen/helpers/scan_state_to_overlay_state.ts +13 -13
  343. package/src/presentation/screen/helpers/use_capture_orientation_lock.ts +69 -7
  344. package/src/presentation/screen/use_smile_id_capture_screen.tsx +107 -28
  345. package/src/presentation/screen/use_smile_id_consent_screen.tsx +16 -5
  346. package/src/presentation/screen/use_smile_id_document_back_instructions.tsx +14 -23
  347. package/src/presentation/screen/use_smile_id_document_preview_screen.tsx +1 -1
  348. package/src/presentation/screen/use_smile_id_preview_screen.tsx +13 -45
  349. package/src/presentation/screen/use_smile_id_processing_screen.tsx +1 -1
  350. package/src/presentation/theme/__tests__/use_use_smile_id_fonts.test.tsx +166 -0
  351. package/src/presentation/utils/__tests__/locale_utils.test.ts +46 -5
  352. package/src/presentation/utils/locale_utils.ts +13 -4
  353. package/src/testing/__tests__/fake_face_analyzer.test.ts +42 -0
  354. package/src/testing/__tests__/scenario_expectations.test.ts +110 -0
  355. package/src/testing/__tests__/scenario_loader.test.ts +64 -0
  356. package/src/testing/fake_face_analyzer.ts +82 -0
  357. package/src/testing/index.ts +15 -0
  358. package/src/testing/scenario_expectations.ts +87 -0
  359. package/src/testing/scenario_loader.ts +83 -0
  360. package/src/testing/scripted_scan_results.ts +66 -0
  361. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts +0 -15
  362. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts.map +0 -1
  363. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js +0 -19
  364. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js.map +0 -1
  365. package/src/presentation/components/use_smile_id_face_silhouette_overlay.tsx +0 -27
@@ -0,0 +1,120 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import * as React from "react";
3
+ import { create as createRenderer, act } from "react-test-renderer";
4
+
5
+ // `vi.mock` is hoisted above the imports, so anything its factory closes over
6
+ // must be created with `vi.hoisted` or it is still in the TDZ when the factory
7
+ // runs.
8
+ const { invalidProps, successProps } = vi.hoisted(() => ({
9
+ invalidProps: { current: undefined as Record<string, unknown> | undefined },
10
+ successProps: { current: undefined as Record<string, unknown> | undefined },
11
+ }));
12
+
13
+ // Stand in for the two flow renderers so these tests exercise the
14
+ // build-and-dispatch decision the component owns, not the screen runtime
15
+ // (which has its own suites).
16
+ vi.mock("../../use_smile_id_flow", () => ({
17
+ FlowInvalidDispatcher: (props: Record<string, unknown>) => {
18
+ invalidProps.current = props;
19
+ return null;
20
+ },
21
+ FlowSuccessRenderer: (props: Record<string, unknown>) => {
22
+ successProps.current = props;
23
+ return null;
24
+ },
25
+ }));
26
+
27
+ import { UseSmileIDBuilder } from "../use_smile_id_builder";
28
+ import type { UseSmileIDFlowBuilder } from "../use_smile_id_flow_builder";
29
+
30
+ /**
31
+ * `<UseSmileIDBuilder>` runs the partner's closure exactly once per mount,
32
+ * builds the flow, and routes the outcome: a valid configuration renders the
33
+ * flow, an invalid one hands the partner's `onResult` and debug-mode flag to
34
+ * the invalid dispatcher. Re-running the closure on every render would
35
+ * restart a partner's flow mid-capture.
36
+ */
37
+
38
+ function render(element: React.ReactElement) {
39
+ let tree!: ReturnType<typeof createRenderer>;
40
+ act(() => {
41
+ tree = createRenderer(element);
42
+ });
43
+ return tree;
44
+ }
45
+
46
+ function validFlow(builder: UseSmileIDFlowBuilder): void {
47
+ builder.screens((screens) => {
48
+ screens.instructions();
49
+ });
50
+ }
51
+
52
+ describe("UseSmileIDBuilder", () => {
53
+ beforeEach(() => {
54
+ invalidProps.current = undefined;
55
+ successProps.current = undefined;
56
+ });
57
+
58
+ it("renders the flow when the configuration builds", () => {
59
+ render(<UseSmileIDBuilder builder={validFlow} />);
60
+
61
+ expect(invalidProps.current).toBeUndefined();
62
+ expect(successProps.current).toBeDefined();
63
+ const configuration = successProps.current?.configuration as {
64
+ screens: { steps: readonly unknown[] };
65
+ };
66
+ expect(configuration.screens.steps).toHaveLength(1);
67
+ });
68
+
69
+ it("routes an invalid configuration to the dispatcher with the partner callback", () => {
70
+ const onResult = vi.fn();
71
+ render(
72
+ <UseSmileIDBuilder
73
+ builder={(builder) => {
74
+ builder.onResult = onResult;
75
+ }}
76
+ />,
77
+ );
78
+
79
+ expect(successProps.current).toBeUndefined();
80
+ expect(invalidProps.current?.onResult).toBe(onResult);
81
+ expect(invalidProps.current?.enableDebugMode).toBe(false);
82
+ const result = invalidProps.current?.result as { kind: string };
83
+ expect(result.kind).toBe("invalid");
84
+ });
85
+
86
+ it("forwards the partner's debug-mode opt-in to the dispatcher", () => {
87
+ render(
88
+ <UseSmileIDBuilder
89
+ builder={(builder) => {
90
+ builder.config((config) => {
91
+ config.enableDebugMode = true;
92
+ });
93
+ }}
94
+ />,
95
+ );
96
+
97
+ expect(invalidProps.current?.enableDebugMode).toBe(true);
98
+ });
99
+
100
+ it("reports debug mode as off when the config block is never opened", () => {
101
+ render(<UseSmileIDBuilder builder={() => {}} />);
102
+
103
+ expect(invalidProps.current?.enableDebugMode).toBe(false);
104
+ });
105
+
106
+ it("runs the builder closure once per mount, not per render", () => {
107
+ const builder = vi.fn(validFlow);
108
+ const tree = render(<UseSmileIDBuilder builder={builder} />);
109
+
110
+ act(() => {
111
+ tree.update(<UseSmileIDBuilder builder={builder} />);
112
+ });
113
+ act(() => {
114
+ tree.update(<UseSmileIDBuilder builder={vi.fn(validFlow)} />);
115
+ });
116
+
117
+ expect(builder).toHaveBeenCalledTimes(1);
118
+ expect(successProps.current).toBeDefined();
119
+ });
120
+ });
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import * as ScreenOrientation from "expo-screen-orientation";
3
+ import { GlobalPrivacyControl } from "../../../../../core/models/global_privacy_control";
4
+ import { UseSmileIDConfigBuilder } from "../config_builder";
5
+
6
+ describe("UseSmileIDConfigBuilder", () => {
7
+ it("snapshots the documented defaults", () => {
8
+ expect(new UseSmileIDConfigBuilder().build()).toEqual({
9
+ enableCrashReporting: true,
10
+ enableDebugMode: false,
11
+ allowOfflineMode: false,
12
+ globalPrivacyControl: GlobalPrivacyControl.OptOut,
13
+ exitOrientations: ScreenOrientation.OrientationLock.PORTRAIT_UP,
14
+ });
15
+ });
16
+
17
+ it("carries an overridden exit orientation lock into the snapshot", () => {
18
+ const builder = new UseSmileIDConfigBuilder();
19
+ builder.exitOrientations = ScreenOrientation.OrientationLock.DEFAULT;
20
+
21
+ expect(builder.build().exitOrientations).toBe(
22
+ ScreenOrientation.OrientationLock.DEFAULT,
23
+ );
24
+ });
25
+ });
@@ -1,4 +1,6 @@
1
+ import * as ScreenOrientation from "expo-screen-orientation";
1
2
  import type { ConfigOptions } from "../../../../core/models/config";
3
+ import { GlobalPrivacyControl } from "../../../../core/models/global_privacy_control";
2
4
 
3
5
  /**
4
6
  * `b.config((c) => { c.enableCrashReporting = false; ... })`
@@ -26,11 +28,30 @@ export class UseSmileIDConfigBuilder {
26
28
  */
27
29
  allowOfflineMode = false;
28
30
 
31
+ /**
32
+ * Consent posture for every session (`global_privacy_control`). Defaults
33
+ * to `GlobalPrivacyControl.OptOut` — the opt-out consent layout is
34
+ * enforced universally and regional detection is ignored. Set
35
+ * `GlobalPrivacyControl.OptIn` to keep the legacy regional mechanics
36
+ * (opt-in inside the EU, opt-out elsewhere).
37
+ */
38
+ globalPrivacyControl: GlobalPrivacyControl = GlobalPrivacyControl.OptOut;
39
+
40
+ /**
41
+ * Orientation lock applied when a capture screen leaves the screen.
42
+ * Defaults to portrait-up; set `ScreenOrientation.OrientationLock.DEFAULT`
43
+ * if your app supports more than one orientation.
44
+ */
45
+ exitOrientations: ScreenOrientation.OrientationLock =
46
+ ScreenOrientation.OrientationLock.PORTRAIT_UP;
47
+
29
48
  build(): ConfigOptions {
30
49
  return Object.freeze({
31
50
  enableCrashReporting: this.enableCrashReporting,
32
51
  enableDebugMode: this.enableDebugMode,
33
52
  allowOfflineMode: this.allowOfflineMode,
53
+ globalPrivacyControl: this.globalPrivacyControl,
54
+ exitOrientations: this.exitOrientations,
34
55
  });
35
56
  }
36
57
  }
@@ -29,15 +29,13 @@ export type ButtonSlot = (props: {
29
29
 
30
30
  // --- Per-screen builders ---------------------------------------------------
31
31
 
32
+ // Required fields stay optional on the type because partners assign them by mutating this builder; the flow validator rejects a missing one.
32
33
  export class ConsentConfigBuilder {
33
- /** Your company name, shown in the consent copy. */
34
+ /** Required. Your company name, shown in the consent copy. */
34
35
  partnerName = "";
35
- /**
36
- * Optional partner logo shown on the consent screen, rendered next to
37
- * the Smile ID icon at the top.
38
- */
36
+ /** Required. Your logo, shown on the consent screen next to the Smile ID icon. */
39
37
  partnerIcon?: ReactNode;
40
- /** URL of your privacy policy, linked from the consent screen. */
38
+ /** Required. URL of your privacy policy, linked from the consent screen. */
41
39
  partnerPrivacyPolicyUrl?: string;
42
40
  /**
43
41
  * Whether to show the "Powered by Smile ID" attribution. Defaults to
@@ -89,8 +87,9 @@ export class InstructionsConfigBuilder {
89
87
 
90
88
  export class SelfieCaptureConfigBuilder {
91
89
  /**
92
- * When true, lets an assisting agent flip to the rear camera to capture
93
- * the subject. Defaults to false.
90
+ * When true, the whole selfie session captures on the rear camera so an
91
+ * assisting agent can point it at the subject. Cannot be combined with
92
+ * {@link enableEnhancedLiveness}.
94
93
  */
95
94
  allowAgentMode = false;
96
95
  /**
@@ -180,10 +179,6 @@ export class PreviewConfigBuilder {
180
179
  * true.
181
180
  */
182
181
  showAttribution = true;
183
- /** Provides a custom continue button in place of the default. */
184
- continueButton?: ButtonSlot;
185
- /** Provides a custom cancel button in place of the default. */
186
- cancelButton?: ButtonSlot;
187
182
  }
188
183
 
189
184
  export class ProcessingConfigBuilder {
@@ -33,6 +33,8 @@ import {
33
33
  } from "../validation/flow_validator";
34
34
  import { ValidationState } from "../validation/validation_state";
35
35
  import type { FlowBuildResult } from "./flow_build_result";
36
+ import { decodeSmileIDToken } from "../../../data/util/use_smile_id_token_decoder";
37
+ import type { TokenPayload } from "../../../data/model/token_payload";
36
38
 
37
39
  /**
38
40
  * Top-level DSL builder.
@@ -326,6 +328,13 @@ export class UseSmileIDFlowBuilder {
326
328
  const screens = this._screens?.build();
327
329
  const network = this._network?.build();
328
330
  const mlConfig = this._ml?.build();
331
+ const config = this._config?.build();
332
+
333
+ // Decode the JWT `payload` claim (pure, synchronous) so validation
334
+ // can relax the userDetails/consent requirements the token already
335
+ // covers. Skip rules mirror FlowNavigationManager's decode: offline
336
+ // mode and a missing/blank token keep the strict legacy rules.
337
+ const tokenPayload = decodeTokenPayload(config?.allowOfflineMode, network);
329
338
 
330
339
  const issues = runFlowValidation({
331
340
  steps: screens?.steps ?? [],
@@ -338,6 +347,7 @@ export class UseSmileIDFlowBuilder {
338
347
  enhancedDocumentVerificationParams:
339
348
  this.enhancedDocumentVerificationParams,
340
349
  userDetails: this.userDetails,
350
+ tokenPayload,
341
351
  });
342
352
  const state = ValidationState.invalid(issues);
343
353
  const blocking = firstErrorIssue(state);
@@ -386,7 +396,7 @@ export class UseSmileIDFlowBuilder {
386
396
  : {}),
387
397
  ...(this.userDetails ? { userDetails: this.userDetails } : {}),
388
398
  ...(this.userId ? { userId: this.userId } : {}),
389
- ...(this._config ? { config: this._config.build() } : {}),
399
+ ...(config ? { config } : {}),
390
400
  ...(this._theme ? { theme: this._theme.build() } : {}),
391
401
  ...(network ? { network } : {}),
392
402
  ...(mlConfig ? { ml: mlConfig } : {}),
@@ -402,6 +412,22 @@ export class UseSmileIDFlowBuilder {
402
412
  }
403
413
  }
404
414
 
415
+ /**
416
+ * Decode the `payload` claim from the partner JWT for build-time
417
+ * validation. `undefined` for offline mode, no/blank token, or an
418
+ * unparseable token — every validation rule then stays exactly as
419
+ * strict as before token support existed.
420
+ */
421
+ function decodeTokenPayload(
422
+ allowOfflineMode: boolean | undefined,
423
+ network: NetworkConfig | undefined,
424
+ ): TokenPayload | undefined {
425
+ if (allowOfflineMode) return undefined;
426
+ const token = network?.config?.token;
427
+ if (!token || token.length === 0) return undefined;
428
+ return decodeSmileIDToken(token)?.tokenPayload;
429
+ }
430
+
405
431
  /**
406
432
  * Frozen snapshot produced by `UseSmileIDFlowBuilder.build()`.
407
433
  *
@@ -1,18 +1,9 @@
1
1
  import type { ScreenSpec } from "./dsl/builder/screens_builder";
2
2
  import type { FlowConfigurationSnapshot } from "./dsl/use_smile_id_flow_builder";
3
3
  import type { JobSubmissionState } from "./job_submission_state";
4
- import type {
5
- DocumentVerificationRequest,
6
- EnhancedDocumentVerificationRequest,
7
- JobRequest,
8
- } from "../../data/model/job_request";
4
+ import type { JobRequest } from "../../data/model/job_request";
9
5
  import type { UseSmileIDRepository } from "../../data/repository/use_smile_id_repository";
10
- import type {
11
- ConsentInformation,
12
- DocumentVerificationParams,
13
- EnhancedDocumentVerificationParams,
14
- UserDetails,
15
- } from "../../core/models/params";
6
+ import type { ConsentInformation, UserDetails } from "../../core/models/params";
16
7
  import { JobType } from "../../core/models/job_type";
17
8
  import { CaptureType } from "../../core/models/ml_config";
18
9
  import {
@@ -20,17 +11,17 @@ import {
20
11
  type DocumentType,
21
12
  } from "../../core/models/document_type";
22
13
  import type { JobSubmissionResponse } from "../../data/model/job_submission_response";
23
- import { MissingRequiredFieldException } from "../../core/exception/job_validation_exceptions";
14
+ import {
15
+ MissingJobTypeException,
16
+ MissingRequiredFieldException,
17
+ } from "../../core/exception/job_validation_exceptions";
24
18
  import {
25
19
  UseSmileIDAnalyticsEventTypes,
26
20
  UseSmileIDAnalyticsParamKeys,
27
21
  UseSmileIDAnalyticsScreenNames,
28
22
  type UseSmileIDAnalyticsEventType,
29
23
  } from "./analytics/use_smile_id_analytics_event";
30
- import type {
31
- MetadataEntry,
32
- UseSmileIDMetadata,
33
- } from "../../metadata/use_smile_id_metadata";
24
+ import type { MetadataEntry } from "../../metadata/use_smile_id_metadata";
34
25
  import {
35
26
  UseSmileIDMetadataFactory,
36
27
  type MetadataProvider,
@@ -52,6 +43,16 @@ import {
52
43
  decodeSmileIDToken,
53
44
  type DecodedToken,
54
45
  } from "../../data/util/use_smile_id_token_decoder";
46
+ import {
47
+ isTokenConsentComplete,
48
+ bindsRequiredUserDetails,
49
+ type TokenPayload,
50
+ } from "../../data/model/token_payload";
51
+ import {
52
+ isUserDetailsPayloadEmpty,
53
+ userDetailsPayloadFrom,
54
+ } from "../../data/model/user_details_payload";
55
+ import type { UserDetailsPayload } from "../../data/model/user_details_payload";
55
56
  import {
56
57
  DocumentSide,
57
58
  EnhancedSmartSelfieConfig,
@@ -166,6 +167,15 @@ export class FlowNavigationManager {
166
167
  /** Canonical `partner_id` decoded from the JWT (raw); resolved via the [partnerId] getter. */
167
168
  private readonly _resolvedPartnerId: string | undefined;
168
169
 
170
+ /**
171
+ * Token-bound user-detail/consent bindings decoded from the JWT
172
+ * `payload` claim, resolved once at construction alongside
173
+ * `resolvedSdkConfig`. `undefined` for offline mode / no token /
174
+ * legacy token — assembly then requires and sends everything exactly
175
+ * as before token support existed.
176
+ */
177
+ private readonly _resolvedTokenPayload: TokenPayload | undefined;
178
+
169
179
  /**
170
180
  * Partner identifier the capture screen stamps into the Smile EXIF tag — the tag's HMAC/AES
171
181
  * keys derive from it and the backend validates the tag against the request's partner_id.
@@ -188,7 +198,6 @@ export class FlowNavigationManager {
188
198
 
189
199
  constructor(options: FlowNavigationManagerOptions) {
190
200
  this._configuration = options.configuration;
191
- this._steps = options.configuration.screens.steps;
192
201
  this._onExit = options.onExit;
193
202
  this._repository = options.repository;
194
203
  // Fall back to a fresh factory so capture-only harnesses still build
@@ -202,6 +211,14 @@ export class FlowNavigationManager {
202
211
  );
203
212
  this.resolvedSdkConfig = decodedToken?.sdkConfig;
204
213
  this._resolvedPartnerId = decodedToken?.partnerId;
214
+ this._resolvedTokenPayload = decodedToken?.tokenPayload;
215
+ // Token consent always wins: a complete binding drops the consent
216
+ // screen from the flow — the server injects the token's record, so
217
+ // nothing is collected here.
218
+ const steps = options.configuration.screens.steps;
219
+ this._steps = isTokenConsentComplete(decodedToken?.tokenPayload?.consent)
220
+ ? steps.filter((s) => s.kind !== "consent")
221
+ : steps;
205
222
  // Seed the path with the first screen so currentScreen/renderer have a
206
223
  // deterministic start. Empty path → host renders the "no screens"
207
224
  // sentinel (validation rejects empty screens at build time anyway).
@@ -693,9 +710,14 @@ export class FlowNavigationManager {
693
710
  void this._submit();
694
711
  }
695
712
 
696
- /** Reset to `notAttempted` and re-fire submission (processing screen's onRetry). */
713
+ /**
714
+ * Reset to `notAttempted` and re-fire submission (processing screen's
715
+ * onRetry). Ignored while a submission is in flight — resetting then would
716
+ * defeat `beginSubmissionIfNeeded()`'s single-flight guard.
717
+ */
697
718
  retrySubmission(): void {
698
719
  if (this._disposed) return;
720
+ if (this._jobSubmissionState.kind === "loading") return;
699
721
  this._setSubmissionState({ kind: "notAttempted" });
700
722
  this.beginSubmissionIfNeeded();
701
723
  }
@@ -770,10 +792,18 @@ export class FlowNavigationManager {
770
792
  private _assembleJob(): JobRequest {
771
793
  const config = this._configuration;
772
794
  const jobType = config.network?.config?.jobType ?? JobType.unknown;
773
- const consent =
774
- this._captured.consentInformation ??
775
- config.consentInformation ??
776
- DEFAULT_CONSENT;
795
+ // Token consent always wins: the `consent` part is omitted — the
796
+ // server injects the token's record — and any local preset or
797
+ // capture is ignored. Without a token binding, a local source
798
+ // (captured on the consent screen, else the builder preset) is
799
+ // sent in full, and the legacy DEFAULT_CONSENT placeholder covers
800
+ // the no-source case.
801
+ const consent = isTokenConsentComplete(this._resolvedTokenPayload?.consent)
802
+ ? undefined
803
+ : (this._captured.consentInformation ??
804
+ config.consentInformation ??
805
+ DEFAULT_CONSENT);
806
+ const consentField = consent !== undefined ? { consent } : {};
777
807
  // Build the per-submission metadata snapshot so it travels on the
778
808
  // request (API layer serializes `request.metadata` directly).
779
809
  // Dynamic fields (orientation, locale, timezone) re-resolve per
@@ -801,8 +831,8 @@ export class FlowNavigationManager {
801
831
  jobType: JobType.smartSelfieEnrollment,
802
832
  selfieImage: selfie.selfiePath,
803
833
  livenessImages: selfie.livenessPaths,
804
- userDetails,
805
- consent,
834
+ ...(userDetails !== undefined ? { userDetails } : {}),
835
+ ...consentField,
806
836
  metadata,
807
837
  enhanced: false,
808
838
  expectedLivenessImageCount: this._expectedLivenessImageCount(),
@@ -817,9 +847,9 @@ export class FlowNavigationManager {
817
847
  jobType: JobType.smartSelfieAuthentication,
818
848
  selfieImage: selfie.selfiePath,
819
849
  livenessImages: selfie.livenessPaths,
820
- userDetails,
850
+ ...(userDetails !== undefined ? { userDetails } : {}),
821
851
  userId,
822
- consent,
852
+ ...consentField,
823
853
  metadata,
824
854
  enhanced: false,
825
855
  expectedLivenessImageCount: this._expectedLivenessImageCount(),
@@ -840,8 +870,8 @@ export class FlowNavigationManager {
840
870
  selfieImage: selfie.selfiePath,
841
871
  livenessImages: selfie.livenessPaths,
842
872
  params,
843
- userDetails,
844
- consent,
873
+ ...(userDetails !== undefined ? { userDetails } : {}),
874
+ ...consentField,
845
875
  metadata,
846
876
  expectedLivenessImageCount: this._expectedLivenessImageCount(),
847
877
  };
@@ -857,14 +887,17 @@ export class FlowNavigationManager {
857
887
  });
858
888
  }
859
889
  const userDetails = this._requireUserDetails(config);
860
- return this._buildDocumentRequest(
861
- selfie,
862
- front,
863
- params,
864
- userDetails,
865
- consent,
890
+ return {
891
+ jobType: JobType.documentVerification,
892
+ ...this._documentRequestFields(selfie, front),
893
+ country: params.country,
894
+ // Optional — omit when unset so the server classifies the document.
895
+ ...(params.idType ? { idType: params.idType } : {}),
896
+ ...(userDetails !== undefined ? { userDetails } : {}),
897
+ ...consentField,
866
898
  metadata,
867
- );
899
+ expectedLivenessImageCount: this._expectedLivenessImageCount(),
900
+ };
868
901
  }
869
902
 
870
903
  case JobType.enhancedDocumentVerification: {
@@ -877,14 +910,16 @@ export class FlowNavigationManager {
877
910
  });
878
911
  }
879
912
  const userDetails = this._requireUserDetails(config);
880
- return this._buildEnhancedDocumentRequest(
881
- selfie,
882
- front,
883
- params,
884
- userDetails,
885
- consent,
913
+ return {
914
+ jobType: JobType.enhancedDocumentVerification,
915
+ ...this._documentRequestFields(selfie, front),
916
+ country: params.country,
917
+ idType: params.idType,
918
+ ...(userDetails !== undefined ? { userDetails } : {}),
919
+ ...consentField,
886
920
  metadata,
887
- );
921
+ expectedLivenessImageCount: this._expectedLivenessImageCount(),
922
+ };
888
923
  }
889
924
 
890
925
  case JobType.enhancedKyc: {
@@ -898,8 +933,8 @@ export class FlowNavigationManager {
898
933
  return {
899
934
  jobType: JobType.enhancedKyc,
900
935
  params,
901
- userDetails,
902
- consent,
936
+ ...(userDetails !== undefined ? { userDetails } : {}),
937
+ ...consentField,
903
938
  metadata,
904
939
  };
905
940
  }
@@ -919,18 +954,15 @@ export class FlowNavigationManager {
919
954
  return {
920
955
  jobType: JobType.bvn,
921
956
  params,
922
- userDetails,
923
- consent,
957
+ ...(userDetails !== undefined ? { userDetails } : {}),
958
+ ...consentField,
924
959
  metadata,
925
960
  };
926
961
  }
927
962
 
928
963
  case JobType.unknown:
929
964
  default:
930
- throw new Error(
931
- "Job type not configured — call `b.network((n) => n.config((c) => { c.jobType = JobType.<...>; }))` " +
932
- "before mounting <UseSmileIDBuilder>.",
933
- );
965
+ throw new MissingJobTypeException();
934
966
  }
935
967
  }
936
968
 
@@ -945,12 +977,28 @@ export class FlowNavigationManager {
945
977
  return { selfiePath, livenessPaths };
946
978
  }
947
979
 
948
- private _requireUserDetails(config: FlowConfigurationSnapshot): UserDetails {
980
+ /**
981
+ * Resolve `b.userDetails` for the wire. May legitimately return
982
+ * `undefined` when the token binds every required field
983
+ * (`given_names`, `last_name`, and a contact field) — the
984
+ * `user_details` part is then omitted and the server injects the
985
+ * token's values. Otherwise throws exactly as before token support.
986
+ */
987
+ private _requireUserDetails(
988
+ config: FlowConfigurationSnapshot,
989
+ ): UserDetailsPayload | undefined {
949
990
  const userDetails = config.userDetails;
950
991
  if (!userDetails) {
992
+ if (bindsRequiredUserDetails(this._resolvedTokenPayload)) {
993
+ return undefined;
994
+ }
951
995
  throw new MissingRequiredFieldException({ fieldName: "userDetails" });
952
996
  }
953
- return userDetails;
997
+ const payload = userDetailsPayloadFrom(
998
+ userDetails,
999
+ this._resolvedTokenPayload,
1000
+ );
1001
+ return isUserDetailsPayloadEmpty(payload) ? undefined : payload;
954
1002
  }
955
1003
 
956
1004
  /**
@@ -974,58 +1022,25 @@ export class FlowNavigationManager {
974
1022
  return frontDocumentPath;
975
1023
  }
976
1024
 
977
- private _buildDocumentRequest(
978
- selfie: {
979
- readonly selfiePath: string;
980
- readonly livenessPaths: readonly string[];
981
- },
982
- frontDocument: string,
983
- params: DocumentVerificationParams,
984
- userDetails: UserDetails,
985
- consent: ConsentInformation,
986
- metadata: UseSmileIDMetadata,
987
- ): DocumentVerificationRequest {
988
- const { backDocumentPath } = this._captured;
989
- return {
990
- jobType: JobType.documentVerification,
991
- selfieImage: selfie.selfiePath,
992
- livenessImages: selfie.livenessPaths,
993
- frontDocument,
994
- ...(backDocumentPath ? { backDocument: backDocumentPath } : {}),
995
- country: params.country,
996
- // Optional — omit when unset so the server classifies the document.
997
- ...(params.idType ? { idType: params.idType } : {}),
998
- userDetails,
999
- consent,
1000
- metadata,
1001
- expectedLivenessImageCount: this._expectedLivenessImageCount(),
1002
- };
1003
- }
1004
-
1005
- private _buildEnhancedDocumentRequest(
1025
+ /** Capture-artifact fields shared by both document request variants. */
1026
+ private _documentRequestFields(
1006
1027
  selfie: {
1007
1028
  readonly selfiePath: string;
1008
1029
  readonly livenessPaths: readonly string[];
1009
1030
  },
1010
1031
  frontDocument: string,
1011
- params: EnhancedDocumentVerificationParams,
1012
- userDetails: UserDetails,
1013
- consent: ConsentInformation,
1014
- metadata: UseSmileIDMetadata,
1015
- ): EnhancedDocumentVerificationRequest {
1032
+ ): {
1033
+ readonly selfieImage: string;
1034
+ readonly livenessImages: readonly string[];
1035
+ readonly frontDocument: string;
1036
+ readonly backDocument?: string;
1037
+ } {
1016
1038
  const { backDocumentPath } = this._captured;
1017
1039
  return {
1018
- jobType: JobType.enhancedDocumentVerification,
1019
1040
  selfieImage: selfie.selfiePath,
1020
1041
  livenessImages: selfie.livenessPaths,
1021
1042
  frontDocument,
1022
1043
  ...(backDocumentPath ? { backDocument: backDocumentPath } : {}),
1023
- country: params.country,
1024
- idType: params.idType,
1025
- userDetails,
1026
- consent,
1027
- metadata,
1028
- expectedLivenessImageCount: this._expectedLivenessImageCount(),
1029
1044
  };
1030
1045
  }
1031
1046