@smileid/usesmileid 12.0.0-beta01 → 12.0.1

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 (556) hide show
  1. package/CHANGELOG.md +15 -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/exception/agent_mode_with_enhanced_liveness_exception.d.ts +11 -0
  12. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.d.ts.map +1 -0
  13. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js +17 -0
  14. package/dist/core/exception/agent_mode_with_enhanced_liveness_exception.js.map +1 -0
  15. package/dist/core/exception/capture_validation_exceptions.d.ts +1 -0
  16. package/dist/core/exception/capture_validation_exceptions.d.ts.map +1 -1
  17. package/dist/core/exception/capture_validation_exceptions.js +1 -0
  18. package/dist/core/exception/capture_validation_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/use_smile_id_error_code.d.ts +1 -0
  24. package/dist/core/exception/use_smile_id_error_code.d.ts.map +1 -1
  25. package/dist/core/exception/use_smile_id_error_code.js +1 -0
  26. package/dist/core/exception/use_smile_id_error_code.js.map +1 -1
  27. package/dist/core/models/config.d.ts +19 -0
  28. package/dist/core/models/config.d.ts.map +1 -1
  29. package/dist/core/models/document_capture_mode.d.ts +8 -0
  30. package/dist/core/models/document_capture_mode.d.ts.map +1 -1
  31. package/dist/core/models/document_capture_mode.js +24 -0
  32. package/dist/core/models/document_capture_mode.js.map +1 -1
  33. package/dist/core/models/document_type.d.ts +2 -2
  34. package/dist/core/models/document_type.d.ts.map +1 -1
  35. package/dist/core/models/document_type.js +7 -5
  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/core/models/result.d.ts +9 -8
  44. package/dist/core/models/result.d.ts.map +1 -1
  45. package/dist/core/models/result.js +5 -6
  46. package/dist/core/models/result.js.map +1 -1
  47. package/dist/data/api/interceptors/smile_auth_interceptor.d.ts.map +1 -1
  48. package/dist/data/api/interceptors/smile_auth_interceptor.js +3 -17
  49. package/dist/data/api/interceptors/smile_auth_interceptor.js.map +1 -1
  50. package/dist/data/api/interceptors/smile_integrity_interceptor.d.ts +11 -0
  51. package/dist/data/api/interceptors/smile_integrity_interceptor.d.ts.map +1 -1
  52. package/dist/data/api/interceptors/smile_integrity_interceptor.js +13 -0
  53. package/dist/data/api/interceptors/smile_integrity_interceptor.js.map +1 -1
  54. package/dist/data/api/interceptors/smile_request_mac_interceptor.d.ts +8 -0
  55. package/dist/data/api/interceptors/smile_request_mac_interceptor.d.ts.map +1 -1
  56. package/dist/data/api/interceptors/smile_request_mac_interceptor.js +11 -0
  57. package/dist/data/api/interceptors/smile_request_mac_interceptor.js.map +1 -1
  58. package/dist/data/api/smile_id_api.d.ts +1 -1
  59. package/dist/data/api/smile_id_api.d.ts.map +1 -1
  60. package/dist/data/compression/compressible_asset.d.ts +41 -0
  61. package/dist/data/compression/compressible_asset.d.ts.map +1 -0
  62. package/dist/data/compression/compressible_asset.js +34 -0
  63. package/dist/data/compression/compressible_asset.js.map +1 -0
  64. package/dist/data/compression/payload_compression_config.d.ts +36 -0
  65. package/dist/data/compression/payload_compression_config.d.ts.map +1 -0
  66. package/dist/data/compression/payload_compression_config.js +52 -0
  67. package/dist/data/compression/payload_compression_config.js.map +1 -0
  68. package/dist/data/compression/payload_compression_runner.d.ts +29 -0
  69. package/dist/data/compression/payload_compression_runner.d.ts.map +1 -0
  70. package/dist/data/compression/payload_compression_runner.js +55 -0
  71. package/dist/data/compression/payload_compression_runner.js.map +1 -0
  72. package/dist/data/compression/payload_compressor.d.ts +40 -0
  73. package/dist/data/compression/payload_compressor.d.ts.map +1 -0
  74. package/dist/data/compression/payload_compressor.js +126 -0
  75. package/dist/data/compression/payload_compressor.js.map +1 -0
  76. package/dist/data/compression/uri_compressible_asset.d.ts +23 -0
  77. package/dist/data/compression/uri_compressible_asset.d.ts.map +1 -0
  78. package/dist/data/compression/uri_compressible_asset.js +77 -0
  79. package/dist/data/compression/uri_compressible_asset.js.map +1 -0
  80. package/dist/data/dsl/builder/cache_config_builder.d.ts +2 -1
  81. package/dist/data/dsl/builder/cache_config_builder.d.ts.map +1 -1
  82. package/dist/data/dsl/builder/cache_config_builder.js +2 -1
  83. package/dist/data/dsl/builder/cache_config_builder.js.map +1 -1
  84. package/dist/data/dsl/builder/config_builder.d.ts +40 -14
  85. package/dist/data/dsl/builder/config_builder.d.ts.map +1 -1
  86. package/dist/data/dsl/builder/config_builder.js +40 -14
  87. package/dist/data/dsl/builder/config_builder.js.map +1 -1
  88. package/dist/data/dsl/builder/interceptor_config_builder.d.ts +9 -7
  89. package/dist/data/dsl/builder/interceptor_config_builder.d.ts.map +1 -1
  90. package/dist/data/dsl/builder/interceptor_config_builder.js +9 -7
  91. package/dist/data/dsl/builder/interceptor_config_builder.js.map +1 -1
  92. package/dist/data/dsl/builder/retry_config_builder.d.ts +19 -4
  93. package/dist/data/dsl/builder/retry_config_builder.d.ts.map +1 -1
  94. package/dist/data/dsl/builder/retry_config_builder.js +21 -6
  95. package/dist/data/dsl/builder/retry_config_builder.js.map +1 -1
  96. package/dist/data/dsl/builder/timeout_config_builder.d.ts +4 -0
  97. package/dist/data/dsl/builder/timeout_config_builder.d.ts.map +1 -1
  98. package/dist/data/dsl/builder/timeout_config_builder.js +4 -0
  99. package/dist/data/dsl/builder/timeout_config_builder.js.map +1 -1
  100. package/dist/data/dsl/use_smile_id_network_builder.d.ts +17 -0
  101. package/dist/data/dsl/use_smile_id_network_builder.d.ts.map +1 -1
  102. package/dist/data/dsl/use_smile_id_network_builder.js +17 -0
  103. package/dist/data/dsl/use_smile_id_network_builder.js.map +1 -1
  104. package/dist/data/factory/networking_factory.d.ts.map +1 -1
  105. package/dist/data/factory/networking_factory.js +11 -1
  106. package/dist/data/factory/networking_factory.js.map +1 -1
  107. package/dist/data/repository/use_smile_id_repository_impl.d.ts +6 -0
  108. package/dist/data/repository/use_smile_id_repository_impl.d.ts.map +1 -1
  109. package/dist/data/repository/use_smile_id_repository_impl.js +82 -8
  110. package/dist/data/repository/use_smile_id_repository_impl.js.map +1 -1
  111. package/dist/data/security/request_canonicalizer.d.ts.map +1 -1
  112. package/dist/data/security/request_canonicalizer.js +14 -2
  113. package/dist/data/security/request_canonicalizer.js.map +1 -1
  114. package/dist/data/security/security_policy.d.ts +43 -0
  115. package/dist/data/security/security_policy.d.ts.map +1 -0
  116. package/dist/data/security/security_policy.js +61 -0
  117. package/dist/data/security/security_policy.js.map +1 -0
  118. package/dist/data/smile_id_urls.d.ts +1 -1
  119. package/dist/data/smile_id_urls.js +1 -1
  120. package/dist/data/smile_id_urls.js.map +1 -1
  121. package/dist/data/util/retry_util.d.ts +2 -2
  122. package/dist/data/util/retry_util.d.ts.map +1 -1
  123. package/dist/data/util/retry_util.js +7 -1
  124. package/dist/data/util/retry_util.js.map +1 -1
  125. package/dist/data/util/use_smile_id_token_decoder.d.ts +22 -0
  126. package/dist/data/util/use_smile_id_token_decoder.d.ts.map +1 -1
  127. package/dist/data/util/use_smile_id_token_decoder.js +58 -24
  128. package/dist/data/util/use_smile_id_token_decoder.js.map +1 -1
  129. package/dist/index.d.ts +2 -2
  130. package/dist/index.d.ts.map +1 -1
  131. package/dist/index.js +2 -2
  132. package/dist/index.js.map +1 -1
  133. package/dist/l10n/strings_en.d.ts +2 -1
  134. package/dist/l10n/strings_en.d.ts.map +1 -1
  135. package/dist/l10n/strings_en.js +2 -1
  136. package/dist/l10n/strings_en.js.map +1 -1
  137. package/dist/l10n/use_smile_id_localizations.d.ts +1 -0
  138. package/dist/l10n/use_smile_id_localizations.d.ts.map +1 -1
  139. package/dist/l10n/use_smile_id_localizations.js +3 -0
  140. package/dist/l10n/use_smile_id_localizations.js.map +1 -1
  141. package/dist/metadata/builder_configuration.d.ts +44 -0
  142. package/dist/metadata/builder_configuration.d.ts.map +1 -0
  143. package/dist/metadata/builder_configuration.js +35 -0
  144. package/dist/metadata/builder_configuration.js.map +1 -0
  145. package/dist/metadata/captured_image_properties.d.ts +11 -0
  146. package/dist/metadata/captured_image_properties.d.ts.map +1 -0
  147. package/dist/metadata/captured_image_properties.js +11 -0
  148. package/dist/metadata/captured_image_properties.js.map +1 -0
  149. package/dist/metadata/device_orientation_collector.d.ts +17 -0
  150. package/dist/metadata/device_orientation_collector.d.ts.map +1 -0
  151. package/dist/metadata/device_orientation_collector.js +66 -0
  152. package/dist/metadata/device_orientation_collector.js.map +1 -0
  153. package/dist/metadata/geolocation.d.ts +20 -0
  154. package/dist/metadata/geolocation.d.ts.map +1 -0
  155. package/dist/metadata/geolocation.js +40 -0
  156. package/dist/metadata/geolocation.js.map +1 -0
  157. package/dist/metadata/metadata_configuration_mappers.d.ts +47 -0
  158. package/dist/metadata/metadata_configuration_mappers.d.ts.map +1 -0
  159. package/dist/metadata/metadata_configuration_mappers.js +178 -0
  160. package/dist/metadata/metadata_configuration_mappers.js.map +1 -0
  161. package/dist/metadata/model_configuration.d.ts +41 -0
  162. package/dist/metadata/model_configuration.d.ts.map +1 -0
  163. package/dist/metadata/model_configuration.js +44 -0
  164. package/dist/metadata/model_configuration.js.map +1 -0
  165. package/dist/metadata/optional_native_modules.d.ts +79 -3
  166. package/dist/metadata/optional_native_modules.d.ts.map +1 -1
  167. package/dist/metadata/optional_native_modules.js +108 -3
  168. package/dist/metadata/optional_native_modules.js.map +1 -1
  169. package/dist/metadata/use_smile_id_metadata.d.ts +45 -7
  170. package/dist/metadata/use_smile_id_metadata.d.ts.map +1 -1
  171. package/dist/metadata/use_smile_id_metadata.js +85 -13
  172. package/dist/metadata/use_smile_id_metadata.js.map +1 -1
  173. package/dist/metadata/use_smile_id_metadata_factory.d.ts +37 -0
  174. package/dist/metadata/use_smile_id_metadata_factory.d.ts.map +1 -1
  175. package/dist/metadata/use_smile_id_metadata_factory.js +197 -16
  176. package/dist/metadata/use_smile_id_metadata_factory.js.map +1 -1
  177. package/dist/presentation/assets/svg/document_shimmer_strings.d.ts +14 -0
  178. package/dist/presentation/assets/svg/document_shimmer_strings.d.ts.map +1 -0
  179. package/dist/presentation/assets/svg/document_shimmer_strings.js +14 -0
  180. package/dist/presentation/assets/svg/document_shimmer_strings.js.map +1 -0
  181. package/dist/presentation/assets/svg/instruction_svg_strings.d.ts +12 -4
  182. package/dist/presentation/assets/svg/instruction_svg_strings.d.ts.map +1 -1
  183. package/dist/presentation/assets/svg/instruction_svg_strings.js +19 -8
  184. package/dist/presentation/assets/svg/instruction_svg_strings.js.map +1 -1
  185. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts +0 -5
  186. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.d.ts.map +1 -1
  187. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js +0 -5
  188. package/dist/presentation/components/helpers/enhanced_smart_selfie_animation_frames.js.map +1 -1
  189. package/dist/presentation/components/icons/index.d.ts +24 -8
  190. package/dist/presentation/components/icons/index.d.ts.map +1 -1
  191. package/dist/presentation/components/icons/index.js +31 -11
  192. package/dist/presentation/components/icons/index.js.map +1 -1
  193. package/dist/presentation/components/use_smile_id_attribution.d.ts +1 -1
  194. package/dist/presentation/components/use_smile_id_button.d.ts +2 -2
  195. package/dist/presentation/components/use_smile_id_button.js +4 -4
  196. package/dist/presentation/components/use_smile_id_button.js.map +1 -1
  197. package/dist/presentation/components/use_smile_id_capture_content.d.ts +13 -0
  198. package/dist/presentation/components/use_smile_id_capture_content.d.ts.map +1 -1
  199. package/dist/presentation/components/use_smile_id_capture_content.js +15 -9
  200. package/dist/presentation/components/use_smile_id_capture_content.js.map +1 -1
  201. package/dist/presentation/components/use_smile_id_captured_document_image.d.ts +2 -3
  202. package/dist/presentation/components/use_smile_id_captured_document_image.d.ts.map +1 -1
  203. package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -3
  204. package/dist/presentation/components/use_smile_id_captured_document_image.js.map +1 -1
  205. package/dist/presentation/components/use_smile_id_document_capture_controls.d.ts +12 -6
  206. package/dist/presentation/components/use_smile_id_document_capture_controls.d.ts.map +1 -1
  207. package/dist/presentation/components/use_smile_id_document_capture_controls.js +59 -16
  208. package/dist/presentation/components/use_smile_id_document_capture_controls.js.map +1 -1
  209. package/dist/presentation/components/use_smile_id_document_overlay.d.ts +22 -5
  210. package/dist/presentation/components/use_smile_id_document_overlay.d.ts.map +1 -1
  211. package/dist/presentation/components/use_smile_id_document_overlay.js +80 -40
  212. package/dist/presentation/components/use_smile_id_document_overlay.js.map +1 -1
  213. package/dist/presentation/components/use_smile_id_document_shimmer.d.ts +15 -0
  214. package/dist/presentation/components/use_smile_id_document_shimmer.d.ts.map +1 -0
  215. package/dist/presentation/components/use_smile_id_document_shimmer.js +23 -0
  216. package/dist/presentation/components/use_smile_id_document_shimmer.js.map +1 -0
  217. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts +5 -2
  218. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.d.ts.map +1 -1
  219. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js +9 -6
  220. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_clip.js.map +1 -1
  221. package/dist/presentation/components/use_smile_id_guideline_thumbnail.d.ts.map +1 -1
  222. package/dist/presentation/components/use_smile_id_guideline_thumbnail.js +84 -1
  223. package/dist/presentation/components/use_smile_id_guideline_thumbnail.js.map +1 -1
  224. package/dist/presentation/components/use_smile_id_instructions_hero.d.ts +3 -1
  225. package/dist/presentation/components/use_smile_id_instructions_hero.d.ts.map +1 -1
  226. package/dist/presentation/components/use_smile_id_instructions_hero.js +25 -5
  227. package/dist/presentation/components/use_smile_id_instructions_hero.js.map +1 -1
  228. package/dist/presentation/components/use_smile_id_oval_border_palette.d.ts.map +1 -1
  229. package/dist/presentation/components/use_smile_id_oval_border_palette.js +7 -3
  230. package/dist/presentation/components/use_smile_id_oval_border_palette.js.map +1 -1
  231. package/dist/presentation/flow/dsl/builder/config_builder.d.ts +28 -0
  232. package/dist/presentation/flow/dsl/builder/config_builder.d.ts.map +1 -1
  233. package/dist/presentation/flow/dsl/builder/config_builder.js +30 -0
  234. package/dist/presentation/flow/dsl/builder/config_builder.js.map +1 -1
  235. package/dist/presentation/flow/dsl/builder/ml_builder.d.ts +13 -4
  236. package/dist/presentation/flow/dsl/builder/ml_builder.d.ts.map +1 -1
  237. package/dist/presentation/flow/dsl/builder/ml_builder.js +3 -2
  238. package/dist/presentation/flow/dsl/builder/ml_builder.js.map +1 -1
  239. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +110 -12
  240. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts.map +1 -1
  241. package/dist/presentation/flow/dsl/builder/screens_builder.js +110 -12
  242. package/dist/presentation/flow/dsl/builder/screens_builder.js.map +1 -1
  243. package/dist/presentation/flow/dsl/builder/theme_builder.d.ts +15 -2
  244. package/dist/presentation/flow/dsl/builder/theme_builder.d.ts.map +1 -1
  245. package/dist/presentation/flow/dsl/builder/theme_builder.js +15 -2
  246. package/dist/presentation/flow/dsl/builder/theme_builder.js.map +1 -1
  247. package/dist/presentation/flow/dsl/use_smile_id_builder.d.ts +6 -4
  248. package/dist/presentation/flow/dsl/use_smile_id_builder.d.ts.map +1 -1
  249. package/dist/presentation/flow/dsl/use_smile_id_builder.js.map +1 -1
  250. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +62 -36
  251. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts.map +1 -1
  252. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +62 -36
  253. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js.map +1 -1
  254. package/dist/presentation/flow/flow_navigation_manager.d.ts +44 -5
  255. package/dist/presentation/flow/flow_navigation_manager.d.ts.map +1 -1
  256. package/dist/presentation/flow/flow_navigation_manager.js +148 -11
  257. package/dist/presentation/flow/flow_navigation_manager.js.map +1 -1
  258. package/dist/presentation/flow/model/instruction_guideline.d.ts +9 -0
  259. package/dist/presentation/flow/model/instruction_guideline.d.ts.map +1 -1
  260. package/dist/presentation/flow/model/instructions_defaults.d.ts +11 -2
  261. package/dist/presentation/flow/model/instructions_defaults.d.ts.map +1 -1
  262. package/dist/presentation/flow/model/instructions_defaults.js +74 -26
  263. package/dist/presentation/flow/model/instructions_defaults.js.map +1 -1
  264. package/dist/presentation/flow/screen/orchestrated_capture_screen.d.ts.map +1 -1
  265. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +48 -14
  266. package/dist/presentation/flow/screen/orchestrated_capture_screen.js.map +1 -1
  267. package/dist/presentation/flow/screen/orchestrated_consent_screen.d.ts.map +1 -1
  268. package/dist/presentation/flow/screen/orchestrated_consent_screen.js +3 -1
  269. package/dist/presentation/flow/screen/orchestrated_consent_screen.js.map +1 -1
  270. package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
  271. package/dist/presentation/flow/screen/orchestrated_instructions_screen.js.map +1 -1
  272. package/dist/presentation/flow/screen/orchestrated_preview_screen.d.ts.map +1 -1
  273. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +3 -9
  274. package/dist/presentation/flow/screen/orchestrated_preview_screen.js.map +1 -1
  275. package/dist/presentation/flow/screen/orchestrated_processing_screen.js +3 -3
  276. package/dist/presentation/flow/screen/orchestrated_processing_screen.js.map +1 -1
  277. package/dist/presentation/flow/use_smile_id_flow.d.ts.map +1 -1
  278. package/dist/presentation/flow/use_smile_id_flow.js +10 -4
  279. package/dist/presentation/flow/use_smile_id_flow.js.map +1 -1
  280. package/dist/presentation/flow/validation/flow_validator.d.ts.map +1 -1
  281. package/dist/presentation/flow/validation/flow_validator.js +42 -20
  282. package/dist/presentation/flow/validation/flow_validator.js.map +1 -1
  283. package/dist/presentation/flow/validation/job_type_validator.d.ts.map +1 -1
  284. package/dist/presentation/flow/validation/job_type_validator.js +14 -0
  285. package/dist/presentation/flow/validation/job_type_validator.js.map +1 -1
  286. package/dist/presentation/screen/helpers/capture_screen_helpers.d.ts.map +1 -1
  287. package/dist/presentation/screen/helpers/capture_screen_helpers.js +1 -0
  288. package/dist/presentation/screen/helpers/capture_screen_helpers.js.map +1 -1
  289. package/dist/presentation/screen/helpers/capture_ui_state.d.ts +6 -6
  290. package/dist/presentation/screen/helpers/capture_ui_state.d.ts.map +1 -1
  291. package/dist/presentation/screen/helpers/capture_ui_state.js +19 -19
  292. package/dist/presentation/screen/helpers/capture_ui_state.js.map +1 -1
  293. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts +2 -2
  294. package/dist/presentation/screen/helpers/ess_turn_haptics.d.ts.map +1 -1
  295. package/dist/presentation/screen/helpers/ess_turn_haptics.js +6 -6
  296. package/dist/presentation/screen/helpers/ess_turn_haptics.js.map +1 -1
  297. package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts +1 -0
  298. package/dist/presentation/screen/helpers/instructions_screen_helpers.d.ts.map +1 -1
  299. package/dist/presentation/screen/helpers/instructions_screen_helpers.js +6 -2
  300. package/dist/presentation/screen/helpers/instructions_screen_helpers.js.map +1 -1
  301. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts +5 -5
  302. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.d.ts.map +1 -1
  303. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js +14 -11
  304. package/dist/presentation/screen/helpers/scan_state_to_overlay_state.js.map +1 -1
  305. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts +13 -3
  306. package/dist/presentation/screen/helpers/use_capture_orientation_lock.d.ts.map +1 -1
  307. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js +48 -6
  308. package/dist/presentation/screen/helpers/use_capture_orientation_lock.js.map +1 -1
  309. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +21 -3
  310. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts.map +1 -1
  311. package/dist/presentation/screen/use_smile_id_capture_screen.js +188 -32
  312. package/dist/presentation/screen/use_smile_id_capture_screen.js.map +1 -1
  313. package/dist/presentation/screen/use_smile_id_consent_screen.d.ts +7 -0
  314. package/dist/presentation/screen/use_smile_id_consent_screen.d.ts.map +1 -1
  315. package/dist/presentation/screen/use_smile_id_consent_screen.js +6 -5
  316. package/dist/presentation/screen/use_smile_id_consent_screen.js.map +1 -1
  317. package/dist/presentation/screen/use_smile_id_document_back_instructions.d.ts.map +1 -1
  318. package/dist/presentation/screen/use_smile_id_document_back_instructions.js +5 -13
  319. package/dist/presentation/screen/use_smile_id_document_back_instructions.js.map +1 -1
  320. package/dist/presentation/screen/use_smile_id_document_preview_screen.d.ts +2 -3
  321. package/dist/presentation/screen/use_smile_id_document_preview_screen.d.ts.map +1 -1
  322. package/dist/presentation/screen/use_smile_id_document_preview_screen.js +17 -12
  323. package/dist/presentation/screen/use_smile_id_document_preview_screen.js.map +1 -1
  324. package/dist/presentation/screen/use_smile_id_instructions_screen.d.ts +2 -0
  325. package/dist/presentation/screen/use_smile_id_instructions_screen.d.ts.map +1 -1
  326. package/dist/presentation/screen/use_smile_id_instructions_screen.js +2 -1
  327. package/dist/presentation/screen/use_smile_id_instructions_screen.js.map +1 -1
  328. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts +7 -26
  329. package/dist/presentation/screen/use_smile_id_preview_screen.d.ts.map +1 -1
  330. package/dist/presentation/screen/use_smile_id_preview_screen.js +10 -20
  331. package/dist/presentation/screen/use_smile_id_preview_screen.js.map +1 -1
  332. package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +2 -3
  333. package/dist/presentation/screen/use_smile_id_processing_screen.d.ts.map +1 -1
  334. package/dist/presentation/screen/use_smile_id_processing_screen.js +4 -3
  335. package/dist/presentation/screen/use_smile_id_processing_screen.js.map +1 -1
  336. package/dist/presentation/theme/theme.d.ts +1 -1
  337. package/dist/presentation/theme/theme.d.ts.map +1 -1
  338. package/dist/presentation/theme/theme.js +20 -18
  339. package/dist/presentation/theme/theme.js.map +1 -1
  340. package/dist/presentation/utils/force_max_brightness.d.ts +4 -4
  341. package/dist/presentation/utils/locale_utils.d.ts +10 -4
  342. package/dist/presentation/utils/locale_utils.d.ts.map +1 -1
  343. package/dist/presentation/utils/locale_utils.js +13 -5
  344. package/dist/presentation/utils/locale_utils.js.map +1 -1
  345. package/dist/testing/fake_face_analyzer.d.ts +43 -0
  346. package/dist/testing/fake_face_analyzer.d.ts.map +1 -0
  347. package/dist/testing/fake_face_analyzer.js +52 -0
  348. package/dist/testing/fake_face_analyzer.js.map +1 -0
  349. package/dist/testing/index.d.ts +5 -0
  350. package/dist/testing/index.d.ts.map +1 -0
  351. package/dist/testing/index.js +7 -0
  352. package/dist/testing/index.js.map +1 -0
  353. package/dist/testing/scenario_expectations.d.ts +28 -0
  354. package/dist/testing/scenario_expectations.d.ts.map +1 -0
  355. package/dist/testing/scenario_expectations.js +65 -0
  356. package/dist/testing/scenario_expectations.js.map +1 -0
  357. package/dist/testing/scenario_loader.d.ts +11 -0
  358. package/dist/testing/scenario_loader.d.ts.map +1 -0
  359. package/dist/testing/scenario_loader.js +64 -0
  360. package/dist/testing/scenario_loader.js.map +1 -0
  361. package/dist/testing/scripted_scan_results.d.ts +17 -0
  362. package/dist/testing/scripted_scan_results.d.ts.map +1 -0
  363. package/dist/testing/scripted_scan_results.js +52 -0
  364. package/dist/testing/scripted_scan_results.js.map +1 -0
  365. package/package.json +34 -7
  366. package/src/__tests__/dsl.test.ts +51 -1
  367. package/src/__tests__/react_native_mock.tsx +8 -0
  368. package/src/__tests__/setup_react_native.ts +17 -2
  369. package/src/__tests__/snapshots/__snapshots__/screens_structural.test.tsx.snap +5261 -0
  370. package/src/__tests__/snapshots/screens_structural.test.tsx +230 -0
  371. package/src/camera/__tests__/use_smile_id_camera_system.test.ts +103 -0
  372. package/src/camera/camera_defaults.ts +11 -9
  373. package/src/camera/orientation_degrees.ts +5 -6
  374. package/src/core/exception/__tests__/builder_validation_exceptions.test.ts +67 -0
  375. package/src/core/exception/agent_mode_with_enhanced_liveness_exception.ts +20 -0
  376. package/src/core/exception/capture_validation_exceptions.ts +1 -0
  377. package/src/core/exception/index.ts +1 -0
  378. package/src/core/exception/use_smile_id_error_code.ts +1 -0
  379. package/src/core/models/__tests__/document_type.test.ts +7 -5
  380. package/src/core/models/__tests__/flow_models.test.ts +26 -0
  381. package/src/core/models/config.ts +22 -0
  382. package/src/core/models/document_capture_mode.ts +27 -0
  383. package/src/core/models/document_type.ts +7 -5
  384. package/src/core/models/global_privacy_control.ts +45 -0
  385. package/src/core/models/network_config.ts +2 -2
  386. package/src/core/models/result.ts +9 -8
  387. package/src/data/api/__tests__/smile_id_api.test.ts +1 -1
  388. package/src/data/api/interceptors/__tests__/gzip_interceptor.test.ts +83 -0
  389. package/src/data/api/interceptors/__tests__/smile_integrity_interceptor.test.ts +37 -1
  390. package/src/data/api/interceptors/__tests__/smile_request_mac_interceptor.test.ts +59 -1
  391. package/src/data/api/interceptors/smile_auth_interceptor.ts +3 -18
  392. package/src/data/api/interceptors/smile_integrity_interceptor.ts +20 -0
  393. package/src/data/api/interceptors/smile_request_mac_interceptor.ts +13 -0
  394. package/src/data/compression/__tests__/payload_compressor.test.ts +200 -0
  395. package/src/data/compression/__tests__/payload_compressor_real_jpeg.test.ts +226 -0
  396. package/src/data/compression/compressible_asset.ts +62 -0
  397. package/src/data/compression/payload_compression_config.ts +53 -0
  398. package/src/data/compression/payload_compression_runner.ts +102 -0
  399. package/src/data/compression/payload_compressor.ts +164 -0
  400. package/src/data/compression/uri_compressible_asset.ts +85 -0
  401. package/src/data/dsl/builder/__tests__/config_builders.test.ts +62 -0
  402. package/src/data/dsl/builder/cache_config_builder.ts +3 -1
  403. package/src/data/dsl/builder/config_builder.ts +45 -14
  404. package/src/data/dsl/builder/interceptor_config_builder.ts +9 -7
  405. package/src/data/dsl/builder/retry_config_builder.ts +27 -6
  406. package/src/data/dsl/builder/timeout_config_builder.ts +7 -0
  407. package/src/data/dsl/use_smile_id_network_builder.ts +17 -0
  408. package/src/data/factory/__tests__/retry_gzip_mac_composition.test.ts +163 -0
  409. package/src/data/factory/__tests__/signed_job_submission_headers.test.ts +1 -1
  410. package/src/data/factory/networking_factory.ts +14 -1
  411. package/src/data/repository/__tests__/network_retries.test.ts +117 -0
  412. package/src/data/repository/__tests__/use_smile_id_repository.test.ts +1 -1
  413. package/src/data/repository/use_smile_id_repository_impl.ts +96 -9
  414. package/src/data/security/__tests__/request_canonicalizer.test.ts +132 -0
  415. package/src/data/security/__tests__/security_policy.test.ts +96 -0
  416. package/src/data/security/__tests__/smile_id_crypto_manager.test.ts +18 -0
  417. package/src/data/security/request_canonicalizer.ts +15 -2
  418. package/src/data/security/security_policy.ts +70 -0
  419. package/src/data/smile_id_urls.ts +1 -1
  420. package/src/data/util/__tests__/retry_util.test.ts +25 -0
  421. package/src/data/util/__tests__/use_smile_id_token_decoder.test.ts +57 -1
  422. package/src/data/util/retry_util.ts +12 -3
  423. package/src/data/util/use_smile_id_token_decoder.ts +76 -28
  424. package/src/index.ts +6 -4
  425. package/src/l10n/strings_en.ts +2 -1
  426. package/src/l10n/use_smile_id_localizations.ts +3 -0
  427. package/src/metadata/__tests__/device_orientation_collector.test.ts +15 -0
  428. package/src/metadata/__tests__/metadata_configuration_mappers.test.ts +254 -0
  429. package/src/metadata/__tests__/metadata_configuration_objects.test.ts +86 -0
  430. package/src/metadata/__tests__/metadata_device_context.test.ts +147 -0
  431. package/src/metadata/__tests__/metadata_device_os_android.test.ts +18 -0
  432. package/src/metadata/__tests__/metadata_golden_fixture.json +13 -0
  433. package/src/metadata/__tests__/metadata_payload_golden.test.ts +193 -0
  434. package/src/metadata/__tests__/use_smile_id_metadata.test.ts +19 -25
  435. package/src/metadata/builder_configuration.ts +69 -0
  436. package/src/metadata/captured_image_properties.ts +10 -0
  437. package/src/metadata/device_orientation_collector.ts +75 -0
  438. package/src/metadata/geolocation.ts +46 -0
  439. package/src/metadata/metadata_configuration_mappers.ts +248 -0
  440. package/src/metadata/model_configuration.ts +68 -0
  441. package/src/metadata/optional_native_modules.ts +157 -3
  442. package/src/metadata/use_smile_id_metadata.ts +140 -17
  443. package/src/metadata/use_smile_id_metadata_factory.ts +211 -16
  444. package/src/presentation/assets/si_document_green_book_hero_animation.json +1 -4668
  445. package/src/presentation/assets/si_document_hero_animation.json +1 -4799
  446. package/src/presentation/assets/si_document_hero_back_animation.json +1 -2039
  447. package/src/presentation/assets/si_document_passport_hero_animation.json +1 -14491
  448. package/src/presentation/assets/si_ess_all_directions_hero_animation.json +1 -0
  449. package/src/presentation/assets/si_ess_animation_all_directions.json +1 -5575
  450. package/src/presentation/assets/si_selfie_hero_animation.json +1 -1439
  451. package/src/presentation/assets/si_smile_to_capture_animation.json +1 -1439
  452. package/src/presentation/assets/svg/document_shimmer_strings.ts +17 -0
  453. package/src/presentation/assets/svg/instruction_svg_strings.ts +34 -12
  454. package/src/presentation/components/__tests__/enhanced_smart_selfie_animation_frames.test.ts +1 -3
  455. package/src/presentation/components/__tests__/use_smile_id_enhanced_smart_selfie_clip.test.tsx +12 -8
  456. package/src/presentation/components/__tests__/use_smile_id_guideline_thumbnail.test.tsx +34 -1
  457. package/src/presentation/components/__tests__/use_smile_id_instructions_hero.test.tsx +71 -0
  458. package/src/presentation/components/__tests__/use_smile_id_oval_border_palette.test.ts +7 -5
  459. package/src/presentation/components/helpers/enhanced_smart_selfie_animation_frames.ts +0 -6
  460. package/src/presentation/components/icons/index.tsx +65 -18
  461. package/src/presentation/components/use_smile_id_attribution.tsx +1 -1
  462. package/src/presentation/components/use_smile_id_button.tsx +4 -4
  463. package/src/presentation/components/use_smile_id_capture_content.tsx +36 -12
  464. package/src/presentation/components/use_smile_id_captured_document_image.tsx +3 -8
  465. package/src/presentation/components/use_smile_id_document_capture_controls.tsx +103 -39
  466. package/src/presentation/components/use_smile_id_document_overlay.tsx +134 -44
  467. package/src/presentation/components/use_smile_id_document_shimmer.tsx +51 -0
  468. package/src/presentation/components/use_smile_id_enhanced_smart_selfie_clip.tsx +9 -6
  469. package/src/presentation/components/use_smile_id_guideline_thumbnail.tsx +131 -2
  470. package/src/presentation/components/use_smile_id_instructions_hero.tsx +111 -60
  471. package/src/presentation/components/use_smile_id_oval_border_palette.ts +7 -3
  472. package/src/presentation/flow/__tests__/document_flow_navigation.test.ts +203 -4
  473. package/src/presentation/flow/__tests__/flow_navigation_manager.test.ts +49 -0
  474. package/src/presentation/flow/dsl/builder/__tests__/config_builder.test.ts +25 -0
  475. package/src/presentation/flow/dsl/builder/config_builder.ts +35 -0
  476. package/src/presentation/flow/dsl/builder/ml_builder.ts +13 -4
  477. package/src/presentation/flow/dsl/builder/screens_builder.ts +110 -12
  478. package/src/presentation/flow/dsl/builder/theme_builder.ts +19 -2
  479. package/src/presentation/flow/dsl/use_smile_id_builder.tsx +6 -4
  480. package/src/presentation/flow/dsl/use_smile_id_flow_builder.ts +62 -36
  481. package/src/presentation/flow/flow_navigation_manager.ts +185 -14
  482. package/src/presentation/flow/model/instruction_guideline.ts +6 -0
  483. package/src/presentation/flow/model/instructions_defaults.ts +104 -26
  484. package/src/presentation/flow/screen/orchestrated_capture_screen.tsx +60 -12
  485. package/src/presentation/flow/screen/orchestrated_consent_screen.tsx +5 -0
  486. package/src/presentation/flow/screen/orchestrated_instructions_screen.tsx +1 -0
  487. package/src/presentation/flow/screen/orchestrated_preview_screen.tsx +4 -14
  488. package/src/presentation/flow/screen/orchestrated_processing_screen.tsx +3 -3
  489. package/src/presentation/flow/use_smile_id_flow.tsx +11 -4
  490. package/src/presentation/flow/validation/__tests__/flow_validator.test.ts +61 -1
  491. package/src/presentation/flow/validation/__tests__/job_type_validator.test.ts +64 -0
  492. package/src/presentation/flow/validation/flow_validator.ts +55 -26
  493. package/src/presentation/flow/validation/job_type_validator.ts +24 -0
  494. package/src/presentation/screen/__tests__/capture_ui_state.test.ts +66 -47
  495. package/src/presentation/screen/__tests__/ess_turn_haptics.test.ts +15 -11
  496. package/src/presentation/screen/__tests__/instructions_screen_helpers.test.ts +15 -3
  497. package/src/presentation/screen/__tests__/scan_state_to_overlay_state.test.ts +47 -27
  498. package/src/presentation/screen/__tests__/use_smile_id_capture_screen.test.tsx +182 -18
  499. package/src/presentation/screen/__tests__/use_smile_id_document_back_instructions.test.tsx +1 -1
  500. package/src/presentation/screen/__tests__/use_smile_id_preview_screen.test.tsx +10 -40
  501. package/src/presentation/screen/helpers/__tests__/use_capture_orientation_lock.test.tsx +212 -0
  502. package/src/presentation/screen/helpers/capture_screen_helpers.ts +1 -0
  503. package/src/presentation/screen/helpers/capture_ui_state.ts +27 -27
  504. package/src/presentation/screen/helpers/ess_turn_haptics.ts +9 -9
  505. package/src/presentation/screen/helpers/instructions_screen_helpers.ts +7 -2
  506. package/src/presentation/screen/helpers/scan_state_to_overlay_state.ts +16 -13
  507. package/src/presentation/screen/helpers/use_capture_orientation_lock.ts +69 -7
  508. package/src/presentation/screen/use_smile_id_capture_screen.tsx +280 -54
  509. package/src/presentation/screen/use_smile_id_consent_screen.tsx +16 -5
  510. package/src/presentation/screen/use_smile_id_document_back_instructions.tsx +14 -23
  511. package/src/presentation/screen/use_smile_id_document_preview_screen.tsx +33 -23
  512. package/src/presentation/screen/use_smile_id_instructions_screen.tsx +4 -0
  513. package/src/presentation/screen/use_smile_id_preview_screen.tsx +21 -53
  514. package/src/presentation/screen/use_smile_id_processing_screen.tsx +8 -8
  515. package/src/presentation/theme/theme.ts +21 -19
  516. package/src/presentation/utils/__tests__/locale_utils.test.ts +46 -5
  517. package/src/presentation/utils/force_max_brightness.tsx +4 -4
  518. package/src/presentation/utils/locale_utils.ts +13 -4
  519. package/src/testing/__tests__/fake_face_analyzer.test.ts +42 -0
  520. package/src/testing/__tests__/scenario_expectations.test.ts +110 -0
  521. package/src/testing/__tests__/scenario_loader.test.ts +64 -0
  522. package/src/testing/fake_face_analyzer.ts +82 -0
  523. package/src/testing/index.ts +15 -0
  524. package/src/testing/scenario_expectations.ts +87 -0
  525. package/src/testing/scenario_loader.ts +83 -0
  526. package/src/testing/scripted_scan_results.ts +66 -0
  527. package/dist/camera/frame_processor_plugin.d.ts +0 -28
  528. package/dist/camera/frame_processor_plugin.d.ts.map +0 -1
  529. package/dist/camera/frame_processor_plugin.js +0 -21
  530. package/dist/camera/frame_processor_plugin.js.map +0 -1
  531. package/dist/camera/use_smile_id_frame_processor.d.ts +0 -38
  532. package/dist/camera/use_smile_id_frame_processor.d.ts.map +0 -1
  533. package/dist/camera/use_smile_id_frame_processor.js +0 -2
  534. package/dist/camera/use_smile_id_frame_processor.js.map +0 -1
  535. package/dist/presentation/components/use_smile_id_document_frame_glyph.d.ts +0 -17
  536. package/dist/presentation/components/use_smile_id_document_frame_glyph.d.ts.map +0 -1
  537. package/dist/presentation/components/use_smile_id_document_frame_glyph.js +0 -17
  538. package/dist/presentation/components/use_smile_id_document_frame_glyph.js.map +0 -1
  539. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.d.ts +0 -24
  540. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.d.ts.map +0 -1
  541. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.js +0 -91
  542. package/dist/presentation/components/use_smile_id_enhanced_smart_selfie_animation.js.map +0 -1
  543. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts +0 -15
  544. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.d.ts.map +0 -1
  545. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js +0 -19
  546. package/dist/presentation/components/use_smile_id_face_silhouette_overlay.js.map +0 -1
  547. package/dist/presentation/components/use_smile_id_instruction_card.d.ts +0 -31
  548. package/dist/presentation/components/use_smile_id_instruction_card.d.ts.map +0 -1
  549. package/dist/presentation/components/use_smile_id_instruction_card.js +0 -43
  550. package/dist/presentation/components/use_smile_id_instruction_card.js.map +0 -1
  551. package/dist/presentation/screen/helpers/use_document_orientation_lock.d.ts +0 -24
  552. package/dist/presentation/screen/helpers/use_document_orientation_lock.d.ts.map +0 -1
  553. package/dist/presentation/screen/helpers/use_document_orientation_lock.js +0 -47
  554. package/dist/presentation/screen/helpers/use_document_orientation_lock.js.map +0 -1
  555. package/src/presentation/components/use_smile_id_document_frame_glyph.tsx +0 -162
  556. package/src/presentation/components/use_smile_id_face_silhouette_overlay.tsx +0 -27
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Partner-declared Global Privacy Control posture for the consent screen
3
+ * (`global_privacy_control` on the wire).
4
+ *
5
+ * Native mobile platforms expose no OS-level equivalent of the web's GPC
6
+ * signal, so the SDK performs no runtime user-signal tracking: the partner
7
+ * declares the posture once at initialization
8
+ * (`b.config((c) => { c.globalPrivacyControl = ...; })`) and it governs
9
+ * every session.
10
+ *
11
+ * Const-object pattern (see `KameraEventType`-style types): enum ergonomics,
12
+ * string wire format.
13
+ */
14
+ export const GlobalPrivacyControl = {
15
+ /**
16
+ * Default. Forces the opt-out consent layout universally: consent starts
17
+ * granted (pre-selected) for every session regardless of region — the
18
+ * user unticks to opt out. Regional detection is never consulted.
19
+ */
20
+ OptOut: "opt-out",
21
+
22
+ /**
23
+ * Legacy regional mechanics: consent starts granted outside the EU and
24
+ * starts ungranted (explicit opt-in required) when the device locale is
25
+ * an EU member state.
26
+ */
27
+ OptIn: "opt-in",
28
+ } as const;
29
+ export type GlobalPrivacyControl =
30
+ (typeof GlobalPrivacyControl)[keyof typeof GlobalPrivacyControl];
31
+
32
+ /** Configuration key this setting is transported under. */
33
+ export const GLOBAL_PRIVACY_CONTROL_WIRE_KEY = "global_privacy_control";
34
+
35
+ /**
36
+ * Parses a wire value ("opt-out" / "opt-in"); anything else falls back to
37
+ * the `OptOut` baseline default.
38
+ */
39
+ export function globalPrivacyControlFromWire(
40
+ value: string | null | undefined,
41
+ ): GlobalPrivacyControl {
42
+ return value === GlobalPrivacyControl.OptIn
43
+ ? GlobalPrivacyControl.OptIn
44
+ : GlobalPrivacyControl.OptOut;
45
+ }
@@ -97,10 +97,10 @@ export type CacheConfig = {
97
97
  };
98
98
 
99
99
  /**
100
- * Retry strategy.
100
+ * Retry strategy. `none` is the default.
101
+ * - `none`: one attempt, no retry — `maxAttempts` is forced to 1
101
102
  * - `exponential`: `delay = min(initialDelay × multiplier^attempt, maxDelay)` plus `±jitter`
102
103
  * - `fixed`: constant `fixedDelay` between attempts
103
- * - `none`: no retry (`maxAttempts: 1`)
104
104
  */
105
105
  export enum RetryStrategy {
106
106
  exponential = "exponential",
@@ -1,31 +1,32 @@
1
1
  /**
2
- * A result type for SDK operations that makes error handling explicit.
2
+ * The outcome of an SDK operation: success with a value, failure with an
3
+ * error, or cancellation when the user exits the flow. Surfaced to
4
+ * partners through `onResult`.
3
5
  *
4
6
  * Partners never need to unwrap a `UseSmileIDResult` directly — all
5
7
  * errors are automatically surfaced through the `onResult` callback.
6
8
  * The discriminated union lets partner code `switch (result.status)`
7
- * and exhaust both branches without try/catch ceremony.
9
+ * and exhaust the branches without try/catch ceremony.
8
10
  */
9
11
  export type UseSmileIDResult<T> =
10
12
  | { readonly status: "success"; readonly value: T }
11
13
  | { readonly status: "failure"; readonly error: unknown }
12
14
  | { readonly status: "cancelled" };
13
15
 
14
- /** Construct a successful result carrying `value`. */
16
+ /** A successful outcome carrying the resulting value. */
15
17
  export function success<T>(value: T): UseSmileIDResult<T> {
16
18
  return { status: "success", value };
17
19
  }
18
20
 
19
- /** Construct a failed result carrying `error`. */
21
+ /** A failed outcome carrying the causing error. */
20
22
  export function failure<T>(error: unknown): UseSmileIDResult<T> {
21
23
  return { status: "failure", error };
22
24
  }
23
25
 
24
26
  /**
25
- * Construct a cancelled result — the user deliberately exited the flow
26
- * (e.g. back-pressed out of the first screen) without completing or
27
- * failing. Distinct from `failure` so partners can tell a user
28
- * cancellation apart from an error.
27
+ * The user deliberately exited the flow without completing or failing it
28
+ * (e.g. back-pressed out of the first screen) — distinct from a failure
29
+ * so cancellation can be handled separately.
29
30
  */
30
31
  export function cancelled<T>(): UseSmileIDResult<T> {
31
32
  return { status: "cancelled" };
@@ -109,7 +109,7 @@ const consent: ConsentInformation = Object.freeze({
109
109
  // builds this and it travels on every `JobRequest`; the API
110
110
  // serializes `request.metadata` as the `metadata` multipart part.
111
111
  const metadata = new UseSmileIDMetadata({
112
- sdkVersion: "12.0.0",
112
+ sdkVersion: "12.0.1",
113
113
  sdk: "react-native-expo",
114
114
  deviceOs: "",
115
115
  deviceModel: "",
@@ -0,0 +1,83 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import axios, { AxiosHeaders, type InternalAxiosRequestConfig } from "axios";
3
+
4
+ import { createGzipInterceptor } from "../gzip_interceptor";
5
+
6
+ /**
7
+ * Pins `createGzipInterceptor`'s wire shape: it stamps `Accept-Encoding: gzip` on every request
8
+ * (never reading `config.data`), leaves the body unmodified (no compression, no Content-Type/
9
+ * Length), never sets `Content-Encoding`, and overwrites any existing `Accept-Encoding`.
10
+ */
11
+ describe("createGzipInterceptor", () => {
12
+ function attachAndRun(
13
+ config: InternalAxiosRequestConfig,
14
+ ): Promise<InternalAxiosRequestConfig> {
15
+ const client = axios.create();
16
+ createGzipInterceptor().attach(client);
17
+ const handlers = (
18
+ client.interceptors.request as unknown as {
19
+ handlers: {
20
+ fulfilled?: (
21
+ cfg: InternalAxiosRequestConfig,
22
+ ) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;
23
+ }[];
24
+ }
25
+ ).handlers;
26
+ const fulfilled = handlers[handlers.length - 1]?.fulfilled;
27
+ if (!fulfilled) throw new Error("gzip interceptor did not register");
28
+ return Promise.resolve(fulfilled(config));
29
+ }
30
+
31
+ it("stamps Accept-Encoding: gzip on a bodyless request", async () => {
32
+ const result = await attachAndRun({
33
+ headers: new AxiosHeaders(),
34
+ } as InternalAxiosRequestConfig);
35
+
36
+ expect(result.headers.get("Accept-Encoding")).toBe("gzip");
37
+ expect(result.data).toBeUndefined();
38
+ });
39
+
40
+ it("stamps the header without touching a JSON string body", async () => {
41
+ const result = await attachAndRun({
42
+ headers: new AxiosHeaders(),
43
+ data: '{"hello":"world"}',
44
+ } as InternalAxiosRequestConfig);
45
+
46
+ expect(result.headers.get("Accept-Encoding")).toBe("gzip");
47
+ // The body is not compressed or otherwise transformed.
48
+ expect(result.data).toBe('{"hello":"world"}');
49
+ expect(result.headers.get("Content-Type")).toBeUndefined();
50
+ });
51
+
52
+ it("stamps the header without touching a multipart FormData body", async () => {
53
+ // A minimal FormData-like stand-in; the interceptor never inspects
54
+ // `config.data`'s shape, so this is enough to prove pass-through.
55
+ const formData = { getParts: () => [] };
56
+ const result = await attachAndRun({
57
+ headers: new AxiosHeaders(),
58
+ data: formData,
59
+ } as unknown as InternalAxiosRequestConfig);
60
+
61
+ expect(result.headers.get("Accept-Encoding")).toBe("gzip");
62
+ expect(result.data).toBe(formData);
63
+ expect(result.headers.get("Content-Type")).toBeUndefined();
64
+ expect(result.headers.get("Content-Length")).toBeUndefined();
65
+ });
66
+
67
+ it("never stamps Content-Encoding — the request body itself is never compressed", async () => {
68
+ const result = await attachAndRun({
69
+ headers: new AxiosHeaders(),
70
+ data: '{"hello":"world"}',
71
+ } as InternalAxiosRequestConfig);
72
+
73
+ expect(result.headers.get("Content-Encoding")).toBeUndefined();
74
+ });
75
+
76
+ it("overwrites an Accept-Encoding value set by an earlier interceptor", async () => {
77
+ const result = await attachAndRun({
78
+ headers: new AxiosHeaders({ "Accept-Encoding": "identity" }),
79
+ } as InternalAxiosRequestConfig);
80
+
81
+ expect(result.headers.get("Accept-Encoding")).toBe("gzip");
82
+ });
83
+ });
@@ -20,6 +20,7 @@ import {
20
20
  ATTESTATION_OPT_IN_KEY,
21
21
  } from "../smile_integrity_interceptor";
22
22
  import { REQUEST_MAC_HEADER } from "../smile_request_mac_interceptor";
23
+ import { SecurityPolicy } from "../../../security/security_policy";
23
24
 
24
25
  /**
25
26
  * Pins down the integrity interceptor's wire shape:
@@ -51,7 +52,10 @@ describe("SmileIntegrityInterceptor", () => {
51
52
 
52
53
  beforeEach(() => {
53
54
  client = axios.create();
54
- interceptor = new SmileIntegrityInterceptor();
55
+ // undefined bit-code is fail-secure: the attestation check is active, matching the pre-policy default.
56
+ interceptor = new SmileIntegrityInterceptor({
57
+ securityPolicy: SecurityPolicy.fromBitCode(undefined),
58
+ });
55
59
  interceptor.attach(client);
56
60
  warmUpAttestation = vi.fn();
57
61
  requestAttestationToken = vi.fn();
@@ -153,4 +157,36 @@ describe("SmileIntegrityInterceptor", () => {
153
157
  expect(ex.message).toBe("hello world");
154
158
  expect(encodeURIComponent(ex.message)).toBe("hello%20world");
155
159
  });
160
+
161
+ it("passes through without a nonce when policy disables attestation", async () => {
162
+ // bit-code 1: only payload signing (bit 0) set; the attestation bit (3) is off, so the token
163
+ // must not be requested even though the MAC header is present.
164
+ const localClient = axios.create();
165
+ new SmileIntegrityInterceptor({
166
+ securityPolicy: SecurityPolicy.fromBitCode(1),
167
+ }).attach(localClient);
168
+ const config = makeConfig({
169
+ [ATTESTATION_OPT_IN_KEY]: true,
170
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
+ } as any);
172
+ config.headers.set(REQUEST_MAC_HEADER, "mac-abc");
173
+
174
+ const handlers = (
175
+ localClient.interceptors.request as unknown as {
176
+ handlers: {
177
+ fulfilled?: (cfg: InternalAxiosRequestConfig) => unknown;
178
+ }[];
179
+ }
180
+ ).handlers;
181
+ let current = config;
182
+ for (let i = handlers.length - 1; i >= 0; i--) {
183
+ const handler = handlers[i]?.fulfilled;
184
+ if (typeof handler === "function") {
185
+ current = (await handler(current)) as InternalAxiosRequestConfig;
186
+ }
187
+ }
188
+
189
+ expect(current.headers.get(DEVICE_NONCE_HEADER)).toBeUndefined();
190
+ expect(requestAttestationToken).not.toHaveBeenCalled();
191
+ });
156
192
  });
@@ -2,6 +2,7 @@ import { describe, expect, it, beforeEach } from "vitest";
2
2
  import axios, { AxiosHeaders, type InternalAxiosRequestConfig } from "axios";
3
3
 
4
4
  import { SmileIDCryptoManager } from "../../../security/smile_id_crypto_manager";
5
+ import { SecurityPolicy } from "../../../security/security_policy";
5
6
  import {
6
7
  SmileRequestMacInterceptor,
7
8
  REQUEST_MAC_HEADER,
@@ -26,7 +27,11 @@ describe("SmileRequestMacInterceptor", () => {
26
27
 
27
28
  beforeEach(() => {
28
29
  client = axios.create();
29
- interceptor = new SmileRequestMacInterceptor({ cryptoManager });
30
+ // undefined bit-code is fail-secure: payload signing is active, matching the pre-policy default.
31
+ interceptor = new SmileRequestMacInterceptor({
32
+ cryptoManager,
33
+ securityPolicy: SecurityPolicy.fromBitCode(undefined),
34
+ });
30
35
  interceptor.attach(client);
31
36
  });
32
37
 
@@ -95,4 +100,57 @@ describe("SmileRequestMacInterceptor", () => {
95
100
  );
96
101
  expect(result.headers.get(REQUEST_MAC_HEADER)).toBeUndefined();
97
102
  });
103
+
104
+ /** Drive a one-interceptor client built with a specific policy. */
105
+ async function runWith(
106
+ policy: SecurityPolicy,
107
+ config: InternalAxiosRequestConfig,
108
+ ): Promise<InternalAxiosRequestConfig> {
109
+ const localClient = axios.create();
110
+ new SmileRequestMacInterceptor({
111
+ cryptoManager,
112
+ securityPolicy: policy,
113
+ }).attach(localClient);
114
+ const handlers = (
115
+ localClient.interceptors.request as unknown as {
116
+ handlers: {
117
+ fulfilled?: (cfg: InternalAxiosRequestConfig) => unknown;
118
+ }[];
119
+ }
120
+ ).handlers;
121
+ let current = config;
122
+ for (let i = handlers.length - 1; i >= 0; i--) {
123
+ const handler = handlers[i]?.fulfilled;
124
+ if (typeof handler === "function") {
125
+ current = (await handler(current)) as InternalAxiosRequestConfig;
126
+ }
127
+ }
128
+ return current;
129
+ }
130
+
131
+ it("leaves the request unsigned when the policy disables payload signing", async () => {
132
+ // bit-code 0: no bits set, so payload signing is off for this session.
133
+ const result = await runWith(
134
+ SecurityPolicy.fromBitCode(0),
135
+ makeConfig({
136
+ data: '{"hello":"world"}',
137
+ headers: new AxiosHeaders({ [REQUEST_TIMESTAMP_HEADER]: TIMESTAMP }),
138
+ [REQUEST_MAC_OPT_IN_KEY]: true,
139
+ } as Partial<InternalAxiosRequestConfig>),
140
+ );
141
+ expect(result.headers.get(REQUEST_MAC_HEADER)).toBeUndefined();
142
+ });
143
+
144
+ it("signs when the policy enables only payload signing", async () => {
145
+ // bit-code 1: bit 0 (payload signing) set; other behaviours off.
146
+ const result = await runWith(
147
+ SecurityPolicy.fromBitCode(1),
148
+ makeConfig({
149
+ data: '{"hello":"world"}',
150
+ headers: new AxiosHeaders({ [REQUEST_TIMESTAMP_HEADER]: TIMESTAMP }),
151
+ [REQUEST_MAC_OPT_IN_KEY]: true,
152
+ } as Partial<InternalAxiosRequestConfig>),
153
+ );
154
+ expect(result.headers.get(REQUEST_MAC_HEADER)).toBeDefined();
155
+ });
98
156
  });
@@ -1,5 +1,5 @@
1
1
  import type { AxiosInstance, InternalAxiosRequestConfig } from "axios";
2
- import { Platform } from "react-native";
2
+ import { sourceSdkIdentifier } from "@smileid/usesmileid_bridge";
3
3
 
4
4
  import type { PartnerConfig } from "../../../core/models/network_config";
5
5
  import { currentRequestTimestamp } from "../../security/request_timestamp";
@@ -26,22 +26,7 @@ import type { TokenHolder } from "../auth/token_holder";
26
26
  * by the next outgoing request.
27
27
  */
28
28
 
29
- const SDK_VERSION = "12.0.0-beta01";
30
-
31
- /**
32
- * Source-SDK identifier, suffixed with the runtime platform so the server can
33
- * attribute the request and resolve the matching per-SDK signing secret.
34
- */
35
- function sourceSdk(): string {
36
- switch (Platform.OS) {
37
- case "android":
38
- return "react-native-expo (android)";
39
- case "ios":
40
- return "react-native-expo (iOS)";
41
- default:
42
- return "react-native-expo";
43
- }
44
- }
29
+ const SDK_VERSION = "12.0.1";
45
30
 
46
31
  export type SmileAuthInterceptorOptions = {
47
32
  readonly partnerConfig: PartnerConfig;
@@ -73,7 +58,7 @@ export class SmileAuthInterceptor {
73
58
  config: InternalAxiosRequestConfig,
74
59
  ): Promise<InternalAxiosRequestConfig> {
75
60
  config.headers.set("smileid-token", this._tokenHolder.current);
76
- config.headers.set("smileid-source-sdk", sourceSdk());
61
+ config.headers.set("smileid-source-sdk", sourceSdkIdentifier());
77
62
  config.headers.set("smileid-source-sdk-version", SDK_VERSION);
78
63
  config.headers.set("smileid-partner-id", this._partnerConfig.partnerId);
79
64
  if (this._partnerConfig.callbackUrl) {
@@ -2,6 +2,7 @@ import type { AxiosInstance, InternalAxiosRequestConfig } from "axios";
2
2
 
3
3
  import { Attestation, AttestationException } from "@smileid/usesmileid_bridge";
4
4
 
5
+ import type { SecurityPolicy } from "../../security/security_policy";
5
6
  import { REQUEST_MAC_HEADER } from "./smile_request_mac_interceptor";
6
7
 
7
8
  /**
@@ -31,6 +32,11 @@ import { REQUEST_MAC_HEADER } from "./smile_request_mac_interceptor";
31
32
  * Warm-up lives outside the interceptor. Call `Attestation.warmUp()`
32
33
  * once at flow mount so the per-request `requestToken` call doesn't
33
34
  * pay the multi-second warm-up cost on every submission.
35
+ *
36
+ * Attestation is additionally gated by the server-driven {@link SecurityPolicy}:
37
+ * when the session's policy disables the device-attestation check, an opted-in
38
+ * request ships without a device nonce. The policy is fail-secure, so a token
39
+ * that omits the claim attests as before.
34
40
  */
35
41
 
36
42
  export const DEVICE_NONCE_HEADER = "smileid-device-nonce";
@@ -38,7 +44,17 @@ export const DEVICE_NONCE_HEADER = "smileid-device-nonce";
38
44
  /** Axios config key that opts a request into attestation stamping. */
39
45
  export const ATTESTATION_OPT_IN_KEY = "_useSmileIDAttestation";
40
46
 
47
+ export type SmileIntegrityInterceptorOptions = {
48
+ readonly securityPolicy: SecurityPolicy;
49
+ };
50
+
41
51
  export class SmileIntegrityInterceptor {
52
+ private readonly _securityPolicy: SecurityPolicy;
53
+
54
+ constructor(options: SmileIntegrityInterceptorOptions) {
55
+ this._securityPolicy = options.securityPolicy;
56
+ }
57
+
42
58
  attach(client: AxiosInstance): { request: number } {
43
59
  const requestId = client.interceptors.request.use(async (config) => {
44
60
  return this._onRequest(config);
@@ -55,6 +71,10 @@ export class SmileIntegrityInterceptor {
55
71
  ] === true;
56
72
  if (!optedIn) return config;
57
73
 
74
+ // Server-driven policy can switch device attestation off for this session; when it is off,
75
+ // ship the opted-in request without requesting a token.
76
+ if (!this._securityPolicy.isPlayIntegrityCheckActive()) return config;
77
+
58
78
  const macHeader = config.headers.get(REQUEST_MAC_HEADER);
59
79
  if (typeof macHeader !== "string") return config;
60
80
 
@@ -2,6 +2,7 @@ import type { AxiosInstance, InternalAxiosRequestConfig } from "axios";
2
2
 
3
3
  import { SmileIDCryptoError } from "../../security/smile_id_crypto_error";
4
4
  import type { SmileIDCryptoManager } from "../../security/smile_id_crypto_manager";
5
+ import type { SecurityPolicy } from "../../security/security_policy";
5
6
 
6
7
  /**
7
8
  * Stamps the `smileid-request-mac` header on every outbound request whose axios
@@ -18,6 +19,11 @@ import type { SmileIDCryptoManager } from "../../security/smile_id_crypto_manage
18
19
  * rather than signed against an invented timestamp.
19
20
  *
20
21
  * Requests without the opt-in flag pass through unchanged.
22
+ *
23
+ * Signing is additionally gated by the server-driven {@link SecurityPolicy}: when the session's
24
+ * policy disables payload signing, an opted-in request still ships, just unsigned — the same wire
25
+ * shape as an opt-out request. The policy is fail-secure, so a token that omits the claim signs as
26
+ * before.
21
27
  */
22
28
 
23
29
  export const REQUEST_MAC_HEADER = "smileid-request-mac";
@@ -28,13 +34,16 @@ export const REQUEST_MAC_OPT_IN_KEY = "_useSmileIDRequestMac";
28
34
 
29
35
  export type SmileRequestMacInterceptorOptions = {
30
36
  readonly cryptoManager: SmileIDCryptoManager;
37
+ readonly securityPolicy: SecurityPolicy;
31
38
  };
32
39
 
33
40
  export class SmileRequestMacInterceptor {
34
41
  private readonly _cryptoManager: SmileIDCryptoManager;
42
+ private readonly _securityPolicy: SecurityPolicy;
35
43
 
36
44
  constructor(options: SmileRequestMacInterceptorOptions) {
37
45
  this._cryptoManager = options.cryptoManager;
46
+ this._securityPolicy = options.securityPolicy;
38
47
  }
39
48
 
40
49
  attach(client: AxiosInstance): { request: number } {
@@ -53,6 +62,10 @@ export class SmileRequestMacInterceptor {
53
62
  ] === true;
54
63
  if (!optedIn) return config;
55
64
 
65
+ // Server-driven policy can switch payload signing off for this session; when it is off, ship
66
+ // the opted-in request unsigned and let the server apply its policy.
67
+ if (!this._securityPolicy.isPayloadSigningActive()) return config;
68
+
56
69
  const timestamp = config.headers.get(REQUEST_TIMESTAMP_HEADER);
57
70
  if (typeof timestamp !== "string") return config;
58
71
 
@@ -0,0 +1,200 @@
1
+ import { ImageType, type ImageTypeId } from "@smileid/usesmileid_bridge";
2
+ import { describe, expect, it } from "vitest";
3
+
4
+ import {
5
+ type CompressibleAsset,
6
+ CompressibleAssets,
7
+ } from "../compressible_asset";
8
+ import {
9
+ base64Len,
10
+ PayloadCompressionConfig as C,
11
+ } from "../payload_compression_config";
12
+ import { PayloadCompressor } from "../payload_compressor";
13
+
14
+ const mib = C.bytesPerMB;
15
+
16
+ /** Encoded size scales linearly with quality: `size(q) = base * q`. */
17
+ class FakeAsset implements CompressibleAsset {
18
+ recompressCount = 0;
19
+ private q: number;
20
+ constructor(
21
+ readonly imageType: ImageTypeId,
22
+ private readonly base: number,
23
+ startQuality: number,
24
+ ) {
25
+ this.q = startQuality;
26
+ }
27
+ encodedSize(): number {
28
+ return Math.trunc(this.base * this.q);
29
+ }
30
+ quality(): number {
31
+ return this.q;
32
+ }
33
+ async recompress(quality: number): Promise<void> {
34
+ this.q = quality;
35
+ this.recompressCount += 1;
36
+ }
37
+ }
38
+
39
+ describe("PayloadCompressor", () => {
40
+ it("base64Len matches ceil(n / 3) * 4", () => {
41
+ expect(base64Len(0)).toBe(0);
42
+ expect(base64Len(1)).toBe(4);
43
+ expect(base64Len(3)).toBe(4);
44
+ expect(base64Len(4)).toBe(8);
45
+ expect(base64Len(6)).toBe(8);
46
+ expect(base64Len(3_000_000)).toBe(Math.ceil(3_000_000 / 3) * 4);
47
+ });
48
+
49
+ it("raw and projected sizes sum images plus metadata", () => {
50
+ const assets = new CompressibleAssets({
51
+ selfie: new FakeAsset(ImageType.Selfie, 300_000, 1.0),
52
+ liveness: [new FakeAsset(ImageType.Liveness, 100_000, 1.0)],
53
+ });
54
+ const metadata = 50_000;
55
+ expect(PayloadCompressor.rawBundleSize(assets, metadata)).toBe(
56
+ 300_000 + 100_000 + 50_000,
57
+ );
58
+ expect(PayloadCompressor.projectedEncodedSize(assets, metadata)).toBe(
59
+ base64Len(300_000) +
60
+ base64Len(100_000) +
61
+ metadata +
62
+ C.structuralOverheadBytes,
63
+ );
64
+ });
65
+
66
+ it("plan order Rule B - selfie over 1MB compresses faces first then back then front", () => {
67
+ const assets = new CompressibleAssets({
68
+ selfie: new FakeAsset(ImageType.Selfie, 2 * mib, 0.95),
69
+ liveness: [new FakeAsset(ImageType.Liveness, mib, 0.8)],
70
+ documentFront: new FakeAsset(ImageType.DocumentFront, mib, 0.85),
71
+ documentBack: new FakeAsset(ImageType.DocumentBack, mib, 0.85),
72
+ });
73
+ const plan = PayloadCompressor.buildCompressionPlan(assets);
74
+ expect(plan.map(([a]) => a.imageType)).toEqual([
75
+ ImageType.Selfie,
76
+ ImageType.Liveness,
77
+ ImageType.DocumentBack,
78
+ ImageType.DocumentFront,
79
+ ]);
80
+ expect(plan[0][1]).toBe(C.faceMinQ);
81
+ expect(plan[3][1]).toBe(C.docMinQ);
82
+ });
83
+
84
+ it("plan order Rule A - documents over 4MB compresses back, front, then faces", () => {
85
+ const assets = new CompressibleAssets({
86
+ selfie: new FakeAsset(ImageType.Selfie, 500_000, 0.95),
87
+ documentFront: new FakeAsset(ImageType.DocumentFront, 3 * mib, 0.9),
88
+ documentBack: new FakeAsset(ImageType.DocumentBack, 3 * mib, 0.9),
89
+ });
90
+ const plan = PayloadCompressor.buildCompressionPlan(assets);
91
+ expect(plan.map(([a]) => a.imageType)).toEqual([
92
+ ImageType.DocumentBack,
93
+ ImageType.DocumentFront,
94
+ ImageType.Selfie,
95
+ ]);
96
+ });
97
+
98
+ it("plan order generic - small selfie and docs uses faces then docs", () => {
99
+ const assets = new CompressibleAssets({
100
+ selfie: new FakeAsset(ImageType.Selfie, 400_000, 0.95),
101
+ documentFront: new FakeAsset(ImageType.DocumentFront, 400_000, 0.85),
102
+ });
103
+ const plan = PayloadCompressor.buildCompressionPlan(assets);
104
+ expect(plan.map(([a]) => a.imageType)).toEqual([
105
+ ImageType.Selfie,
106
+ ImageType.DocumentFront,
107
+ ]);
108
+ });
109
+
110
+ it("already small bundle is left untouched", async () => {
111
+ const selfie = new FakeAsset(ImageType.Selfie, mib, 0.95);
112
+ await PayloadCompressor.prepareBundleForTransmission(
113
+ new CompressibleAssets({ selfie }),
114
+ 50_000,
115
+ );
116
+ expect(selfie.recompressCount).toBe(0);
117
+ });
118
+
119
+ it("over-budget bundle compresses under target and never breaches floors", async () => {
120
+ const selfie = new FakeAsset(
121
+ ImageType.Selfie,
122
+ Math.trunc((22 * mib) / 10),
123
+ 0.95,
124
+ );
125
+ const liveness = [
126
+ new FakeAsset(ImageType.Liveness, Math.trunc((12 * mib) / 10), 0.8),
127
+ new FakeAsset(ImageType.Liveness, Math.trunc((12 * mib) / 10), 0.8),
128
+ ];
129
+ const assets = new CompressibleAssets({ selfie, liveness });
130
+ const metadata = Math.trunc((2 * mib) / 10);
131
+ await PayloadCompressor.prepareBundleForTransmission(assets, metadata);
132
+
133
+ expect(
134
+ PayloadCompressor.rawBundleSize(assets, metadata),
135
+ ).toBeLessThanOrEqual(C.rawFallback);
136
+ expect(selfie.quality()).toBeGreaterThanOrEqual(C.faceMinQ);
137
+ for (const frame of liveness)
138
+ expect(frame.quality()).toBeGreaterThanOrEqual(C.faceMinQ);
139
+ });
140
+
141
+ it("document-heavy bundle compresses back first and never breaches the doc floor", async () => {
142
+ const front = new FakeAsset(
143
+ ImageType.DocumentFront,
144
+ Math.trunc((23 * mib) / 10),
145
+ 0.9,
146
+ );
147
+ const back = new FakeAsset(
148
+ ImageType.DocumentBack,
149
+ Math.trunc((23 * mib) / 10),
150
+ 0.9,
151
+ );
152
+ const selfie = new FakeAsset(ImageType.Selfie, 400_000, 0.95);
153
+ const assets = new CompressibleAssets({
154
+ selfie,
155
+ documentFront: front,
156
+ documentBack: back,
157
+ });
158
+ await PayloadCompressor.prepareBundleForTransmission(assets, 100_000);
159
+ expect(back.quality()).toBeLessThanOrEqual(front.quality());
160
+ expect(front.quality()).toBeGreaterThanOrEqual(C.docMinQ);
161
+ expect(back.quality()).toBeGreaterThanOrEqual(C.docMinQ);
162
+ expect(
163
+ PayloadCompressor.rawBundleSize(assets, 100_000),
164
+ ).toBeLessThanOrEqual(C.rawTarget);
165
+ });
166
+
167
+ it("hard guarantee drops past the per-type floor to fit the relaxed cap", async () => {
168
+ // floor .45 -> 12MB*.45 = 5.4MB raw, still over the relaxed 4.5MB ceiling. The guarantee pass
169
+ // must step quality below faceMinQ until the bundle fits both caps.
170
+ const selfie = new FakeAsset(ImageType.Selfie, 12 * mib, 0.95);
171
+ const assets = new CompressibleAssets({ selfie });
172
+ await PayloadCompressor.prepareBundleForTransmission(assets, 100_000);
173
+ expect(
174
+ PayloadCompressor.rawBundleSize(assets, 100_000),
175
+ ).toBeLessThanOrEqual(C.rawFallback);
176
+ expect(
177
+ PayloadCompressor.projectedEncodedSize(assets, 100_000),
178
+ ).toBeLessThanOrEqual(C.hardEncodedCap);
179
+ expect(selfie.quality()).toBeLessThan(C.faceMinQ);
180
+ expect(selfie.quality()).toBeGreaterThanOrEqual(C.absoluteMinQ);
181
+ });
182
+
183
+ it("extreme bundle unreachable by quality drives to the absolute floor without throwing", async () => {
184
+ // So large that even absoluteMinQ leaves it over the cap (real JPEGs shrink far more than this
185
+ // linear fake). The guarantee pass drives to the absolute floor and ships — never loops forever.
186
+ const selfie = new FakeAsset(ImageType.Selfie, 80 * mib, 0.95);
187
+ const assets = new CompressibleAssets({ selfie });
188
+ await PayloadCompressor.prepareBundleForTransmission(assets, 100_000);
189
+ expect(selfie.quality()).toBe(C.absoluteMinQ);
190
+ });
191
+
192
+ it("bundle in fallback band with light metadata is accepted", async () => {
193
+ const selfie = new FakeAsset(ImageType.Selfie, 9 * mib, 0.95);
194
+ const assets = new CompressibleAssets({ selfie });
195
+ await PayloadCompressor.prepareBundleForTransmission(assets, 100_000);
196
+ const raw = PayloadCompressor.rawBundleSize(assets, 100_000);
197
+ expect(raw).toBeGreaterThan(C.rawTarget);
198
+ expect(raw).toBeLessThanOrEqual(C.rawFallback);
199
+ });
200
+ });