@smileid/usesmileid 12.1.0 → 12.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/README.md +28 -17
  3. package/dist/camera/document_image_pipeline.d.ts +1 -1
  4. package/dist/camera/document_image_pipeline.js +1 -1
  5. package/dist/camera/use_smile_id_frame_stream.js +1 -1
  6. package/dist/camera/use_smile_id_kamera_camera_system.d.ts +6 -0
  7. package/dist/camera/use_smile_id_kamera_camera_system.js +1 -1
  8. package/dist/core/exception/invalid_job_type_screen_config_exception.js +1 -1
  9. package/dist/core/exception/missing_partner_config_exception.d.ts +2 -0
  10. package/dist/core/exception/missing_partner_config_exception.js +1 -1
  11. package/dist/core/exception/unused_job_params_exception.js +1 -1
  12. package/dist/core/exception/use_smile_id_error_code.d.ts +1 -0
  13. package/dist/core/exception/use_smile_id_error_code.js +1 -1
  14. package/dist/core/exception/use_smile_id_network_exception.d.ts +2 -1
  15. package/dist/core/exception/use_smile_id_network_exception.js +1 -1
  16. package/dist/core/models/document_type.d.ts +1 -1
  17. package/dist/core/models/job_type.d.ts +5 -0
  18. package/dist/core/models/job_type.js +1 -1
  19. package/dist/core/models/network_config.d.ts +2 -3
  20. package/dist/core/models/params.d.ts +18 -0
  21. package/dist/core/models/params.js +1 -1
  22. package/dist/data/api/interceptors/smile_auth_interceptor.js +1 -1
  23. package/dist/data/api/smile_id_api.d.ts +6 -12
  24. package/dist/data/api/smile_id_api.js +1 -1
  25. package/dist/data/compression/compressible_asset.d.ts +9 -1
  26. package/dist/data/compression/compressible_asset.js +1 -1
  27. package/dist/data/compression/payload_compression_runner.d.ts +3 -0
  28. package/dist/data/compression/payload_compression_runner.js +1 -1
  29. package/dist/data/compression/payload_compressor.js +1 -1
  30. package/dist/data/factory/networking_factory.d.ts +2 -0
  31. package/dist/data/factory/networking_factory.js +1 -1
  32. package/dist/data/model/job_request.d.ts +20 -1
  33. package/dist/data/model/token_payload.d.ts +17 -1
  34. package/dist/data/model/token_payload.js +1 -1
  35. package/dist/data/repository/use_smile_id_repository_impl.js +1 -1
  36. package/dist/debug/capture_debug_geometry.d.ts +29 -0
  37. package/dist/debug/capture_debug_geometry.js +1 -0
  38. package/dist/debug/document_preview_debug_geometry.d.ts +36 -0
  39. package/dist/debug/document_preview_debug_geometry.js +1 -0
  40. package/dist/debug/preview_debug_drawing.d.ts +51 -0
  41. package/dist/debug/preview_debug_drawing.js +1 -0
  42. package/dist/debug/selfie_preview_debug_geometry.d.ts +55 -0
  43. package/dist/debug/selfie_preview_debug_geometry.js +1 -0
  44. package/dist/debug/use_smile_id_debug.d.ts +61 -0
  45. package/dist/debug/use_smile_id_debug.js +1 -0
  46. package/dist/debug/use_smile_id_debug_tap.d.ts +25 -0
  47. package/dist/debug/use_smile_id_debug_tap.js +1 -0
  48. package/dist/debug/use_smile_id_document_capture_debug_overlay.d.ts +56 -0
  49. package/dist/debug/use_smile_id_document_capture_debug_overlay.js +1 -0
  50. package/dist/debug/use_smile_id_document_preview_debug_overlay.d.ts +32 -0
  51. package/dist/debug/use_smile_id_document_preview_debug_overlay.js +1 -0
  52. package/dist/debug/use_smile_id_selfie_capture_debug_overlay.d.ts +27 -0
  53. package/dist/debug/use_smile_id_selfie_capture_debug_overlay.js +1 -0
  54. package/dist/debug/use_smile_id_selfie_preview_debug_overlay.d.ts +60 -0
  55. package/dist/debug/use_smile_id_selfie_preview_debug_overlay.js +1 -0
  56. package/dist/index.d.ts +8 -1
  57. package/dist/index.js +1 -1
  58. package/dist/l10n/strings_en.d.ts +2 -1
  59. package/dist/l10n/strings_en.js +2 -2
  60. package/dist/l10n/use_smile_id_localizations.d.ts +1 -0
  61. package/dist/l10n/use_smile_id_localizations.js +1 -1
  62. package/dist/metadata/document_image_origin.d.ts +1 -0
  63. package/dist/metadata/document_image_origin.js +1 -1
  64. package/dist/metadata/metadata_configuration_mappers.js +1 -1
  65. package/dist/metadata/use_smile_id_metadata_factory.d.ts +4 -4
  66. package/dist/metadata/use_smile_id_metadata_factory.js +1 -1
  67. package/dist/presentation/assets/si_document_visa_hero_animation.json +1 -0
  68. package/dist/presentation/components/helpers/document_interior_crop.d.ts +54 -0
  69. package/dist/presentation/components/helpers/document_interior_crop.js +1 -0
  70. package/dist/presentation/components/helpers/selfie_interior_crop.d.ts +60 -0
  71. package/dist/presentation/components/helpers/selfie_interior_crop.js +1 -0
  72. package/dist/presentation/components/use_smile_id_capture_content.d.ts +7 -1
  73. package/dist/presentation/components/use_smile_id_capture_content.js +1 -1
  74. package/dist/presentation/components/use_smile_id_captured_document_image.d.ts +16 -0
  75. package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -1
  76. package/dist/presentation/components/use_smile_id_captured_image_oval.d.ts +12 -4
  77. package/dist/presentation/components/use_smile_id_captured_image_oval.js +1 -1
  78. package/dist/presentation/components/use_smile_id_document_overlay.d.ts +12 -1
  79. package/dist/presentation/components/use_smile_id_document_overlay.js +1 -1
  80. package/dist/presentation/components/use_smile_id_document_shimmer.d.ts +10 -5
  81. package/dist/presentation/components/use_smile_id_document_shimmer.js +1 -1
  82. package/dist/presentation/flow/analytics/use_smile_id_analytics_event.d.ts +4 -1
  83. package/dist/presentation/flow/analytics/use_smile_id_analytics_event.js +1 -1
  84. package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.d.ts +89 -0
  85. package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.js +1 -0
  86. package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +4 -2
  87. package/dist/presentation/flow/dsl/builder/screens_builder.js +1 -1
  88. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +17 -13
  89. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +1 -1
  90. package/dist/presentation/flow/flow_navigation_manager.d.ts +52 -6
  91. package/dist/presentation/flow/flow_navigation_manager.js +1 -1
  92. package/dist/presentation/flow/model/instructions_defaults.d.ts +1 -0
  93. package/dist/presentation/flow/model/instructions_defaults.js +1 -1
  94. package/dist/presentation/flow/screen/helpers/always_enabled_slot.d.ts +8 -0
  95. package/dist/presentation/flow/screen/helpers/always_enabled_slot.js +1 -0
  96. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +1 -1
  97. package/dist/presentation/flow/screen/orchestrated_consent_screen.js +1 -1
  98. package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
  99. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +1 -1
  100. package/dist/presentation/flow/screen/orchestrated_processing_screen.js +1 -1
  101. package/dist/presentation/flow/validation/flow_validator.d.ts +13 -12
  102. package/dist/presentation/flow/validation/flow_validator.js +1 -1
  103. package/dist/presentation/flow/validation/job_type_validator.d.ts +9 -1
  104. package/dist/presentation/flow/validation/job_type_validator.js +1 -1
  105. package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +6 -1
  106. package/dist/presentation/screen/use_smile_id_capture_screen.js +1 -1
  107. package/dist/presentation/screen/use_smile_id_document_back_instructions.js +1 -1
  108. package/dist/presentation/screen/use_smile_id_document_preview_screen.d.ts +5 -0
  109. package/dist/presentation/screen/use_smile_id_document_preview_screen.js +1 -1
  110. package/dist/presentation/screen/use_smile_id_preview_screen.js +1 -1
  111. package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +5 -0
  112. package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
  113. package/package.json +15 -15
@@ -5,6 +5,7 @@ import type { UseSmileIDRepository } from "../../data/repository/use_smile_id_re
5
5
  import type { ConsentInformation } from "../../core/models/params";
6
6
  import { type DocumentType } from "../../core/models/document_type";
7
7
  import type { JobSubmissionResponse } from "../../data/model/job_submission_response";
8
+ import { type UseSmileIDAnalyticsEventKind } from "./analytics/use_smile_id_analytics_event_kind";
8
9
  import { type MetadataProvider } from "../../metadata/use_smile_id_metadata_factory";
9
10
  import { type DocumentImageOrigin } from "../../metadata/document_image_origin";
10
11
  import type { CaptureTelemetry } from "../screen/use_smile_id_capture_screen";
@@ -25,6 +26,7 @@ export type CapturedArtifacts = {
25
26
  readonly livenessPaths: readonly string[];
26
27
  readonly frontDocumentPath?: string;
27
28
  readonly backDocumentPath?: string;
29
+ readonly visaDocumentPath?: string;
28
30
  readonly consentInformation?: ConsentInformation;
29
31
  };
30
32
  export type FlowExitReason = {
@@ -71,6 +73,11 @@ export declare class FlowNavigationManager {
71
73
  * retaken.
72
74
  */
73
75
  private _documentBackSkipped;
76
+ /**
77
+ * The hidden border each captured document side was saved with, as a fraction of its short side;
78
+ * a side with none, such as a manual-shutter still or a gallery import, has no entry.
79
+ */
80
+ private readonly _documentBorderFractions;
74
81
  /**
75
82
  * How each captured document side was produced (auto / manual / gallery).
76
83
  * Emitted as `DocumentFront/BackImageOrigin` submission metadata; an entry is
@@ -164,22 +171,45 @@ export declare class FlowNavigationManager {
164
171
  exit(reason?: FlowExitReason): void;
165
172
  get captured(): CapturedArtifacts;
166
173
  setCapturedSelfie(selfiePath: string, livenessPaths: readonly string[]): void;
167
- setCapturedFrontDocument(path: string): void;
168
- setCapturedBackDocument(path: string): void;
174
+ setCapturedFrontDocument(path: string, borderFraction?: number): void;
175
+ setCapturedBackDocument(path: string, borderFraction?: number): void;
176
+ /**
177
+ * The second document image this flow captures after the front, or undefined
178
+ * when the flow is front-only. A residency job always captures a visa; other
179
+ * document jobs capture a back when the document type has one and the partner
180
+ * requested both sides.
181
+ */
182
+ supportingDocumentSide(): DocumentSide | undefined;
169
183
  /** Whether the configured document capture asks for a back side. */
170
184
  documentCapturesBack(): boolean;
185
+ /** Captured path for {@link supportingDocumentSide}, or undefined when absent or not yet captured. */
186
+ private _capturedSupportingDocument;
187
+ /** The document image the capture screen should handle now: front until it exists, then the supporting image. */
188
+ currentDocumentCaptureSide(): DocumentSide;
189
+ setCapturedVisaDocument(path: string, borderFraction?: number): void;
171
190
  /** Whether a `preview {}` immediately follows the document capture step. */
172
191
  documentPreviewFollowsCapture(): boolean;
173
192
  /** Whether the active preview previews a document side (vs the selfie). */
174
193
  currentPreviewIsDocument(): boolean;
175
- /** Path of the document side under review: the back if captured, else the front. */
194
+ /** Path of the document image under review: the supporting image if captured, else the front. */
176
195
  currentDocumentPreviewPath(): string | undefined;
196
+ /**
197
+ * The hidden border `side`'s captured document was saved with, as a fraction of its short side;
198
+ * 0 for a side saved without one or not captured. The screens that show it hide exactly this much.
199
+ */
200
+ documentBorderFraction(side: DocumentSide): number;
201
+ /** The hidden border of the document under review, {@link currentDocumentPreviewPath}. */
202
+ currentDocumentPreviewBorderFraction(): number;
203
+ private _setDocumentBorderFraction;
177
204
  /**
178
205
  * Discard the side under review for re-capture: the back if captured,
179
206
  * else the front (which also un-skips the back). Does not navigate.
180
207
  */
181
208
  retakeCurrentDocumentSide(): void;
182
- /** Skip the document back side from the flip interstitial ("Skip"). */
209
+ /**
210
+ * Skip the document back side from the flip interstitial ("Skip"). A no-op for
211
+ * a residency flow, where the visa is part of the submission.
212
+ */
183
213
  skipDocumentBack(): void;
184
214
  /**
185
215
  * Record how a side was produced (→ `DocumentFront/BackImageOrigin`
@@ -191,7 +221,7 @@ export declare class FlowNavigationManager {
191
221
  /** Records per-side document capture telemetry at each side's completion. */
192
222
  recordDocumentCaptureTelemetry(side: DocumentSide, telemetry: CaptureTelemetry): void;
193
223
  private _captureCameraName;
194
- /** Per-side document origin wire entries (front then back; omitted when unset). */
224
+ /** Per-image document origin wire entries (front, back, visa; omitted when unset). */
195
225
  private _documentImageOriginEntries;
196
226
  /**
197
227
  * Capture telemetry + image-property wire entries. Durations and retries keep
@@ -227,6 +257,17 @@ export declare class FlowNavigationManager {
227
257
  */
228
258
  private _assembleJob;
229
259
  private _requireSelfie;
260
+ /**
261
+ * The ID field to submit, blanked — so the wire omits its part — when
262
+ * the token binds it. The server injects the token-bound value and
263
+ * rejects a request whose body contradicts it, so a bound field is
264
+ * never sent, whatever the builder was given. An unbound blank field
265
+ * throws here, as a missing `userDetails` does, rather than failing on
266
+ * the server — so a blank reaching the API is always a token-bound one.
267
+ */
268
+ private _unlessTokenBinds;
269
+ /** The KYC params' three ID fields, each one the token binds blanked. */
270
+ private _kycIdFieldsToSubmit;
230
271
  /**
231
272
  * Resolve `b.userDetails` for the wire. May legitimately return
232
273
  * `undefined` when the token binds every required field
@@ -244,13 +285,18 @@ export declare class FlowNavigationManager {
244
285
  private _requireFrontDocument;
245
286
  /** Capture-artifact fields shared by both document request variants. */
246
287
  private _documentRequestFields;
288
+ /** The captured visa page, or undefined when the residency flow has not reached it. */
289
+ private _capturedVisaDocument;
247
290
  /** Emit terminal `flow_completed` (completed/failed only; not cancelled), then exit via `onExit`. */
248
291
  /** Captured images are removed at every terminal unless offline mode keeps them for a later submission. */
249
292
  private _deleteCapturedFiles;
250
293
  private _finishFlow;
294
+ /** Emits a capture-layer event, such as a selfie session restart, with the flow's `session_id` + `timestamp` enrichment. */
295
+ logAnalyticsEvent(event: UseSmileIDAnalyticsEventKind): void;
296
+ private _logKind;
251
297
  /** Forward a lifecycle event to `onAnalyticsEvent`, adding `session_id` + `timestamp`. No-op without a callback. */
252
298
  private _logEvent;
253
- private _jobTypeName;
299
+ private _jobType;
254
300
  private _screenAnalyticsName;
255
301
  /** Subscribe to state changes; returns an unsubscribe (safe after `dispose()`). */
256
302
  subscribe(listener: Listener): () => void;
@@ -1 +1 @@
1
- "use strict";import{File as b}from"expo-file-system";import{JobType as n}from"../../core/models/job_type";import{CaptureType as _}from"../../core/models/ml_config";import{defaultFrameAspectRatio as y}from"../../core/models/document_type";import{MissingJobTypeException as P,MissingRequiredFieldException as h}from"../../core/exception/job_validation_exceptions";import{InvalidFieldValueException as I}from"../../core/exception/invalid_field_value_exception";import{UseSmileIDAnalyticsEventTypes as m,UseSmileIDAnalyticsParamKeys as a,UseSmileIDAnalyticsScreenNames as g}from"./analytics/use_smile_id_analytics_event";import{UseSmileIDMetadataFactory as C}from"../../metadata/use_smile_id_metadata_factory";import{DOCUMENT_BACK_IMAGE_ORIGIN_KEY as D,DOCUMENT_FRONT_IMAGE_ORIGIN_KEY as T}from"../../metadata/document_image_origin";import{configurationMetadataEntries as w}from"../../metadata/metadata_configuration_mappers";import{CAPTURE_IMAGE_COLOR_DEPTH as v,CAPTURE_IMAGE_PIXEL_CONFIG as S}from"../../metadata/captured_image_properties";import{numSamplesOverride as E}from"../../data/model/sdk_config";import{decodeSmileIDToken as N}from"../../data/util/use_smile_id_token_decoder";import{isTokenConsentComplete as k,bindsRequiredUserDetails as x}from"../../data/model/token_payload";import{isUserDetailsPayloadEmpty as j,userDetailsPayloadFrom as F}from"../../data/model/user_details_payload";import{DocumentSide as p,EnhancedSmartSelfieConfig as A}from"@smileid/usesmileid_bridge";import{ScanPerformanceProfile as O}from"@smileid/usesmileid_bridge";const q=Object.freeze({granted:!1,grantedAt:"",noticeLanguage:"",noticePrivacyPolicyUrl:""});export class FlowNavigationManager{_navigationPath=[];_captured={livenessPaths:[]};_documentBackSkipped=!1;_documentImageOrigins=new Map;_selfieCaptureTelemetry;_documentCaptureTelemetry=new Map;_cameraName;_devicePositions=[];_deviceMovementDetected=null;_jobSubmissionState={kind:"notAttempted"};_listeners=new Set;_steps;_configuration;_repository;_metadataProvider;_onExit;_disposed=!1;_submissionResponse;resolvedSdkConfig;_resolvedPartnerId;_resolvedTokenPayload;get partnerId(){const e=this._resolvedPartnerId;return e&&e.length>0?e:this._configuration.network?.config?.partnerConfig?.partnerId??""}_sessionId;_submissionAttemptCount=0;constructor(e){this._configuration=e.configuration,this._onExit=e.onExit,this._repository=e.repository,this._metadataProvider=e.metadataProvider??new C,this._sessionId=this._metadataProvider.sessionId;const t=FlowNavigationManager._decodeToken(e.configuration);this.resolvedSdkConfig=t?.sdkConfig,this._resolvedPartnerId=t?.partnerId,this._resolvedTokenPayload=t?.tokenPayload;const i=e.configuration.screens.steps;this._steps=k(t?.tokenPayload?.consent)?i.filter(c=>c.kind!=="consent"):i;const s=this._steps[0];s&&(this._navigationPath=[s]),this._logEvent(m.flowStarted,{[a.jobType]:this._jobTypeName()}),s&&this._logEvent(m.screenViewed,{[a.screenName]:this._screenAnalyticsName(s)})}get configuration(){return this._configuration}get enhancedLivenessEnabled(){return this._steps.some(e=>e.kind==="capture"&&e.config.selfieConfig?.enableEnhancedLiveness===!0)}get documentType(){for(const e of this._steps)if(e.kind==="capture"&&e.config.captureType===_.document)return e.config.documentConfig?.documentType}get documentFrameAspectRatio(){for(const e of this._steps)if(e.kind==="capture"&&e.config.captureType===_.document){const t=e.config.documentConfig;return t?.resolvedAspectRatio??y(t?.documentType?.orientation??"Landscape")}}get navigationPath(){return this._navigationPath}get currentScreen(){return this._navigationPath[this._navigationPath.length-1]}get currentDepth(){return this._navigationPath.length-1}get isFirstScreen(){return this._navigationPath.length<=1}get isLastScreen(){return this.getNextScreen()===void 0}getNextScreen(){const e=this.currentScreen;if(!e)return;const t=this._steps.indexOf(e);if(!(t<0)){if(e.kind==="preview"&&this._precedingStepIsDocumentCapture(t)&&this.documentCapturesBack()&&this._captured.backDocumentPath===void 0&&!this._documentBackSkipped){const i=this._steps[t-1];if(i!==void 0)return i}return e.kind==="capture"&&e.config.captureType===_.document&&this._documentBackSkipped&&this._steps[t+1]?.kind==="preview"?this._steps[t+2]:e.kind==="capture"&&e.config.captureType===_.document&&this.documentCapturesBack()&&this._captured.frontDocumentPath!==void 0&&this._captured.backDocumentPath===void 0&&!this._documentBackSkipped&&!this.documentPreviewFollowsCapture()?e:this._steps[t+1]}}navigateToNext(){if(this._disposed)return;const e=this.getNextScreen();if(!e){const t=this._jobSubmissionState;if(this._submissionResponse){this._finishFlow({kind:"completed",job:this._submissionResponse});return}if(t.kind==="failure"){this._finishFlow({kind:"failed",error:t.error});return}const i=t.kind==="success"?t.response:{userId:"",jobId:"",status:"",message:""};this._finishFlow({kind:"completed",job:i});return}this._navigationPath=[...this._navigationPath,e],this._logEvent(m.screenViewed,{[a.screenName]:this._screenAnalyticsName(e)}),e.kind==="processing"&&this._jobSubmissionState.kind==="notAttempted"&&this._repository!==void 0&&(this._jobSubmissionState={kind:"loading"}),this._notify(),e.kind==="processing"&&this._jobSubmissionState.kind==="loading"&&this._repository!==void 0&&setTimeout(()=>{this._disposed||this._submit()},0)}navigateBack(){if(!this._disposed){if(this.isFirstScreen){this._onExit({kind:"cancelled"});return}this._logEvent(m.retakeRequested),this._navigationPath=this._navigationPath.slice(0,-1),this._notify()}}navigateToStart(){if(this._disposed)return;const e=this._steps[0];this._navigationPath=e?[e]:[],this._notify()}exit(e={kind:"cancelled"}){this._disposed||this._finishFlow(e)}get captured(){return this._captured}setCapturedSelfie(e,t){this._disposed||(this._captured={...this._captured,selfiePath:e,livenessPaths:Object.freeze([...t])},this._logEvent(m.selfieCaptured,{[a.livenessImageCount]:String(t.length)}),this._notify())}setCapturedFrontDocument(e){this._disposed||(this._captured={...this._captured,frontDocumentPath:e},this._logEvent(m.documentCaptured,{[a.documentSide]:"front"}),this._notify())}setCapturedBackDocument(e){this._disposed||(this._captured={...this._captured,backDocumentPath:e},this._logEvent(m.documentCaptured,{[a.documentSide]:"both"}),this._notify())}documentCapturesBack(){for(const e of this._steps)if(e.kind==="capture"&&e.config.captureType===_.document){const t=e.config.documentConfig;return t?.documentType?.hasBackSide===!0&&(t?.captureBothSides??!1)}return!1}documentPreviewFollowsCapture(){const e=this._steps.findIndex(t=>t.kind==="capture"&&t.config.captureType===_.document);return e<0?!1:this._steps[e+1]?.kind==="preview"}currentPreviewIsDocument(){const e=this.currentScreen;return!e||e.kind!=="preview"?!1:this._precedingStepIsDocumentCapture(this._steps.indexOf(e))}currentDocumentPreviewPath(){return this._captured.backDocumentPath??this._captured.frontDocumentPath}retakeCurrentDocumentSide(){if(this._disposed)return;const e={...this._captured};e.backDocumentPath!==void 0?(delete e.backDocumentPath,this._documentImageOrigins.delete(p.Back)):(delete e.frontDocumentPath,this._documentImageOrigins.delete(p.Front),this._documentBackSkipped=!1),this._captured=e,this._notify()}skipDocumentBack(){this._disposed||(this._documentBackSkipped=!0,this._notify())}recordDocumentImageOrigin(e,t){this._disposed||this._documentImageOrigins.set(e,t)}recordSelfieCaptureTelemetry(e){this._disposed||(this._selfieCaptureTelemetry=e,this._captureCameraName(e))}recordDocumentCaptureTelemetry(e,t){this._disposed||(this._documentCaptureTelemetry.set(e,t),this._captureCameraName(t))}_captureCameraName(e){e.cameraName&&(this._cameraName=e.cameraName),e.devicePositions.length>0&&(this._devicePositions=e.devicePositions,this._deviceMovementDetected=e.deviceMovementDetected)}_documentImageOriginEntries(){const e=[],t=this._documentImageOrigins.get(p.Front);t!==void 0&&e.push({name:T,value:t});const i=this._documentImageOrigins.get(p.Back);return i!==void 0&&e.push({name:D,value:i}),e}_captureTelemetryEntries(){const e=[],t=(c,u)=>e.push({name:c,value:u}),i=this._selfieCaptureTelemetry;i&&(t("selfie_capture_duration_ms",String(i.durationMs)),t("selfie_capture_retries",String(i.retries))),this._captured.selfiePath!==void 0&&(t("selfie_image_color_depth",String(v)),t("selfie_image_pixel_config",S)),this._cameraName&&t("camera_name",this._cameraName),this._devicePositions.length>0&&(t("device_positions",JSON.stringify(this._devicePositions)),this._deviceMovementDetected!==null&&t("device_movement_detected",String(this._deviceMovementDetected)));const s=c=>c===p.Back?"back":"front";for(const c of[p.Front,p.Back]){const u=s(c),f=this._documentCaptureTelemetry.get(c);f&&(t(`document_${u}_capture_duration_ms`,String(f.durationMs)),t(`document_${u}_capture_retries`,String(f.retries)),t(`document_${u}_image_color_depth`,String(v)),t(`document_${u}_image_pixel_config`,S))}return e}_precedingStepIsDocumentCapture(e){const t=this._steps[e-1];return t?.kind==="capture"&&t.config.captureType===_.document}setConsent(e){this._disposed||(this._captured={...this._captured,consentInformation:e},this._logEvent(m.consentCaptured,{[a.decision]:"granted"}),this._notify())}get jobSubmissionState(){return this._jobSubmissionState}beginSubmissionIfNeeded(){this._disposed||this._jobSubmissionState.kind==="notAttempted"&&this._submit()}retrySubmission(){this._disposed||this._jobSubmissionState.kind!=="loading"&&(this._setSubmissionState({kind:"notAttempted"}),this.beginSubmissionIfNeeded())}_setSubmissionState(e){this._disposed||(this._jobSubmissionState=e,this._notify())}async _submit(){if(!this._repository){this._setSubmissionState({kind:"failure",error:new Error("Job submission not configured \u2014 no network/repository was supplied to <UseSmileIDBuilder>. Call `b.network((n) => n.config((c) => { c.token = ...; c.partnerConfig((p) => { p.partnerId = ...; }); }))`.")});return}let e;try{e=this._assembleJob()}catch(i){this._setSubmissionState({kind:"failure",error:i instanceof Error?i:new Error(String(i))});return}this._setSubmissionState({kind:"loading"}),this._submissionAttemptCount+=1,this._logEvent(m.jobSubmitted,{[a.jobType]:this._jobTypeName(),[a.attempt]:String(this._submissionAttemptCount)});const t=await this._repository.submit(e,this.resolvedSdkConfig);if(!this._disposed){if(t.status==="success")this._submissionResponse=t.value,this._setSubmissionState({kind:"success",response:t.value});else if(t.status==="failure"){const i=t.error instanceof Error?t.error:new Error(String(t.error));this._setSubmissionState({kind:"failure",error:i})}}}_assembleJob(){const e=this._configuration,t=e.network?.config?.jobType??n.unknown,i=k(this._resolvedTokenPayload?.consent)?void 0:this._captured.consentInformation??e.consentInformation??q,s=i!==void 0?{consent:i}:{},c=this._metadataProvider.make(this.resolvedSdkConfig),u=[...this._documentImageOriginEntries(),...this._captureTelemetryEntries(),...w(e)],f=c.withAdditionalEntries(u);switch(t){case n.smartSelfieEnrollment:{const r=this._requireSelfie(),o=this._requireUserDetails(e);return{jobType:n.smartSelfieEnrollment,selfieImage:r.selfiePath,livenessImages:r.livenessPaths,...o!==void 0?{userDetails:o}:{},...s,metadata:f,enhanced:!1,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case n.smartSelfieAuthentication:{const r=this._requireSelfie(),o=this._requireUserDetails(e),d=this._requireUserId(e);return{jobType:n.smartSelfieAuthentication,selfieImage:r.selfiePath,livenessImages:r.livenessPaths,...o!==void 0?{userDetails:o}:{},userId:d,...s,metadata:f,enhanced:!1,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case n.biometricKyc:{const r=this._requireSelfie(),o=e.biometricKYCParams;if(!o)throw new h({fieldName:"biometricKYCParams"});const d=this._requireUserDetails(e);return{jobType:n.biometricKyc,selfieImage:r.selfiePath,livenessImages:r.livenessPaths,params:o,...d!==void 0?{userDetails:d}:{},...s,metadata:f,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case n.documentVerification:{const r=this._requireSelfie(),o=this._requireFrontDocument(),d=e.documentVerificationParams;if(!d)throw new h({fieldName:"documentVerificationParams"});const l=this._requireUserDetails(e);return{jobType:n.documentVerification,...this._documentRequestFields(r,o),country:d.country,...d.idType?{idType:d.idType}:{},...l!==void 0?{userDetails:l}:{},...s,metadata:f,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case n.enhancedDocumentVerification:{const r=this._requireSelfie(),o=this._requireFrontDocument(),d=e.enhancedDocumentVerificationParams;if(!d)throw new h({fieldName:"enhancedDocumentVerificationParams"});const l=this._requireUserDetails(e);return{jobType:n.enhancedDocumentVerification,...this._documentRequestFields(r,o),country:d.country,idType:d.idType,...l!==void 0?{userDetails:l}:{},...s,metadata:f,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case n.enhancedKyc:{const r=e.enhancedKYCParams;if(!r)throw new h({fieldName:"enhancedKYCParams"});const o=this._requireUserDetails(e);return{jobType:n.enhancedKyc,params:r,...o!==void 0?{userDetails:o}:{},...s,metadata:f}}case n.bvn:throw new I({fieldName:"jobType",reason:"BVN submission is not yet supported"});case n.unknown:default:throw new P}}_requireSelfie(){const{selfiePath:e,livenessPaths:t}=this._captured;if(!e)throw new h({fieldName:"selfieImage"});return{selfiePath:e,livenessPaths:t}}_requireUserDetails(e){const t=e.userDetails;if(!t){if(x(this._resolvedTokenPayload))return;throw new h({fieldName:"userDetails"})}const i=F(t,this._resolvedTokenPayload);return j(i)?void 0:i}_requireUserId(e){const t=e.userId?.trim();if(!t)throw new h({fieldName:"userId"});return t}_requireFrontDocument(){const{frontDocumentPath:e}=this._captured;if(!e)throw new h({fieldName:"frontDocument"});return e}_documentRequestFields(e,t){const{backDocumentPath:i}=this._captured;return{selfieImage:e.selfiePath,livenessImages:e.livenessPaths,frontDocument:t,...i?{backDocument:i}:{}}}_deleteCapturedFiles(){if(this._configuration.config?.allowOfflineMode)return;const{selfiePath:e,livenessPaths:t,frontDocumentPath:i,backDocumentPath:s}=this._captured;for(const c of[e,...t,i,s])if(c)try{const u=new b(c);u.exists&&u.delete()}catch{}}_finishFlow(e){if(this._deleteCapturedFiles(),e.kind==="completed"){const t={[a.result]:"success"};e.job.jobId&&(t[a.jobId]=e.job.jobId),this._logEvent(m.flowCompleted,t)}else e.kind==="failed"&&this._logEvent(m.flowCompleted,{[a.result]:"failure",[a.errorMessage]:e.error instanceof Error?e.error.message:String(e.error)});this._onExit(e)}_logEvent(e,t={}){const i=this._configuration.onAnalyticsEvent;i&&i({type:e,extras:{...t,[a.sessionId]:this._sessionId,[a.timestamp]:String(Date.now())}})}_jobTypeName(){return String(this._configuration.network?.config?.jobType??n.unknown)}_screenAnalyticsName(e){return{consent:g.consent,instructions:g.instructions,capture:g.capture,preview:g.preview,processing:g.processing}[e.kind]}subscribe(e){return this._listeners.add(e),()=>{this._listeners.delete(e)}}dispose(){this._disposed=!0,this._listeners.clear()}_expectedLivenessImageCount(){if(this.enhancedLivenessEnabled)return A.EXPECTED_LIVENESS_IMAGE_COUNT;const t=E(this.resolvedSdkConfig);return t!==void 0?t-1:(this._configuration.ml?.analyzers.selfieConfig?.numSamples??O.Balanced.numSamples)-1}static _decodeToken(e){if(e.config?.allowOfflineMode)return;const t=e.network?.config?.token;if(!(!t||t.length===0))return N(t)}_notify(){for(const e of this._listeners)try{e()}catch{}}}
1
+ "use strict";import{File as v}from"expo-file-system";import{JobType as o}from"../../core/models/job_type";import{CaptureType as p}from"../../core/models/ml_config";import{defaultFrameAspectRatio as D}from"../../core/models/document_type";import{MissingJobTypeException as b,MissingRequiredFieldException as h}from"../../core/exception/job_validation_exceptions";import{InvalidFieldValueException as P}from"../../core/exception/invalid_field_value_exception";import{UseSmileIDAnalyticsParamKeys as g,UseSmileIDAnalyticsScreenNames as _}from"./analytics/use_smile_id_analytics_event";import{encodeAnalyticsEventKind as I}from"./analytics/use_smile_id_analytics_event_kind";import{UseSmileIDMetadataFactory as T}from"../../metadata/use_smile_id_metadata_factory";import{DOCUMENT_BACK_IMAGE_ORIGIN_KEY as C,DOCUMENT_FRONT_IMAGE_ORIGIN_KEY as w,DOCUMENT_VISA_IMAGE_ORIGIN_KEY as E}from"../../metadata/document_image_origin";import{configurationMetadataEntries as F}from"../../metadata/metadata_configuration_mappers";import{CAPTURE_IMAGE_COLOR_DEPTH as k,CAPTURE_IMAGE_PIXEL_CONFIG as S}from"../../metadata/captured_image_properties";import{numSamplesOverride as B}from"../../data/model/sdk_config";import{decodeSmileIDToken as N}from"../../data/util/use_smile_id_token_decoder";import{isIdFieldBoundByToken as x,isTokenConsentComplete as y,bindsRequiredUserDetails as j}from"../../data/model/token_payload";import{isUserDetailsPayloadEmpty as A,userDetailsPayloadFrom as V}from"../../data/model/user_details_payload";import{DocumentSide as n,EnhancedSmartSelfieConfig as O}from"@smileid/usesmileid_bridge";import{ScanPerformanceProfile as K}from"@smileid/usesmileid_bridge";const q=Object.freeze({granted:!1,grantedAt:"",noticeLanguage:"",noticePrivacyPolicyUrl:""});export class FlowNavigationManager{_navigationPath=[];_captured={livenessPaths:[]};_documentBackSkipped=!1;_documentBorderFractions=new Map;_documentImageOrigins=new Map;_selfieCaptureTelemetry;_documentCaptureTelemetry=new Map;_cameraName;_devicePositions=[];_deviceMovementDetected=null;_jobSubmissionState={kind:"notAttempted"};_listeners=new Set;_steps;_configuration;_repository;_metadataProvider;_onExit;_disposed=!1;_submissionResponse;resolvedSdkConfig;_resolvedPartnerId;_resolvedTokenPayload;get partnerId(){const e=this._resolvedPartnerId;return e&&e.length>0?e:this._configuration.network?.config?.partnerConfig?.partnerId??""}_sessionId;_submissionAttemptCount=0;constructor(e){this._configuration=e.configuration,this._onExit=e.onExit,this._repository=e.repository,this._metadataProvider=e.metadataProvider??new T,this._sessionId=this._metadataProvider.sessionId;const t=FlowNavigationManager._decodeToken(e.configuration);this.resolvedSdkConfig=t?.sdkConfig,this._resolvedPartnerId=t?.partnerId,this._resolvedTokenPayload=t?.tokenPayload;const i=e.configuration.screens.steps;this._steps=y(t?.tokenPayload?.consent)?i.filter(c=>c.kind!=="consent"):i;const s=this._steps[0];s&&(this._navigationPath=[s]),this._logKind({kind:"flowStarted",jobType:this._jobType()}),s&&this._logKind({kind:"screenViewed",screen:this._screenAnalyticsName(s)})}get configuration(){return this._configuration}get enhancedLivenessEnabled(){return this._steps.some(e=>e.kind==="capture"&&e.config.selfieConfig?.enableEnhancedLiveness===!0)}get documentType(){for(const e of this._steps)if(e.kind==="capture"&&e.config.captureType===p.document)return e.config.documentConfig?.documentType}get documentFrameAspectRatio(){for(const e of this._steps)if(e.kind==="capture"&&e.config.captureType===p.document){const t=e.config.documentConfig;return t?.resolvedAspectRatio??D(t?.documentType?.orientation??"Landscape")}}get navigationPath(){return this._navigationPath}get currentScreen(){return this._navigationPath[this._navigationPath.length-1]}get currentDepth(){return this._navigationPath.length-1}get isFirstScreen(){return this._navigationPath.length<=1}get isLastScreen(){return this.getNextScreen()===void 0}getNextScreen(){const e=this.currentScreen;if(!e)return;const t=this._steps.indexOf(e);if(!(t<0)){if(e.kind==="preview"&&this._precedingStepIsDocumentCapture(t)&&this.supportingDocumentSide()!==void 0&&this._capturedSupportingDocument()===void 0&&!this._documentBackSkipped){const i=this._steps[t-1];if(i!==void 0)return i}return e.kind==="capture"&&e.config.captureType===p.document&&this._documentBackSkipped&&this._steps[t+1]?.kind==="preview"?this._steps[t+2]:e.kind==="capture"&&e.config.captureType===p.document&&this.supportingDocumentSide()!==void 0&&this._captured.frontDocumentPath!==void 0&&this._capturedSupportingDocument()===void 0&&!this._documentBackSkipped&&!this.documentPreviewFollowsCapture()?e:this._steps[t+1]}}navigateToNext(){if(this._disposed)return;const e=this.getNextScreen();if(!e){const t=this._jobSubmissionState;if(this._submissionResponse){this._finishFlow({kind:"completed",job:this._submissionResponse});return}if(t.kind==="failure"){this._finishFlow({kind:"failed",error:t.error});return}const i=t.kind==="success"?t.response:{userId:"",jobId:"",status:"",message:""};this._finishFlow({kind:"completed",job:i});return}this._navigationPath=[...this._navigationPath,e],this._logKind({kind:"screenViewed",screen:this._screenAnalyticsName(e)}),e.kind==="processing"&&this._jobSubmissionState.kind==="notAttempted"&&this._repository!==void 0&&(this._jobSubmissionState={kind:"loading"}),this._notify(),e.kind==="processing"&&this._jobSubmissionState.kind==="loading"&&this._repository!==void 0&&setTimeout(()=>{this._disposed||this._submit()},0)}navigateBack(){if(!this._disposed){if(this.isFirstScreen){this._onExit({kind:"cancelled"});return}this._logKind({kind:"retakeRequested"}),this._navigationPath=this._navigationPath.slice(0,-1),this._notify()}}navigateToStart(){if(this._disposed)return;const e=this._steps[0];this._navigationPath=e?[e]:[],this._notify()}exit(e={kind:"cancelled"}){this._disposed||this._finishFlow(e)}get captured(){return this._captured}setCapturedSelfie(e,t){this._disposed||(this._captured={...this._captured,selfiePath:e,livenessPaths:Object.freeze([...t])},this._logKind({kind:"selfieCaptured",livenessImageCount:t.length}),this._notify())}setCapturedFrontDocument(e,t=0){if(this._disposed)return;const i={...this._captured,frontDocumentPath:e};delete i.backDocumentPath,delete i.visaDocumentPath;for(const s of[n.Back,n.Visa])this._documentImageOrigins.delete(s),this._documentCaptureTelemetry.delete(s),this._documentBorderFractions.delete(s);this._setDocumentBorderFraction(n.Front,t),this._captured=i,this._logKind({kind:"documentCaptured",side:n.Front}),this._notify()}setCapturedBackDocument(e,t=0){this._disposed||(this._captured={...this._captured,backDocumentPath:e},this._setDocumentBorderFraction(n.Back,t),this._logKind({kind:"documentCaptured",side:n.Back}),this._notify())}supportingDocumentSide(){if(this._configuration.network?.config?.jobType===o.residencyDocumentVerification)return n.Visa;for(const e of this._steps)if(e.kind==="capture"&&e.config.captureType===p.document){const t=e.config.documentConfig;return t?.documentType?.hasBackSide===!0&&(t?.captureBothSides??!1)?n.Back:void 0}}documentCapturesBack(){return this.supportingDocumentSide()===n.Back}_capturedSupportingDocument(){switch(this.supportingDocumentSide()){case n.Visa:return this._captured.visaDocumentPath;case n.Back:return this._captured.backDocumentPath;default:return}}currentDocumentCaptureSide(){return this._captured.frontDocumentPath===void 0?n.Front:this.supportingDocumentSide()??n.Back}setCapturedVisaDocument(e,t=0){this._disposed||(this._captured={...this._captured,visaDocumentPath:e},this._setDocumentBorderFraction(n.Visa,t),this._logKind({kind:"documentCaptured",side:n.Visa}),this._notify())}documentPreviewFollowsCapture(){const e=this._steps.findIndex(t=>t.kind==="capture"&&t.config.captureType===p.document);return e<0?!1:this._steps[e+1]?.kind==="preview"}currentPreviewIsDocument(){const e=this.currentScreen;return!e||e.kind!=="preview"?!1:this._precedingStepIsDocumentCapture(this._steps.indexOf(e))}currentDocumentPreviewPath(){return this._capturedSupportingDocument()??this._captured.frontDocumentPath}documentBorderFraction(e){return this._documentBorderFractions.get(e)??0}currentDocumentPreviewBorderFraction(){const e=this.supportingDocumentSide();return e!==void 0&&this._capturedSupportingDocument()!==void 0?this.documentBorderFraction(e):this.documentBorderFraction(n.Front)}_setDocumentBorderFraction(e,t){Number.isFinite(t)&&t>0?this._documentBorderFractions.set(e,t):this._documentBorderFractions.delete(e)}retakeCurrentDocumentSide(){if(this._disposed)return;const e={...this._captured},t=this.supportingDocumentSide();t!==void 0&&this._capturedSupportingDocument()!==void 0?(t===n.Visa?delete e.visaDocumentPath:delete e.backDocumentPath,this._documentImageOrigins.delete(t),this._documentCaptureTelemetry.delete(t),this._documentBorderFractions.delete(t)):(delete e.frontDocumentPath,this._documentImageOrigins.delete(n.Front),this._documentBorderFractions.delete(n.Front),this._documentBackSkipped=!1),this._captured=e,this._notify()}skipDocumentBack(){this._disposed||this.supportingDocumentSide()!==n.Visa&&(this._documentBackSkipped=!0,this._notify())}recordDocumentImageOrigin(e,t){this._disposed||this._documentImageOrigins.set(e,t)}recordSelfieCaptureTelemetry(e){this._disposed||(this._selfieCaptureTelemetry=e,this._captureCameraName(e))}recordDocumentCaptureTelemetry(e,t){this._disposed||(this._documentCaptureTelemetry.set(e,t),this._captureCameraName(t))}_captureCameraName(e){e.cameraName&&(this._cameraName=e.cameraName),e.devicePositions.length>0&&(this._devicePositions=e.devicePositions,this._deviceMovementDetected=e.deviceMovementDetected)}_documentImageOriginEntries(){const e=[],t=this._documentImageOrigins.get(n.Front);t!==void 0&&e.push({name:w,value:t});const i=this._documentImageOrigins.get(n.Back);i!==void 0&&e.push({name:C,value:i});const s=this._documentImageOrigins.get(n.Visa);return s!==void 0&&e.push({name:E,value:s}),e}_captureTelemetryEntries(){const e=[],t=(c,u)=>e.push({name:c,value:u}),i=this._selfieCaptureTelemetry;i&&(t("selfie_capture_duration_ms",String(i.durationMs)),t("selfie_capture_retries",String(i.retries))),this._captured.selfiePath!==void 0&&(t("selfie_image_color_depth",String(k)),t("selfie_image_pixel_config",S)),this._cameraName&&t("camera_name",this._cameraName),this._devicePositions.length>0&&(t("device_positions",JSON.stringify(this._devicePositions)),this._deviceMovementDetected!==null&&t("device_movement_detected",String(this._deviceMovementDetected)));const s=c=>{switch(c){case n.Back:return"back";case n.Visa:return"visa";default:return"front"}};for(const c of[n.Front,n.Back,n.Visa]){const u=s(c),m=this._documentCaptureTelemetry.get(c);m&&(t(`document_${u}_capture_duration_ms`,String(m.durationMs)),t(`document_${u}_capture_retries`,String(m.retries)),t(`document_${u}_image_color_depth`,String(k)),t(`document_${u}_image_pixel_config`,S))}return e}_precedingStepIsDocumentCapture(e){const t=this._steps[e-1];return t?.kind==="capture"&&t.config.captureType===p.document}setConsent(e){this._disposed||(this._captured={...this._captured,consentInformation:e},this._logKind({kind:"consentCaptured"}),this._notify())}get jobSubmissionState(){return this._jobSubmissionState}beginSubmissionIfNeeded(){this._disposed||this._jobSubmissionState.kind==="notAttempted"&&this._submit()}retrySubmission(){this._disposed||this._jobSubmissionState.kind!=="loading"&&(this._setSubmissionState({kind:"notAttempted"}),this.beginSubmissionIfNeeded())}_setSubmissionState(e){this._disposed||(this._jobSubmissionState=e,this._notify())}async _submit(){if(!this._repository){this._setSubmissionState({kind:"failure",error:new Error("Job submission not configured \u2014 no network/repository was supplied to <UseSmileIDBuilder>. Call `b.network((n) => n.config((c) => { c.token = ...; c.partnerConfig((p) => { p.partnerId = ...; }); }))`.")});return}let e;try{e=this._assembleJob()}catch(i){this._setSubmissionState({kind:"failure",error:i instanceof Error?i:new Error(String(i))});return}this._setSubmissionState({kind:"loading"}),this._submissionAttemptCount+=1,this._logKind({kind:"jobSubmitted",jobType:this._jobType(),attempt:this._submissionAttemptCount});const t=await this._repository.submit(e,this.resolvedSdkConfig);if(!this._disposed){if(t.status==="success")this._submissionResponse=t.value,this._setSubmissionState({kind:"success",response:t.value});else if(t.status==="failure"){const i=t.error instanceof Error?t.error:new Error(String(t.error));this._setSubmissionState({kind:"failure",error:i})}}}_assembleJob(){const e=this._configuration,t=e.network?.config?.jobType??o.unknown,i=y(this._resolvedTokenPayload?.consent)?void 0:this._captured.consentInformation??e.consentInformation??q,s=i!==void 0?{consent:i}:{},c=this._metadataProvider.make(this.resolvedSdkConfig),u=[...this._documentImageOriginEntries(),...this._captureTelemetryEntries(),...F(e)],m=c.withAdditionalEntries(u);switch(t){case o.smartSelfieEnrollment:{const r=this._requireSelfie(),a=this._requireUserDetails(e);return{jobType:o.smartSelfieEnrollment,selfieImage:r.selfiePath,livenessImages:r.livenessPaths,...a!==void 0?{userDetails:a}:{},...s,metadata:m,enhanced:!1,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case o.smartSelfieAuthentication:{const r=this._requireSelfie(),a=this._requireUserDetails(e),d=this._requireUserId(e);return{jobType:o.smartSelfieAuthentication,selfieImage:r.selfiePath,livenessImages:r.livenessPaths,...a!==void 0?{userDetails:a}:{},userId:d,...s,metadata:m,enhanced:!1,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case o.biometricKyc:{const r=this._requireSelfie(),a=e.biometricKYCParams;if(!a)throw new h({fieldName:"biometricKYCParams"});const d=this._requireUserDetails(e);return{jobType:o.biometricKyc,selfieImage:r.selfiePath,livenessImages:r.livenessPaths,params:{...a,...this._kycIdFieldsToSubmit(a)},...d!==void 0?{userDetails:d}:{},...s,metadata:m,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case o.documentVerification:{const r=this._requireSelfie(),a=this._requireFrontDocument(),d=e.documentVerificationParams;if(!d)throw new h({fieldName:"documentVerificationParams"});const f=this._requireUserDetails(e);return{jobType:o.documentVerification,...this._documentRequestFields(r,a),country:this._unlessTokenBinds(d.country,"country"),...d.idType?{idType:this._unlessTokenBinds(d.idType,"idType")}:{},...f!==void 0?{userDetails:f}:{},...s,metadata:m,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case o.enhancedDocumentVerification:{const r=this._requireSelfie(),a=this._requireFrontDocument(),d=e.enhancedDocumentVerificationParams;if(!d)throw new h({fieldName:"enhancedDocumentVerificationParams"});const f=this._requireUserDetails(e);return{jobType:o.enhancedDocumentVerification,...this._documentRequestFields(r,a),country:this._unlessTokenBinds(d.country,"country"),idType:this._unlessTokenBinds(d.idType,"idType"),...f!==void 0?{userDetails:f}:{},...s,metadata:m,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case o.residencyDocumentVerification:{const r=this._requireSelfie(),a=this._requireFrontDocument(),d=e.residencyDocumentVerificationParams;if(!d)throw new h({fieldName:"residencyDocumentVerificationParams"});const f=this._capturedVisaDocument();if(!f)throw new h({fieldName:"visaDocument"});const l=this._requireUserDetails(e);return{jobType:o.residencyDocumentVerification,selfieImage:r.selfiePath,livenessImages:r.livenessPaths,frontDocument:a,visaDocument:f,country:this._unlessTokenBinds(d.country,"country"),idType:this._unlessTokenBinds(d.idType,"idType"),...l!==void 0?{userDetails:l}:{},...s,metadata:m,expectedLivenessImageCount:this._expectedLivenessImageCount()}}case o.enhancedKyc:{const r=e.enhancedKYCParams;if(!r)throw new h({fieldName:"enhancedKYCParams"});const a=this._requireUserDetails(e);return{jobType:o.enhancedKyc,params:{...r,...this._kycIdFieldsToSubmit(r)},...a!==void 0?{userDetails:a}:{},...s,metadata:m}}case o.bvn:throw new P({fieldName:"jobType",reason:"BVN submission is not yet supported"});case o.unknown:default:throw new b}}_requireSelfie(){const{selfiePath:e,livenessPaths:t}=this._captured;if(!e)throw new h({fieldName:"selfieImage"});return{selfiePath:e,livenessPaths:t}}_unlessTokenBinds(e,t){if(x(this._resolvedTokenPayload,t))return"";if(!e.trim())throw new h({fieldName:t});return e}_kycIdFieldsToSubmit(e){return{idType:this._unlessTokenBinds(e.idType,"idType"),idNumber:this._unlessTokenBinds(e.idNumber,"idNumber"),country:this._unlessTokenBinds(e.country,"country")}}_requireUserDetails(e){const t=e.userDetails;if(!t){if(j(this._resolvedTokenPayload))return;throw new h({fieldName:"userDetails"})}const i=V(t,this._resolvedTokenPayload);return A(i)?void 0:i}_requireUserId(e){const t=e.userId?.trim();if(!t)throw new h({fieldName:"userId"});return t}_requireFrontDocument(){const{frontDocumentPath:e}=this._captured;if(!e)throw new h({fieldName:"frontDocument"});return e}_documentRequestFields(e,t){const{backDocumentPath:i}=this._captured;return{selfieImage:e.selfiePath,livenessImages:e.livenessPaths,frontDocument:t,...i?{backDocument:i}:{}}}_capturedVisaDocument(){return this._captured.visaDocumentPath}_deleteCapturedFiles(){if(this._configuration.config?.allowOfflineMode)return;const{selfiePath:e,livenessPaths:t,frontDocumentPath:i,backDocumentPath:s}=this._captured;for(const c of[e,...t,i,s])if(c)try{const u=new v(c);u.exists&&u.delete()}catch{}}_finishFlow(e){this._deleteCapturedFiles(),e.kind==="completed"?this._logKind(e.job.jobId?{kind:"flowCompletedSuccess",jobId:e.job.jobId}:{kind:"flowCompletedSuccess"}):e.kind==="failed"&&this._logKind({kind:"flowCompletedFailure",errorMessage:e.error instanceof Error?e.error.message:String(e.error)}),this._onExit(e)}logAnalyticsEvent(e){this._disposed||this._logKind(e)}_logKind(e){const{type:t,extras:i}=I(e);this._logEvent(t,i)}_logEvent(e,t={}){const i=this._configuration.onAnalyticsEvent;i&&i({type:e,extras:{...t,[g.sessionId]:this._sessionId,[g.timestamp]:String(Date.now())}})}_jobType(){return this._configuration.network?.config?.jobType??o.unknown}_screenAnalyticsName(e){return{consent:_.consent,instructions:_.instructions,capture:_.capture,preview:_.preview,processing:_.processing}[e.kind]}subscribe(e){return this._listeners.add(e),()=>{this._listeners.delete(e)}}dispose(){this._disposed=!0,this._listeners.clear()}_expectedLivenessImageCount(){if(this.enhancedLivenessEnabled)return O.EXPECTED_LIVENESS_IMAGE_COUNT;const t=B(this.resolvedSdkConfig);return t!==void 0?t-1:(this._configuration.ml?.analyzers.selfieConfig?.numSamples??K.Balanced.numSamples)-1}static _decodeToken(e){if(e.config?.allowOfflineMode)return;const t=e.network?.config?.token;if(!(!t||t.length===0))return N(t)}_notify(){for(const e of this._listeners)try{e()}catch{}}}
@@ -11,6 +11,7 @@ export declare const DOCUMENT_HERO_ANIMATION: AnimationObject;
11
11
  export declare const DOCUMENT_PASSPORT_HERO_ANIMATION: AnimationObject;
12
12
  export declare const DOCUMENT_GREEN_BOOK_HERO_ANIMATION: AnimationObject;
13
13
  export declare const DOCUMENT_HERO_BACK_ANIMATION: AnimationObject;
14
+ export declare const DOCUMENT_HERO_VISA_ANIMATION: AnimationObject;
14
15
  /**
15
16
  * Default Smart Selfie capture guidelines — allocated once and deeply frozen
16
17
  * (array + each entry) so the shared default never churns and can't be
@@ -1 +1 @@
1
- "use strict";import{SiGuidelineAccessories as r,SiGuidelineDocumentGenericGood as u,SiGuidelineDocumentGenericNotBlurry as c,SiGuidelineDocumentGenericNotCropped as l,SiGuidelineDocumentGenericNotReflective as d,SiGuidelineDocumentGreenBookGood as a,SiGuidelineDocumentGreenBookNotBlurry as _,SiGuidelineDocumentGreenBookNotCropped as G,SiGuidelineDocumentGreenBookNotReflective as p,SiGuidelineDocumentPassportGood as N,SiGuidelineDocumentPassportNotBlurry as E,SiGuidelineDocumentPassportNotCropped as m,SiGuidelineDocumentPassportNotReflective as D,SiGuidelineGood as O,SiGuidelineMultipleFaces as I,SiGuidelinePoorLighting as S}from"../../components/icons";export const SELFIE_HERO_ANIMATION=require("../../assets/si_selfie_hero_animation.json"),ESS_HERO_ANIMATION=require("../../assets/si_ess_all_directions_hero_animation.json"),DOCUMENT_HERO_ANIMATION=require("../../assets/si_document_hero_animation.json"),DOCUMENT_PASSPORT_HERO_ANIMATION=require("../../assets/si_document_passport_hero_animation.json"),DOCUMENT_GREEN_BOOK_HERO_ANIMATION=require("../../assets/si_document_green_book_hero_animation.json"),DOCUMENT_HERO_BACK_ANIMATION=require("../../assets/si_document_hero_back_animation.json");const A=[{image:O,label:e=>e.siInstructionsGuidelineGood,status:"allowed"},{image:r,label:e=>e.siInstructionsGuidelineAccessories,status:"blocked"},{image:I,label:e=>e.siInstructionsGuidelineMultipleFaces,status:"blocked"},{image:S,label:e=>e.siInstructionsGuidelinePoorLighting,status:"blocked"}];export const SELFIE_GUIDELINES=Object.freeze(A.map(e=>Object.freeze(e)));const n=Object.freeze({width:80,height:56}),R=Object.freeze({width:60,height:84});function i(e,t){const s=[{image:e.good,label:o=>o.siDocumentGuidelineGood,status:"allowed",card:t},{image:e.notCropped,label:o=>o.siDocumentGuidelineNotCropped,status:"blocked",card:t},{image:e.notBlurry,label:o=>o.siDocumentGuidelineNotBlurry,status:"blocked",card:t},{image:e.notReflective,label:o=>o.siDocumentGuidelineNotReflective,status:"blocked",card:t}];return Object.freeze(s.map(o=>Object.freeze(o)))}export const DOCUMENT_GUIDELINES_GENERIC=i({good:u,notCropped:l,notBlurry:c,notReflective:d},n),DOCUMENT_GUIDELINES_PASSPORT=i({good:N,notCropped:m,notBlurry:E,notReflective:D},n),DOCUMENT_GUIDELINES_GREEN_BOOK=i({good:a,notCropped:G,notBlurry:_,notReflective:p},R);export function documentGuidelines(e){switch(e?.kind){case"southAfricaGreenBook":return DOCUMENT_GUIDELINES_GREEN_BOOK;case"passport":return DOCUMENT_GUIDELINES_PASSPORT;default:return DOCUMENT_GUIDELINES_GENERIC}}export function documentHeroAnimation(e){switch(e?.kind){case"southAfricaGreenBook":return DOCUMENT_GREEN_BOOK_HERO_ANIMATION;case"passport":return DOCUMENT_PASSPORT_HERO_ANIMATION;default:return DOCUMENT_HERO_ANIMATION}}
1
+ "use strict";import{SiGuidelineAccessories as r,SiGuidelineDocumentGenericGood as u,SiGuidelineDocumentGenericNotBlurry as c,SiGuidelineDocumentGenericNotCropped as l,SiGuidelineDocumentGenericNotReflective as a,SiGuidelineDocumentGreenBookGood as d,SiGuidelineDocumentGreenBookNotBlurry as _,SiGuidelineDocumentGreenBookNotCropped as G,SiGuidelineDocumentGreenBookNotReflective as N,SiGuidelineDocumentPassportGood as p,SiGuidelineDocumentPassportNotBlurry as E,SiGuidelineDocumentPassportNotCropped as m,SiGuidelineDocumentPassportNotReflective as O,SiGuidelineGood as D,SiGuidelineMultipleFaces as I,SiGuidelinePoorLighting as S}from"../../components/icons";export const SELFIE_HERO_ANIMATION=require("../../assets/si_selfie_hero_animation.json"),ESS_HERO_ANIMATION=require("../../assets/si_ess_all_directions_hero_animation.json"),DOCUMENT_HERO_ANIMATION=require("../../assets/si_document_hero_animation.json"),DOCUMENT_PASSPORT_HERO_ANIMATION=require("../../assets/si_document_passport_hero_animation.json"),DOCUMENT_GREEN_BOOK_HERO_ANIMATION=require("../../assets/si_document_green_book_hero_animation.json"),DOCUMENT_HERO_BACK_ANIMATION=require("../../assets/si_document_hero_back_animation.json"),DOCUMENT_HERO_VISA_ANIMATION=require("../../assets/si_document_visa_hero_animation.json");const A=[{image:D,label:e=>e.siInstructionsGuidelineGood,status:"allowed"},{image:r,label:e=>e.siInstructionsGuidelineAccessories,status:"blocked"},{image:I,label:e=>e.siInstructionsGuidelineMultipleFaces,status:"blocked"},{image:S,label:e=>e.siInstructionsGuidelinePoorLighting,status:"blocked"}];export const SELFIE_GUIDELINES=Object.freeze(A.map(e=>Object.freeze(e)));const n=Object.freeze({width:80,height:56}),R=Object.freeze({width:60,height:84});function i(e,t){const s=[{image:e.good,label:o=>o.siDocumentGuidelineGood,status:"allowed",card:t},{image:e.notCropped,label:o=>o.siDocumentGuidelineNotCropped,status:"blocked",card:t},{image:e.notBlurry,label:o=>o.siDocumentGuidelineNotBlurry,status:"blocked",card:t},{image:e.notReflective,label:o=>o.siDocumentGuidelineNotReflective,status:"blocked",card:t}];return Object.freeze(s.map(o=>Object.freeze(o)))}export const DOCUMENT_GUIDELINES_GENERIC=i({good:u,notCropped:l,notBlurry:c,notReflective:a},n),DOCUMENT_GUIDELINES_PASSPORT=i({good:p,notCropped:m,notBlurry:E,notReflective:O},n),DOCUMENT_GUIDELINES_GREEN_BOOK=i({good:d,notCropped:G,notBlurry:_,notReflective:N},R);export function documentGuidelines(e){switch(e?.kind){case"southAfricaGreenBook":return DOCUMENT_GUIDELINES_GREEN_BOOK;case"passport":return DOCUMENT_GUIDELINES_PASSPORT;default:return DOCUMENT_GUIDELINES_GENERIC}}export function documentHeroAnimation(e){switch(e?.kind){case"southAfricaGreenBook":return DOCUMENT_GREEN_BOOK_HERO_ANIMATION;case"passport":return DOCUMENT_PASSPORT_HERO_ANIMATION;default:return DOCUMENT_HERO_ANIMATION}}
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ButtonSlot } from "../../dsl/builder/screens_builder";
3
+ /** A screen slot for a button that is always actionable once shown, so it takes no `enabled`. */
4
+ export type AlwaysEnabledSlot = (props: {
5
+ onClick: () => void;
6
+ }) => ReactNode;
7
+ /** Adapts a partner's `{ onClick, enabled }` slot to a screen's always-actionable one; an absent slot stays absent, so the screen draws its default. */
8
+ export declare function alwaysEnabledSlot(slot: ButtonSlot | undefined): AlwaysEnabledSlot | undefined;
@@ -0,0 +1 @@
1
+ "use strict";export function alwaysEnabledSlot(e){return e===void 0?void 0:({onClick:n})=>e({onClick:n,enabled:!0})}
@@ -1 +1 @@
1
- "use strict";import{jsx as h}from"react/jsx-runtime";import{useEffect as R,useState as E}from"react";import{CaptureReadyEventKind as w,DeviceIntegrityProvider as K,DocumentSide as l,ImageType as S}from"@smileid/usesmileid_bridge";import{CaptureType as o}from"../../../core/models/ml_config";import{defaultFrameAspectRatio as g,documentFrameOrientation as j}from"../../../core/models/document_type";import{SmileExifTagger as D}from"@smileid/usesmileid_bridge/exif/smile_exif_tagger";import{resolveDeviceModel as G}from"../../../metadata/use_smile_id_metadata_factory";import{claimAnalyzer as O,releaseAnalyzer as J}from"./helpers/analyzer_lifecycle";import{resolveFaceSessionConfig as Q}from"./helpers/face_session_config";import{useCaptureOrientationLock as $}from"../../screen/helpers/use_capture_orientation_lock";import{resolveJpegQuality as H}from"../../../data/model/sdk_config_capture";import{AttributionResolver as _,flowShowAttribution as b}from"../config/attribution_resolver";import{UseSmileIDCaptureScreen as q}from"../../screen/use_smile_id_capture_screen";import{cameraExceptionFromKameraError as V}from"../../../camera/kamera_error_mapping";import{defaultLightTheme as W}from"../../theme/theme";import{UseSmileIDDocumentBackInstructions as X}from"../../screen/use_smile_id_document_back_instructions";import{useFlowNavigation as Y}from"../flow_navigation_context";import{UseSmileIDOrchestratedScreenHost as Z}from"./use_smile_id_orchestrated_screen_host";export function OrchestratedCaptureScreen({configuration:n}){const r=Y(),t=n.captureType,i=n.documentConfig,d=i?.documentType,a=d?.hasBackSide===!0&&(i?.captureBothSides??!1),v=i?.allowSkipBack??!1,B=r.documentPreviewFollowsCapture(),F=r.captured.frontDocumentPath!==void 0,[c,A]=E(a&&F?l.Back:l.Front),[T,k]=E(a&&F),I=r.configuration.config?.exitOrientations,x=t===o.document&&!T?j(d,i?.resolvedAspectRatio):"Portrait";$(x,{...I!==void 0?{exitLock:I}:{}});const s=t===o.selfie?r.configuration.ml?.analyzers.selfie:r.configuration.ml?.analyzers.document;return R(()=>{if(!s)return;let e=0;if(t===o.selfie&&"key"in s){const m=r.configuration.ml?.analyzers.selfieConfig??{analyzerFactoryKey:s.key};e=O(()=>s.configure(Q(m,n.selfieConfig?.enableEnhancedLiveness??!1,r.resolvedSdkConfig)))}else if(t===o.document&&!("key"in s)){const m=H(r.resolvedSdkConfig?.document);e=O(()=>s.configure({documentSide:c,...m!==void 0?{documentJpegQuality:m}:{}}))}return()=>{J(e,s)}},[s,t,r,n,c]),h(Z,{content:({navigationManager:e,onBack:m,onNavigateNext:y})=>{if(!s)throw new Error(`OrchestratedCaptureScreen: no ${t} analyzer registered in the ML DSL`);return t===o.document&&a&&T?h(X,{documentTypeDisplayName:d?.displayName??"",allowSkip:v,showAttribution:b(e.configuration),onCaptureBack:()=>k(!1),onSkip:()=>{e.skipDocumentBack(),y()},onBack:()=>{if(B){m();return}k(!1),A(l.Front)}}):h(q,{captureType:t,analyzer:s,theme:e.configuration.theme?.light??W,enableEnhancedLiveness:n.selfieConfig?.enableEnhancedLiveness??!1,allowAgentMode:n.selfieConfig?.allowAgentMode??!1,showAttribution:_.resolveForCapture({captureType:t,showAttribution:b(e.configuration)}),...t===o.document&&i?{knownIdAspectRatio:i.resolvedAspectRatio??g(d?.orientation??"Landscape"),captureMode:i.captureMode,allowGalleryUpload:i.allowGalleryUpload,documentSide:c,...d?.kind?{documentTypeKind:d.kind}:{},onDocumentOrigin:f=>e.recordDocumentImageOrigin(c,f),onClose:()=>e.exit()}:{},...n.startButton!==void 0?{startButton:n.startButton}:{},onBack:()=>{if(t===o.document&&a&&c===l.Back){k(!0);return}m()},onCameraError:(f,C)=>{e.exit({kind:"failed",error:V(f,C)})},onCaptureComplete:(f,C)=>{const p=M(f,t);if(!p)return;const U=e.partnerId,P=t===o.selfie?S.Selfie:a&&c===l.Back?S.DocumentBack:S.DocumentFront,z=f.filter(u=>u.kind===w.Liveness).map(u=>u.url),L=G();K.signals().then(({fingerprint:u})=>D.setContext({partnerId:U,...L?{deviceModel:L}:{},...u?{fingerprint:u}:{}})).then(async()=>{await D.finalize(p,P),await Promise.all(z.map(u=>D.finalize(u,S.Liveness)))}).catch(()=>{}).finally(async()=>{if(t===o.selfie){e.setCapturedSelfie(p,z),e.recordSelfieCaptureTelemetry(C),y();return}if(e.recordDocumentCaptureTelemetry(c,C),a&&c===l.Front){e.setCapturedFrontDocument(p),B?y():(A(l.Back),k(!0));return}if(a&&c===l.Back){e.setCapturedBackDocument(p),y();return}e.setCapturedFrontDocument(p),y()})}},t===o.document?`document-capture-${c}`:"capture")}})}function M(n,r){const t=r===o.selfie?w.Selfie:w.Document;return n.find(i=>i.kind===t)?.url}
1
+ "use strict";import{jsx as I}from"react/jsx-runtime";import{useEffect as G,useState as P}from"react";import{CaptureReadyEventKind as A,DeviceIntegrityProvider as H,DocumentSide as u,ImageType as h}from"@smileid/usesmileid_bridge";import{CaptureType as i}from"../../../core/models/ml_config";import{defaultFrameAspectRatio as _,documentFrameOrientation as j}from"../../../core/models/document_type";import{SmileExifTagger as B}from"@smileid/usesmileid_bridge/exif/smile_exif_tagger";import{resolveDeviceModel as J}from"../../../metadata/use_smile_id_metadata_factory";import{claimAnalyzer as x,releaseAnalyzer as Q}from"./helpers/analyzer_lifecycle";import{resolveFaceSessionConfig as $}from"./helpers/face_session_config";import{useCaptureOrientationLock as q}from"../../screen/helpers/use_capture_orientation_lock";import{resolveJpegQuality as W}from"../../../data/model/sdk_config_capture";import{AttributionResolver as X,flowShowAttribution as F}from"../config/attribution_resolver";import{UseSmileIDCaptureScreen as Y}from"../../screen/use_smile_id_capture_screen";import{cameraExceptionFromKameraError as Z}from"../../../camera/kamera_error_mapping";import{defaultLightTheme as N}from"../../theme/theme";import{UseSmileIDDocumentBackInstructions as M}from"../../screen/use_smile_id_document_back_instructions";import{UseSmileIDInstructionsScreen as ee}from"../../screen/use_smile_id_instructions_screen";import{DOCUMENT_HERO_VISA_ANIMATION as te,documentGuidelines as oe}from"../model/instructions_defaults";import{useUseSmileIDLocalizations as re}from"../../../l10n/use_smile_id_localizations_provider";import{useFlowNavigation as ne}from"../flow_navigation_context";import{UseSmileIDOrchestratedScreenHost as ie}from"./use_smile_id_orchestrated_screen_host";export function OrchestratedCaptureScreen({configuration:s}){const r=ne(),C=re(),t=s.captureType,m=s.documentConfig,f=m?.documentType,S=r.supportingDocumentSide(),p=S!==void 0,v=m?.allowSkipBack??!1,T=r.documentPreviewFollowsCapture(),E=r.captured.frontDocumentPath!==void 0,[o,O]=P(p&&E&&S!==void 0?S:u.Front),[z,k]=P(p&&E),L=r.configuration.config?.exitOrientations,R=t===i.document&&!z?j(f,m?.resolvedAspectRatio):"Portrait";q(R,{...L!==void 0?{exitLock:L}:{}});const c=t===i.selfie?r.configuration.ml?.analyzers.selfie:r.configuration.ml?.analyzers.document;return G(()=>{if(!c)return;let e=0;if(t===i.selfie&&"key"in c){const a=r.configuration.ml?.analyzers.selfieConfig??{analyzerFactoryKey:c.key};e=x(()=>c.configure($(a,s.selfieConfig?.enableEnhancedLiveness??!1,r.resolvedSdkConfig)))}else if(t===i.document&&!("key"in c)){const a=W(r.resolvedSdkConfig?.document);e=x(()=>c.configure({documentSide:o,...a!==void 0?{documentJpegQuality:a}:{}}))}return()=>{Q(e,c)}},[c,t,r,s,o]),I(ie,{content:({navigationManager:e,onBack:a,onNavigateNext:y})=>{if(!c)throw new Error(`OrchestratedCaptureScreen: no ${t} analyzer registered in the ML DSL`);if(t===i.document&&p&&z){const n=()=>{if(T){a();return}k(!1),O(u.Front)};return o===u.Visa?I(ee,{titlePrefix:C.siInstructionsTitlePrefix,titleEmphasis:C.siInstructionsEmphasisVisa,heroSource:te,guidelinesSectionTitle:C.siInstructionsCaptureGuidelines,guidelines:oe(f),showAttribution:F(e.configuration),showHeroOval:!1,showHeroOvalProgress:!1,showCardBackground:!0,onContinue:()=>k(!1),onBack:n}):I(M,{documentTypeDisplayName:f?.displayName??"",allowSkip:v,showAttribution:F(e.configuration),onCaptureBack:()=>k(!1),onSkip:()=>{e.skipDocumentBack(),y()},onBack:n})}return I(Y,{captureType:t,analyzer:c,onAnalyticsEvent:n=>e.logAnalyticsEvent(n),theme:e.configuration.theme?.light??N,enableEnhancedLiveness:s.selfieConfig?.enableEnhancedLiveness??!1,allowAgentMode:s.selfieConfig?.allowAgentMode??!1,showAttribution:X.resolveForCapture({captureType:t,showAttribution:F(e.configuration)}),...t===i.document&&m?{knownIdAspectRatio:m.resolvedAspectRatio??_(f?.orientation??"Landscape"),captureMode:m.captureMode,allowGalleryUpload:m.allowGalleryUpload,documentSide:o,...f?.kind?{documentTypeKind:f.kind}:{},onDocumentOrigin:n=>e.recordDocumentImageOrigin(o,n),onClose:()=>e.exit()}:{},...s.startButton!==void 0?{startButton:s.startButton}:{},onBack:()=>{if(t===i.document&&p&&o!==u.Front){k(!0);return}a()},onCameraError:(n,w)=>{e.exit({kind:"failed",error:Z(n,w)})},onCaptureComplete:(n,w,V)=>{const D=V??0,d=se(n,t);if(!d)return;const K=e.partnerId,g=t===i.selfie?h.Selfie:o===u.Visa?h.DocumentVisa:o===u.Back?h.DocumentBack:h.DocumentFront,b=n.filter(l=>l.kind===A.Liveness).map(l=>l.url),U=J();H.signals().then(({fingerprint:l})=>B.setContext({partnerId:K,...U?{deviceModel:U}:{},...l?{fingerprint:l}:{}})).then(async()=>{await B.finalize(d,g),await Promise.all(b.map(l=>B.finalize(l,h.Liveness)))}).catch(()=>{}).finally(async()=>{if(t===i.selfie){e.setCapturedSelfie(d,b),e.recordSelfieCaptureTelemetry(w),y();return}if(e.recordDocumentCaptureTelemetry(o,w),p&&S!==void 0&&o===u.Front){e.setCapturedFrontDocument(d,D),T?y():(O(S),k(!0));return}if(o===u.Visa){e.setCapturedVisaDocument(d,D),y();return}if(p&&o===u.Back){e.setCapturedBackDocument(d,D),y();return}e.setCapturedFrontDocument(d,D),y()})}},t===i.document?`document-capture-${o}`:"capture")}})}function se(s,r){const C=r===i.selfie?A.Selfie:A.Document;return s.find(t=>t.kind===C)?.url}
@@ -1 +1 @@
1
- "use strict";import{jsx as o}from"react/jsx-runtime";import{UseSmileIDCrashReporting as i}from"@smileid/usesmileid_bridge";import{ConsentDeniedException as l}from"../../../core/exception";import{GlobalPrivacyControl as p}from"../../../core/models/global_privacy_control";import{flowShowAttribution as m}from"../config/attribution_resolver";import{UseSmileIDConsentScreen as s}from"../../screen/use_smile_id_consent_screen";import{UseSmileIDOrchestratedScreenHost as a}from"./use_smile_id_orchestrated_screen_host";export function OrchestratedConsentScreen({configuration:r}){return o(a,{content:({navigationManager:e,onNavigateNext:n})=>o(s,{partnerName:r.partnerName,globalPrivacyControl:e.configuration.config?.globalPrivacyControl??p.OptOut,...r.partnerIcon!==void 0?{partnerIcon:r.partnerIcon}:{},...r.partnerPrivacyPolicyUrl!==void 0?{partnerPrivacyPolicyUrl:r.partnerPrivacyPolicyUrl}:{},showAttribution:m(e.configuration),onAllow:t=>{e.setConsent(t);try{r.onConsentGranted?.(t)}catch(c){i.captureException(c)}n()},onDeny:()=>{e.exit({kind:"failed",error:new l({timestamp:Date.now()})})}})})}
1
+ "use strict";import{jsx as n}from"react/jsx-runtime";import{UseSmileIDCrashReporting as p}from"@smileid/usesmileid_bridge";import{ConsentDeniedException as c}from"../../../core/exception";import{GlobalPrivacyControl as s}from"../../../core/models/global_privacy_control";import{flowShowAttribution as i}from"../config/attribution_resolver";import{UseSmileIDConsentScreen as a}from"../../screen/use_smile_id_consent_screen";import{UseSmileIDOrchestratedScreenHost as d}from"./use_smile_id_orchestrated_screen_host";import{alwaysEnabledSlot as y}from"./helpers/always_enabled_slot";export function OrchestratedConsentScreen({configuration:r}){const e=y(r.denyButton);return n(d,{content:({navigationManager:t,onNavigateNext:l})=>n(a,{partnerName:r.partnerName,globalPrivacyControl:t.configuration.config?.globalPrivacyControl??s.OptOut,...r.partnerIcon!==void 0?{partnerIcon:r.partnerIcon}:{},...r.partnerPrivacyPolicyUrl!==void 0?{partnerPrivacyPolicyUrl:r.partnerPrivacyPolicyUrl}:{},showAttribution:i(t.configuration),...r.allowButton?{allowButton:r.allowButton}:{},...e!==void 0?{denyButton:e}:{},onAllow:o=>{t.setConsent(o);try{r.onConsentGranted?.(o)}catch(m){p.captureException(m)}l()},onDeny:()=>{t.exit({kind:"failed",error:new c({timestamp:Date.now()})})}})})}
@@ -1 +1 @@
1
- "use strict";import{jsx as s}from"react/jsx-runtime";import{useCallback as a}from"react";import{flowShowAttribution as m}from"../config/attribution_resolver";import{UseSmileIDInstructionsScreen as d}from"../../screen/use_smile_id_instructions_screen";import{UseSmileIDOrchestratedScreenHost as h}from"./use_smile_id_orchestrated_screen_host";import{resolveInstructionsConfig as f}from"../../screen/helpers/instructions_screen_helpers";import{useUseSmileIDLocalizations as p}from"../../../l10n/use_smile_id_localizations_provider";import{getCameraPermissionStatus as S,requestCameraPermission as w}from"../../../camera/camera_permissions";export function OrchestratedInstructionsScreen({configuration:t}){const r=p();return s(h,{content:o=>s(C,{renderProps:o,configuration:t,localizations:r})})}function C({renderProps:{navigationManager:t,onBack:r,onNavigateNext:o},configuration:n,localizations:u}){const l=a(async()=>{if(await S()==="granted"){o();return}await w()==="granted"&&o()},[o]),i=t.documentType,e=f(n,{enhancedLivenessEnabled:t.enhancedLivenessEnabled,...i?{documentType:i}:{}},u);return s(d,{titlePrefix:e.titlePrefix,titleEmphasis:e.titleEmphasis,heroSource:e.heroSource,guidelinesSectionTitle:e.guidelinesSectionTitle,guidelines:e.guidelines,showAttribution:m(t.configuration),showHeroOval:e.showHeroOval,showHeroOvalProgress:e.showHeroOvalProgress,showCardBackground:e.showCardBackground,...n.continueButton?{continueButton:({onClick:c})=>n.continueButton?.({onClick:c,enabled:!0})}:{},onContinue:l,onBack:r})}
1
+ "use strict";import{jsx as n}from"react/jsx-runtime";import{useCallback as a}from"react";import{flowShowAttribution as m}from"../config/attribution_resolver";import{UseSmileIDInstructionsScreen as d}from"../../screen/use_smile_id_instructions_screen";import{UseSmileIDOrchestratedScreenHost as f}from"./use_smile_id_orchestrated_screen_host";import{resolveInstructionsConfig as h}from"../../screen/helpers/instructions_screen_helpers";import{alwaysEnabledSlot as p}from"./helpers/always_enabled_slot";import{useUseSmileIDLocalizations as S}from"../../../l10n/use_smile_id_localizations_provider";import{getCameraPermissionStatus as w,requestCameraPermission as C}from"../../../camera/camera_permissions";export function OrchestratedInstructionsScreen({configuration:o}){const r=S();return n(f,{content:t=>n(P,{renderProps:t,configuration:o,localizations:r})})}function P({renderProps:{navigationManager:o,onBack:r,onNavigateNext:t},configuration:s,localizations:u}){const l=a(async()=>{if(await w()==="granted"){t();return}await C()==="granted"&&t()},[t]),i=o.documentType,e=h(s,{enhancedLivenessEnabled:o.enhancedLivenessEnabled,...i?{documentType:i}:{}},u),c=p(s.continueButton);return n(d,{titlePrefix:e.titlePrefix,titleEmphasis:e.titleEmphasis,heroSource:e.heroSource,guidelinesSectionTitle:e.guidelinesSectionTitle,guidelines:e.guidelines,showAttribution:m(o.configuration),showHeroOval:e.showHeroOval,showHeroOvalProgress:e.showHeroOvalProgress,showCardBackground:e.showCardBackground,...c!==void 0?{continueButton:c}:{},onContinue:l,onBack:r})}
@@ -1 +1 @@
1
- "use strict";import{jsx as r}from"react/jsx-runtime";import{flowShowAttribution as s}from"../config/attribution_resolver";import{UseSmileIDPreviewScreen as i}from"../../screen/use_smile_id_preview_screen";import{UseSmileIDDocumentPreviewScreen as l}from"../../screen/use_smile_id_document_preview_screen";import{UseSmileIDOrchestratedScreenHost as p}from"./use_smile_id_orchestrated_screen_host";import{defaultFrameAspectRatio as d}from"../../../core/models/document_type";export function OrchestratedPreviewScreen({configuration:t}){return r(p,{content:({navigationManager:e,onBack:o,onNavigateNext:c})=>{const m=s(e.configuration);if(e.currentPreviewIsDocument()){const u=e.currentDocumentPreviewPath();return r(l,{documentAspectRatio:e.documentFrameAspectRatio??d("Landscape"),showAttribution:m,...u!==void 0?{capturedDocumentPath:u}:{},onAccept:c,onReject:()=>{t.allowRetake&&(e.retakeCurrentDocumentSide(),o())},onClose:()=>e.exit()})}return r(i,{allowRetake:t.allowRetake,showAttribution:m,...e.captured.selfiePath!==void 0?{capturedSelfiePath:e.captured.selfiePath}:{},onAccept:c,onReject:()=>{t.allowRetake&&o()}})}})}
1
+ "use strict";import{jsx as r}from"react/jsx-runtime";import{flowShowAttribution as s}from"../config/attribution_resolver";import{UseSmileIDPreviewScreen as i}from"../../screen/use_smile_id_preview_screen";import{UseSmileIDDocumentPreviewScreen as d}from"../../screen/use_smile_id_document_preview_screen";import{UseSmileIDOrchestratedScreenHost as p}from"./use_smile_id_orchestrated_screen_host";import{defaultFrameAspectRatio as l}from"../../../core/models/document_type";export function OrchestratedPreviewScreen({configuration:t}){return r(p,{content:({navigationManager:e,onBack:o,onNavigateNext:c})=>{const u=s(e.configuration);if(e.currentPreviewIsDocument()){const m=e.currentDocumentPreviewPath();return r(d,{documentAspectRatio:e.documentFrameAspectRatio??l("Landscape"),showAttribution:u,...m!==void 0?{capturedDocumentPath:m,capturedDocumentBorderFraction:e.currentDocumentPreviewBorderFraction()}:{},onAccept:c,onReject:()=>{t.allowRetake&&(e.retakeCurrentDocumentSide(),o())},onClose:()=>e.exit()})}return r(i,{allowRetake:t.allowRetake,showAttribution:u,...e.captured.selfiePath!==void 0?{capturedSelfiePath:e.captured.selfiePath}:{},onAccept:c,onReject:()=>{t.allowRetake&&o()}})}})}
@@ -1 +1 @@
1
- "use strict";import{jsx as i}from"react/jsx-runtime";import{useEffect as a}from"react";import{UseSmileIDProcessingScreen as u}from"../../screen/use_smile_id_processing_screen";import{flowShowAttribution as f}from"../config/attribution_resolver";import{UseSmileIDOrchestratedScreenHost as m}from"./use_smile_id_orchestrated_screen_host";import{ProcessingCancelledException as d}from"../../../core/exception";import{resolveFailureMessage as g}from"./helpers/failure_message";import{useUseSmileIDLocalizations as l}from"../../../l10n/use_smile_id_localizations_provider";import{JobType as p,requiresDocument as s,requiresSelfie as S}from"../../../core/models/job_type";export function OrchestratedProcessingScreen({configuration:n}){return i(m,{content:({navigationManager:e,onNavigateNext:o})=>i(P,{processingConfig:n,navigationManager:e,onContinue:o})})}function P(n){const{navigationManager:e}=n;a(()=>{e.beginSubmissionIfNeeded()},[e]);const o=e.configuration.network?.config?.jobType??p.unknown,r=s(o)?e.captured.frontDocumentPath:void 0,t=e.documentFrameAspectRatio,c=l();return i(u,{state:h(e.jobSubmissionState,c),showFaceOval:S(o)||s(o),showProgressPercentage:n.processingConfig.showProgressPercentage,showAttribution:f(e.configuration),...r!==void 0?{documentImageUri:r}:{},...r!==void 0&&t!==void 0?{documentAspectRatio:t}:{},...e.captured.selfiePath!==void 0?{capturedSelfiePath:e.captured.selfiePath}:{},onContinue:n.onContinue,onRetry:()=>e.retrySubmission(),onExit:()=>e.exit({kind:"failed",error:b(e.jobSubmissionState)})})}function b(n){return n.kind==="failure"?n.error:new d}function h(n,e){switch(n.kind){case"notAttempted":case"loading":return{kind:"loading"};case"success":return{kind:"success",message:n.response.message};case"failure":return{kind:"failed",errorMessage:g(n.error,e)}}}
1
+ "use strict";import{jsx as r}from"react/jsx-runtime";import{useEffect as a}from"react";import{DocumentSide as d}from"@smileid/usesmileid_bridge";import{UseSmileIDProcessingScreen as m}from"../../screen/use_smile_id_processing_screen";import{flowShowAttribution as f}from"../config/attribution_resolver";import{UseSmileIDOrchestratedScreenHost as g}from"./use_smile_id_orchestrated_screen_host";import{ProcessingCancelledException as l}from"../../../core/exception";import{alwaysEnabledSlot as i}from"./helpers/always_enabled_slot";import{resolveFailureMessage as p}from"./helpers/failure_message";import{useUseSmileIDLocalizations as S}from"../../../l10n/use_smile_id_localizations_provider";import{JobType as b,requiresDocument as c,requiresSelfie as P}from"../../../core/models/job_type";export function OrchestratedProcessingScreen({configuration:n}){return r(g,{content:({navigationManager:e,onNavigateNext:o})=>r(h,{processingConfig:n,navigationManager:e,onContinue:o})})}function h(n){const{navigationManager:e}=n;a(()=>{e.beginSubmissionIfNeeded()},[e]);const o=e.configuration.network?.config?.jobType??b.unknown,t=c(o)?e.captured.frontDocumentPath:void 0,s=e.documentFrameAspectRatio,u=S();return r(m,{state:x(e.jobSubmissionState,u),showFaceOval:P(o)||c(o),showProgressPercentage:n.processingConfig.showProgressPercentage,showAttribution:f(e.configuration),...t!==void 0?{documentImageUri:t,documentBorderFraction:e.documentBorderFraction(d.Front)}:{},...t!==void 0&&s!==void 0?{documentAspectRatio:s}:{},...e.captured.selfiePath!==void 0?{capturedSelfiePath:e.captured.selfiePath}:{},...w(n.processingConfig),onContinue:n.onContinue,onRetry:()=>e.retrySubmission(),onExit:()=>e.exit({kind:"failed",error:B(e.jobSubmissionState)})})}function w(n){const e=i(n.continueButton),o=i(n.retryButton),t=i(n.exitButton);return{...e!==void 0?{continueButton:e}:{},...o!==void 0?{retryButton:o}:{},...t!==void 0?{exitButton:t}:{}}}function B(n){return n.kind==="failure"?n.error:new l}function x(n,e){switch(n.kind){case"notAttempted":case"loading":return{kind:"loading"};case"success":return{kind:"success",message:n.response.message};case"failure":return{kind:"failed",errorMessage:p(n.error,e)}}}
@@ -1,8 +1,8 @@
1
1
  import type { MLConfig } from "../../../core/models/ml_config";
2
2
  import type { NetworkConfig } from "../../../core/models/network_config";
3
- import type { BiometricKYCParams, ConsentInformation, DocumentVerificationParams, EnhancedDocumentVerificationParams, EnhancedKYCParams, UserDetails } from "../../../core/models/params";
3
+ import type { BiometricKYCParams, ConsentInformation, DocumentVerificationParams, EnhancedDocumentVerificationParams, EnhancedKYCParams, ResidencyDocumentVerificationParams, UserDetails } from "../../../core/models/params";
4
4
  import type { ScreenSpec } from "../dsl/builder/screens_builder";
5
- import type { TokenPayload } from "../../../data/model/token_payload";
5
+ import { type TokenPayload } from "../../../data/model/token_payload";
6
6
  import { type ValidationIssue } from "./validation_state";
7
7
  /**
8
8
  * Pure flow validator — returns typed `UseSmileIDValidationException`
@@ -35,6 +35,7 @@ type FlowValidatorInput = {
35
35
  readonly enhancedKYCParams?: EnhancedKYCParams | undefined;
36
36
  readonly biometricKYCParams?: BiometricKYCParams | undefined;
37
37
  readonly documentVerificationParams?: DocumentVerificationParams | undefined;
38
+ readonly residencyDocumentVerificationParams?: ResidencyDocumentVerificationParams | undefined;
38
39
  readonly enhancedDocumentVerificationParams?: EnhancedDocumentVerificationParams | undefined;
39
40
  readonly userDetails?: UserDetails | undefined;
40
41
  /**
@@ -44,12 +45,7 @@ type FlowValidatorInput = {
44
45
  */
45
46
  readonly tokenPayload?: TokenPayload | undefined;
46
47
  };
47
- /**
48
- * Quick triage of the in-progress builder state. Today the only rule
49
- * surfaced is the empty-screens-block error — `b.screens(...)` must
50
- * register at least one screen before the builder can produce a flow.
51
- * `b.validate()` calls this; the full rule set runs inside `build()`.
52
- */
48
+ /** `b.validate()`'s pre-flight: screens and their own settings, not job-level rules. */
53
49
  export declare function runBuilderValidation(steps: readonly ScreenSpec[]): ValidationIssue[];
54
50
  /**
55
51
  * Run every flow-level validation rule and return the collected
@@ -65,23 +61,28 @@ export declare function runFlowValidation(input: FlowValidatorInput): Validation
65
61
  * `b.validateEnhancedKYCParams(params)` so partners can pre-flight a
66
62
  * payload before assigning it to the builder.
67
63
  */
68
- export declare function runEnhancedKYCParamsValidation(params: EnhancedKYCParams): ValidationIssue[];
64
+ export declare function runEnhancedKYCParamsValidation(params: EnhancedKYCParams, tokenPayload?: TokenPayload): ValidationIssue[];
69
65
  /**
70
66
  * Validate a `BiometricKYCParams` payload in isolation. Used by
71
67
  * `b.validateBiometricKYCParams(params)`.
72
68
  */
73
- export declare function runBiometricKYCParamsValidation(params: BiometricKYCParams): ValidationIssue[];
69
+ export declare function runBiometricKYCParamsValidation(params: BiometricKYCParams, tokenPayload?: TokenPayload): ValidationIssue[];
74
70
  /**
75
71
  * Validate a `DocumentVerificationParams` payload in isolation. `country`
76
72
  * must be non-blank; `idType` is optional, but a present-but-blank value
77
73
  * is rejected.
78
74
  */
79
- export declare function runDocumentVerificationParamsValidation(params: DocumentVerificationParams): ValidationIssue[];
75
+ export declare function runDocumentVerificationParamsValidation(params: DocumentVerificationParams, tokenPayload?: TokenPayload): ValidationIssue[];
80
76
  /**
81
77
  * Validate an `EnhancedDocumentVerificationParams` payload in isolation.
82
78
  * Both `country` and `idType` are required and must be non-blank.
83
79
  */
84
- export declare function runEnhancedDocumentVerificationParamsValidation(params: EnhancedDocumentVerificationParams): ValidationIssue[];
80
+ export declare function runEnhancedDocumentVerificationParamsValidation(params: EnhancedDocumentVerificationParams, tokenPayload?: TokenPayload): ValidationIssue[];
81
+ /**
82
+ * Validate a `ResidencyDocumentVerificationParams` payload in isolation.
83
+ * Both `country` and `idType` are required and must be non-blank.
84
+ */
85
+ export declare function runResidencyDocumentVerificationParamsValidation(params: ResidencyDocumentVerificationParams, tokenPayload?: TokenPayload): ValidationIssue[];
85
86
  /**
86
87
  * Validate a `ConsentInformation` payload in isolation. Used by
87
88
  * `b.validateConsent(consent)` so partners can pre-flight a payload
@@ -1 +1 @@
1
- "use strict";import{CaptureType as d}from"../../../core/models/ml_config";import{JobType as h,jobTypeFromValue as y}from"../../../core/models/job_type";import{InvalidConsentConfigException as s,InvalidConsentInformationException as m}from"../../../core/exception/consent_validation_exceptions";import{InvalidFieldValueException as a,MissingJobTypeException as g,MissingMLConfigurationBlockException as w,MissingNetworkConfigurationBlockException as b}from"../../../core/exception/job_validation_exceptions";import{AgentModeWithEnhancedLivenessException as C,InvalidDocumentCaptureConfigException as c,InvalidMissingCaptureTypeException as P}from"../../../core/exception/capture_validation_exceptions";import{EmptyScreensBlockException as p}from"../../../core/exception/screen_config_validation_exceptions";import{collectJobTypeIssues as k}from"./job_type_validator";import{missingUserDetailsFieldIssues as I}from"./user_details_validator";function x(e){return e?.allowAgentMode&&e.enableEnhancedLiveness?[new C]:[]}function T(e){return e===void 0?[new c({reason:"document is required when mode is Document"})]:e.documentType===void 0?[new c({reason:"documentType is required for document capture"})]:e.knownIdAspectRatio!==void 0&&e.knownIdAspectRatio<=0?[new c({reason:"knownIdAspectRatio must be > 0 if provided"})]:[]}export function runBuilderValidation(e){const n=[];return e.length===0&&n.push(new p),n}export function runFlowValidation(e){const n=[];if(e.steps.length===0)return n.push(new p),n;for(const o of e.steps)o.kind==="capture"&&(o.config.captureType===void 0&&n.push(new P),o.config.captureType===d.selfie&&n.push(...x(o.config.selfieConfig)),o.config.captureType===d.document&&n.push(...T(o.config.documentConfig)));const i=e.steps.some(o=>o.kind==="capture"),t=e.steps.some(o=>o.kind==="processing"),u=e.network?.config?.jobType,f=u===void 0||y(u)===h.unknown;i&&!e.ml&&f&&n.push(new w),t&&!e.network&&n.push(new b),t&&f&&n.push(new g);const r=e.steps.find(o=>o.kind==="consent");return r&&(!r.config.partnerName||!r.config.partnerName.trim())&&n.push(new s({reason:"partnerName cannot be empty"})),r&&r.config.partnerIcon===void 0&&n.push(new s({reason:"partnerIcon is required for the consent screen"})),r&&(r.config.partnerPrivacyPolicyUrl===void 0||!r.config.partnerPrivacyPolicyUrl.trim())&&n.push(new s({reason:"partnerPrivacyPolicyUrl is required for the consent screen"})),r!==void 0&&e.consentInformation!==void 0&&n.push(new s({reason:"Exactly one form of consent is required \u2014 remove the consent { } screen if using consentInformation, or clear consentInformation if using the consent screen"})),e.consentInformation&&e.tokenPayload?.consent===void 0&&n.push(...runConsentValidation(e.consentInformation)),e.enhancedKYCParams&&n.push(...runEnhancedKYCParamsValidation(e.enhancedKYCParams)),e.biometricKYCParams&&n.push(...runBiometricKYCParamsValidation(e.biometricKYCParams)),e.documentVerificationParams&&n.push(...runDocumentVerificationParamsValidation(e.documentVerificationParams)),e.enhancedDocumentVerificationParams&&n.push(...runEnhancedDocumentVerificationParamsValidation(e.enhancedDocumentVerificationParams)),e.userDetails&&n.push(...runUserDetailsValidation(e.userDetails,e.tokenPayload)),k(e,n),n}export function runEnhancedKYCParamsValidation(e){return l(e)}export function runBiometricKYCParamsValidation(e){return l(e)}export function runDocumentVerificationParamsValidation(e){const n=[];return(!e.country||!e.country.trim())&&n.push(new a({fieldName:"country",reason:"must not be blank"})),e.idType!==void 0&&!e.idType.trim()&&n.push(new a({fieldName:"idType",reason:"must not be blank"})),n}export function runEnhancedDocumentVerificationParamsValidation(e){const n=[];for(const i of["country","idType"]){const t=e[i];(!t||!t.trim())&&n.push(new a({fieldName:i,reason:"must not be blank"}))}return n}export function runConsentValidation(e){const n=[];e.granted||n.push(new m({detail:"granted must be true"}));for(const i of["grantedAt","noticeLanguage","noticePrivacyPolicyUrl"])(!e[i]||!e[i].trim())&&n.push(new m({detail:`${i} must not be blank`}));return n}function l(e){const n=[];for(const i of["idType","idNumber","country"]){const t=e[i];(!t||!t.trim())&&n.push(new a({fieldName:i,reason:"must not be blank"}))}return n}export function runUserDetailsValidation(e,n){return I(e,n)}
1
+ "use strict";import{CaptureType as d}from"../../../core/models/ml_config";import{JobType as g,jobTypeFromValue as w}from"../../../core/models/job_type";import{InvalidConsentConfigException as c,InvalidConsentInformationException as m}from"../../../core/exception/consent_validation_exceptions";import{InvalidFieldValueException as s,MissingJobTypeException as P,MissingMLConfigurationBlockException as k,MissingNetworkConfigurationBlockException as b}from"../../../core/exception/job_validation_exceptions";import{AgentModeWithEnhancedLivenessException as C,InvalidDocumentCaptureConfigException as u,InvalidMissingCaptureTypeException as I}from"../../../core/exception/capture_validation_exceptions";import{EmptyScreensBlockException as l}from"../../../core/exception/screen_config_validation_exceptions";import{isIdFieldBoundByToken as a}from"../../../data/model/token_payload";import{collectJobTypeIssues as T}from"./job_type_validator";import{missingUserDetailsFieldIssues as V}from"./user_details_validator";function x(e){return e?.allowAgentMode&&e.enableEnhancedLiveness?[new C]:[]}function v(e){return e===void 0?[new u({reason:"document is required when mode is Document"})]:e.documentType===void 0?[new u({reason:"documentType is required for document capture"})]:e.knownIdAspectRatio!==void 0&&e.knownIdAspectRatio<=0?[new u({reason:"knownIdAspectRatio must be > 0 if provided"})]:[]}export function runBuilderValidation(e){return e.length===0?[new l]:[...p(e),...y(e)]}function p(e){const n=[];for(const o of e)o.kind==="capture"&&(o.config.captureType===void 0&&n.push(new I),o.config.captureType===d.selfie&&n.push(...x(o.config.selfieConfig)),o.config.captureType===d.document&&n.push(...v(o.config.documentConfig)));return n}function y(e){const n=[],o=e.find(i=>i.kind==="consent");return o&&(!o.config.partnerName||!o.config.partnerName.trim())&&n.push(new c({reason:"partnerName cannot be empty"})),o&&o.config.partnerIcon===void 0&&n.push(new c({reason:"partnerIcon is required for the consent screen"})),o&&(o.config.partnerPrivacyPolicyUrl===void 0||!o.config.partnerPrivacyPolicyUrl.trim())&&n.push(new c({reason:"partnerPrivacyPolicyUrl is required for the consent screen"})),n}export function runFlowValidation(e){const n=[];if(e.steps.length===0)return n.push(new l),n;n.push(...p(e.steps));const o=e.steps.some(t=>t.kind==="capture"),i=e.steps.some(t=>t.kind==="processing"),r=e.network?.config?.jobType,f=r===void 0||w(r)===g.unknown;return o&&!e.ml&&f&&n.push(new k),i&&!e.network&&n.push(new b),i&&f&&n.push(new P),n.push(...y(e.steps)),e.steps.some(t=>t.kind==="consent")&&e.consentInformation!==void 0&&n.push(new c({reason:"Exactly one form of consent is required \u2014 remove the consent { } screen if using consentInformation, or clear consentInformation if using the consent screen"})),e.consentInformation&&e.tokenPayload?.consent===void 0&&n.push(...runConsentValidation(e.consentInformation)),e.enhancedKYCParams&&n.push(...runEnhancedKYCParamsValidation(e.enhancedKYCParams,e.tokenPayload)),e.biometricKYCParams&&n.push(...runBiometricKYCParamsValidation(e.biometricKYCParams,e.tokenPayload)),e.documentVerificationParams&&n.push(...runDocumentVerificationParamsValidation(e.documentVerificationParams,e.tokenPayload)),e.enhancedDocumentVerificationParams&&n.push(...runEnhancedDocumentVerificationParamsValidation(e.enhancedDocumentVerificationParams,e.tokenPayload)),e.residencyDocumentVerificationParams&&n.push(...runResidencyDocumentVerificationParamsValidation(e.residencyDocumentVerificationParams,e.tokenPayload)),e.userDetails&&n.push(...runUserDetailsValidation(e.userDetails,e.tokenPayload)),T(e,n),n}export function runEnhancedKYCParamsValidation(e,n){return h(e,n)}export function runBiometricKYCParamsValidation(e,n){return h(e,n)}export function runDocumentVerificationParamsValidation(e,n){const o=[];return!a(n,"country")&&!e.country?.trim()&&o.push(new s({fieldName:"country",reason:"must not be blank"})),!a(n,"idType")&&e.idType!==void 0&&!e.idType.trim()&&o.push(new s({fieldName:"idType",reason:"must not be blank"})),o}export function runEnhancedDocumentVerificationParamsValidation(e,n){const o=[];for(const i of["country","idType"]){const r=e[i];!a(n,i)&&!r?.trim()&&o.push(new s({fieldName:i,reason:"must not be blank"}))}return o}export function runResidencyDocumentVerificationParamsValidation(e,n){const o=[];for(const i of["country","idType"]){const r=e[i];!a(n,i)&&!r?.trim()&&o.push(new s({fieldName:i,reason:"must not be blank"}))}return o}export function runConsentValidation(e){const n=[];e.granted||n.push(new m({detail:"granted must be true"}));for(const o of["grantedAt","noticeLanguage","noticePrivacyPolicyUrl"])(!e[o]||!e[o].trim())&&n.push(new m({detail:`${o} must not be blank`}));return n}function h(e,n){const o=[];for(const i of["idType","idNumber","country"]){const r=e[i];!a(n,i)&&!r?.trim()&&o.push(new s({fieldName:i,reason:"must not be blank"}))}return o}export function runUserDetailsValidation(e,n){return V(e,n)}
@@ -1,8 +1,9 @@
1
1
  import type { MLConfig } from "../../../core/models/ml_config";
2
2
  import type { NetworkConfig } from "../../../core/models/network_config";
3
- import type { BiometricKYCParams, ConsentInformation, DocumentVerificationParams, EnhancedDocumentVerificationParams, EnhancedKYCParams, UserDetails } from "../../../core/models/params";
3
+ import type { BiometricKYCParams, ConsentInformation, DocumentVerificationParams, EnhancedDocumentVerificationParams, EnhancedKYCParams, ResidencyDocumentVerificationParams, UserDetails } from "../../../core/models/params";
4
4
  import { JobType } from "../../../core/models/job_type";
5
5
  import type { ScreenSpec } from "../dsl/builder/screens_builder";
6
+ import { InvalidJobTypeScreenConfigException } from "../../../core/exception/job_validation_exceptions";
6
7
  import { type TokenPayload } from "../../../data/model/token_payload";
7
8
  import type { ValidationIssue } from "./validation_state";
8
9
  /**
@@ -46,6 +47,7 @@ export type JobTypeValidatorInput = {
46
47
  readonly enhancedKYCParams?: EnhancedKYCParams | undefined;
47
48
  readonly biometricKYCParams?: BiometricKYCParams | undefined;
48
49
  readonly documentVerificationParams?: DocumentVerificationParams | undefined;
50
+ readonly residencyDocumentVerificationParams?: ResidencyDocumentVerificationParams | undefined;
49
51
  readonly enhancedDocumentVerificationParams?: EnhancedDocumentVerificationParams | undefined;
50
52
  readonly userDetails?: UserDetails | undefined;
51
53
  /**
@@ -131,3 +133,9 @@ export declare function resolveJobTypeValidator(jobType: JobType | undefined): J
131
133
  * generic structural validator as the fallback for unknown job types.
132
134
  */
133
135
  export declare function collectJobTypeIssues(input: JobTypeValidatorInput, issues: ValidationIssue[]): void;
136
+ /**
137
+ * The issue for a Residency Document Verification `idType` other than
138
+ * {@link RESIDENCY_ID_TYPE}. One builder, so the flow validation and the
139
+ * standalone params pre-check report it identically.
140
+ */
141
+ export declare function residencyIdTypeIssue(): InvalidJobTypeScreenConfigException;
@@ -1 +1 @@
1
- "use strict";import{JobType as i}from"../../../core/models/job_type";import{CaptureType as h}from"../dsl/builder/screens_builder";import{InvalidJobTypeScreenConfigException as a}from"../../../core/exception/job_validation_exceptions";import{JOB_PARAMS_OWNERS as S,UnusedJobParamsException as x}from"../../../core/exception/unused_job_params_exception";import{DuplicateScreenTypeException as k,InvalidScreenOrderException as d,MissingCaptureForPreviewException as E}from"../../../core/exception/screen_config_validation_exceptions";import{IssueSeverity as p}from"../../../core/exception/issue_severity";import{tokenConsentMissingSubfields as P,bindsRequiredUserDetails as D}from"../../../data/model/token_payload";import{missingUserDetailsFieldIssues as V}from"./user_details_validator";const I=new Map([[i.biometricKyc,"BiometricKyc"],[i.smartSelfieAuthentication,"SmartSelfieAuthentication"],[i.smartSelfieEnrollment,"SmartSelfieEnrollment"],[i.enhancedKyc,"EnhancedKyc"],[i.documentVerification,"DocumentVerification"],[i.bvn,"BVN"],[i.enhancedDocumentVerification,"EnhancedDocumentVerification"],[i.unknown,"Unknown"]]);function m(o){return I.get(o)??"Unknown"}function y(o,e,n){const t=e.steps.some(s=>s.kind==="consent"),r=e.consentInformation!==void 0,c=e.tokenPayload?.consent;if(c!==void 0){const s=P(c);s.length>0&&n.push(a.forJob({jobType:o,reason:`the token's consent binding is incomplete \u2014 missing: ${s.join(", ")}`,suggestedFix:"Bind all four consent fields when minting the token at /v3/token, or bind none and collect consent in the app"}));return}!t&&!r&&n.push(a.forJob({jobType:o,reason:"must include either a Consent screen or a pre-supplied consentInformation",suggestedFix:"Add a consent { } screen to your flow, or supply consentInformation via the builder"}))}export const JOB_PARAMS_SUPPLIERS={enhancedKYCParams:o=>o.enhancedKYCParams,biometricKYCParams:o=>o.biometricKYCParams,documentVerificationParams:o=>o.documentVerificationParams,enhancedDocumentVerificationParams:o=>o.enhancedDocumentVerificationParams};function b(o,e,n){for(const{fieldName:t,owner:r}of S)r!==o&&JOB_PARAMS_SUPPLIERS[t]?.(e)!==void 0&&n.push(new x({jobType:o,fieldName:t}))}function T(o,e,n){if(e.userDetails===void 0&&!D(e.tokenPayload)){if(e.tokenPayload===void 0){n.push(a.forJob({jobType:o,reason:`userDetails is required for ${m(o)} jobs`,suggestedFix:"Set builder.userDetails with the end user PII before building"}));return}n.push(...V(void 0,e.tokenPayload))}}function f(o,e){const n=new Map;o.forEach((t,r)=>{if(t.kind==="capture"||t.kind==="preview")return;const c=n.get(t.kind)??[];c.push(r),n.set(t.kind,c)});for(const[t,r]of n)r.length>1&&e.push(new k({screenType:t,indices:r}))}function u(o,e){o.findIndex(c=>c.kind==="consent")>0&&e.push(new d({description:"Consent screen must be the first screen in the flow",severity:p.error}));const t=o.findIndex(c=>c.kind==="processing");t!==-1&&t!==o.length-1&&e.push(new d({description:"Processing screen must be the last screen in the flow",severity:p.error}));let r=!1;o.forEach((c,s)=>{c.kind==="capture"?r=!0:c.kind==="instructions"&&r&&e.push(new d({description:`Instructions screen at index ${s} must appear before the Capture screen`,severity:p.error}))})}function l(o,e,n){let t=!1,r=0;o.forEach((c,s)=>{c.kind==="capture"?t=!0:c.kind==="preview"&&(r+=1,t?r>e&&n.push(new d({description:`Preview screen at index ${s} exceeds the maximum of ${e} preview screen(s) allowed for this flow`,severity:p.error})):n.push(new E({previewIndex:s})))})}function g(o,e,n){e.steps.some(r=>r.kind==="processing")||n.push(a.forJob({jobType:o,reason:"must include a Processing screen",suggestedFix:"Add a processing { } screen as the last screen in the flow"}))}function j(o,e,n){const t=e.steps.filter(r=>r.kind==="capture"&&r.config.captureType===h.selfie);t.length===0?n.push(a.forJob({jobType:o,reason:"must include exactly one Capture screen with captureType = SELFIE",suggestedFix:"Add a capture { captureType = CaptureType.SELFIE } screen to your flow"})):t.length>1&&n.push(a.forJob({jobType:o,reason:`must include exactly one Capture screen with captureType = SELFIE, but found ${t.length}`,suggestedFix:"Remove the extra selfie capture screens \u2014 only one is allowed"}))}function w(o,e,n,t){e.ml?e.ml.analyzers.selfie||t.push(a.forJob({jobType:o,reason:"ml { } block must register analyzers for CaptureType.SELFIE",suggestedFix:"Add forCaptureType(CaptureType.SELFIE) { } inside the ml { } block"})):t.push(a.forJob({jobType:o,reason:"ml { } block is required and must register analyzers for CaptureType.SELFIE",suggestedFix:n}))}export class SelfieJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){f(e.steps,n),u(e.steps,n),l(e.steps,1,n),y(this.jobType,e,n),b(this.jobType,e,n),j(this.jobType,e,n),e.steps.some(r=>r.kind==="capture"&&r.config.captureType===h.document)&&n.push(a.forJob({jobType:this.jobType,reason:"must not include a Document capture screen",suggestedFix:`Remove the Document capture screen \u2014 ${m(this.jobType)} requires selfie capture only`})),g(this.jobType,e,n),w(this.jobType,e,"Add the ml { } block with forCaptureType(CaptureType.SELFIE) { } inside",n),e.ml?.analyzers.document&&n.push(a.forJob({jobType:this.jobType,reason:"ml { } block must not register analyzers for CaptureType.DOCUMENT",suggestedFix:`Remove forCaptureType(CaptureType.DOCUMENT) { } from the ml { } block \u2014 ${m(this.jobType)} does not use document capture`})),this.jobType===i.biometricKyc&&e.biometricKYCParams===void 0&&n.push(a.forJob({jobType:this.jobType,reason:"biometricKYCParams is required for BiometricKyc jobs",suggestedFix:"Set builder.biometricKYCParams with the identity fields before building"})),this.jobType===i.smartSelfieAuthentication&&(e.userId===void 0||e.userId.trim()==="")&&n.push(a.forJob({jobType:this.jobType,reason:"userId is required for SmartSelfieAuthentication jobs",suggestedFix:"Set builder.userId with the identifier returned by a prior Smart Selfie Enrollment before building"})),T(this.jobType,e,n)}}export class DocumentJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){f(e.steps,n),u(e.steps,n),l(e.steps,2,n),y(this.jobType,e,n),b(this.jobType,e,n),j(this.jobType,e,n);const t=e.steps.filter(r=>r.kind==="capture"&&r.config.captureType===h.document);t.length===0?n.push(a.forJob({jobType:this.jobType,reason:"must include exactly one Capture screen with captureType = DOCUMENT",suggestedFix:"Add a capture { captureType = CaptureType.DOCUMENT } screen to your flow"})):t.length>1&&n.push(a.forJob({jobType:this.jobType,reason:`must include exactly one Capture screen with captureType = DOCUMENT, but found ${t.length}`,suggestedFix:"Remove the extra document capture screens \u2014 only one is allowed"})),g(this.jobType,e,n),w(this.jobType,e,"Add the ml { } block with forCaptureType(CaptureType.SELFIE) { } inside \u2014 document capture uses NoopDocumentDetectorAnalyzer by default",n),this.appendParamsRule(e,n),this.jobType===i.enhancedDocumentVerification&&t.some(r=>r.kind==="capture"&&r.config.documentConfig?.documentType?.kind==="southAfricaGreenBook")&&n.push(a.forJob({jobType:this.jobType,reason:"South Africa Green Book is not supported for Enhanced Document Verification",suggestedFix:"Use JobType.documentVerification for the Green Book, or choose a supported document type for Enhanced Document Verification"})),T(this.jobType,e,n)}appendParamsRule(e,n){this.jobType===i.documentVerification&&e.documentVerificationParams===void 0&&n.push(a.forJob({jobType:this.jobType,reason:"documentVerificationParams is required for DocumentVerification jobs",suggestedFix:"Set builder.documentVerificationParams with the country (and optional idType) before building"})),this.jobType===i.enhancedDocumentVerification&&e.enhancedDocumentVerificationParams===void 0&&n.push(a.forJob({jobType:this.jobType,reason:"enhancedDocumentVerificationParams is required for EnhancedDocumentVerification jobs",suggestedFix:"Set builder.enhancedDocumentVerificationParams with the country and idType before building"}))}}export class NonCaptureJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){f(e.steps,n),u(e.steps,n),l(e.steps,0,n),y(this.jobType,e,n),b(this.jobType,e,n),g(this.jobType,e,n);const t=e.steps.filter(r=>r.kind!=="consent"&&r.kind!=="processing");if(t.length>0){const r=[...new Set(t.map(c=>c.kind))].join(", ");n.push(a.forJob({jobType:this.jobType,reason:`must only contain consent and processing screens, but found: ${r}`,suggestedFix:"Remove all screens except consent { } and processing { } from your flow"}))}this.jobType===i.enhancedKyc&&e.enhancedKYCParams===void 0&&n.push(a.forJob({jobType:this.jobType,reason:"enhancedKYCParams is required for EnhancedKyc jobs",suggestedFix:"Set builder.enhancedKYCParams with the identity fields before building"})),this.jobType===i.enhancedKyc&&T(this.jobType,e,n)}}export class DefaultStructuralValidator{validate(e,n){f(e.steps,n),u(e.steps,n),l(e.steps,1,n)}}export const jobTypeValidators=new Map([[i.biometricKyc,new SelfieJobTypeValidator(i.biometricKyc)],[i.smartSelfieAuthentication,new SelfieJobTypeValidator(i.smartSelfieAuthentication)],[i.smartSelfieEnrollment,new SelfieJobTypeValidator(i.smartSelfieEnrollment)],[i.documentVerification,new DocumentJobTypeValidator(i.documentVerification)],[i.enhancedDocumentVerification,new DocumentJobTypeValidator(i.enhancedDocumentVerification)],[i.enhancedKyc,new NonCaptureJobTypeValidator(i.enhancedKyc)],[i.bvn,new NonCaptureJobTypeValidator(i.bvn)]]);const C=new DefaultStructuralValidator;export function resolveJobTypeValidator(o){return o===void 0||o===i.unknown?C:jobTypeValidators.get(o)??C}export function collectJobTypeIssues(o,e){const n=o.network?.config?.jobType;resolveJobTypeValidator(n).validate(o,e)}
1
+ "use strict";import{RESIDENCY_ID_TYPE as l}from"../../../core/models/params";import{JobType as t}from"../../../core/models/job_type";import{CaptureType as h}from"../dsl/builder/screens_builder";import{InvalidJobTypeScreenConfigException as c}from"../../../core/exception/job_validation_exceptions";import{JOB_PARAMS_OWNERS as C,UnusedJobParamsException as D}from"../../../core/exception/unused_job_params_exception";import{DuplicateScreenTypeException as P,InvalidScreenOrderException as d,MissingCaptureForPreviewException as x}from"../../../core/exception/screen_config_validation_exceptions";import{IssueSeverity as p}from"../../../core/exception/issue_severity";import{tokenConsentMissingSubfields as V,bindsRequiredUserDetails as E}from"../../../data/model/token_payload";import{missingUserDetailsFieldIssues as I}from"./user_details_validator";const J=new Map([[t.biometricKyc,"BiometricKyc"],[t.smartSelfieAuthentication,"SmartSelfieAuthentication"],[t.smartSelfieEnrollment,"SmartSelfieEnrollment"],[t.enhancedKyc,"EnhancedKyc"],[t.documentVerification,"DocumentVerification"],[t.bvn,"BVN"],[t.enhancedDocumentVerification,"EnhancedDocumentVerification"],[t.residencyDocumentVerification,"ResidencyDocumentVerification"],[t.unknown,"Unknown"]]);function y(o){return J.get(o)??"Unknown"}function b(o,e,n){const r=e.steps.some(s=>s.kind==="consent"),i=e.consentInformation!==void 0,a=e.tokenPayload?.consent;if(a!==void 0){const s=V(a);s.length>0&&n.push(c.forJob({jobType:o,reason:`the token's consent binding is incomplete \u2014 missing: ${s.join(", ")}`,suggestedFix:"Bind all four consent fields when minting the token at /v3/token, or bind none and collect consent in the app"}));return}!r&&!i&&n.push(c.forJob({jobType:o,reason:"must include either a Consent screen or a pre-supplied consentInformation",suggestedFix:"Add a consent { } screen to your flow, or supply consentInformation via the builder"}))}export const JOB_PARAMS_SUPPLIERS={enhancedKYCParams:o=>o.enhancedKYCParams,biometricKYCParams:o=>o.biometricKYCParams,documentVerificationParams:o=>o.documentVerificationParams,enhancedDocumentVerificationParams:o=>o.enhancedDocumentVerificationParams,residencyDocumentVerificationParams:o=>o.residencyDocumentVerificationParams};function T(o,e,n){for(const{fieldName:r,owner:i}of C)i!==o&&JOB_PARAMS_SUPPLIERS[r]?.(e)!==void 0&&n.push(new D({jobType:o,fieldName:r}))}function g(o,e,n){if(e.userDetails===void 0&&!E(e.tokenPayload)){if(e.tokenPayload===void 0){n.push(c.forJob({jobType:o,reason:`userDetails is required for ${y(o)} jobs`,suggestedFix:"Set builder.userDetails with the end user PII before building"}));return}n.push(...I(void 0,e.tokenPayload))}}function u(o,e){const n=new Map;o.forEach((r,i)=>{if(r.kind==="capture"||r.kind==="preview")return;const a=n.get(r.kind)??[];a.push(i),n.set(r.kind,a)});for(const[r,i]of n)i.length>1&&e.push(new P({screenType:r,indices:i}))}function f(o,e){o.findIndex(a=>a.kind==="consent")>0&&e.push(new d({description:"Consent screen must be the first screen in the flow",severity:p.error}));const r=o.findIndex(a=>a.kind==="processing");r!==-1&&r!==o.length-1&&e.push(new d({description:"Processing screen must be the last screen in the flow",severity:p.error}));let i=!1;o.forEach((a,s)=>{a.kind==="capture"?i=!0:a.kind==="instructions"&&i&&e.push(new d({description:`Instructions screen at index ${s} must appear before the Capture screen`,severity:p.error}))})}function m(o,e,n){let r=!1,i=0;o.forEach((a,s)=>{a.kind==="capture"?r=!0:a.kind==="preview"&&(i+=1,r?i>e&&n.push(new d({description:`Preview screen at index ${s} exceeds the maximum of ${e} preview screen(s) allowed for this flow`,severity:p.error})):n.push(new x({previewIndex:s})))})}function j(o,e,n){e.steps.some(i=>i.kind==="processing")||n.push(c.forJob({jobType:o,reason:"must include a Processing screen",suggestedFix:"Add a processing { } screen as the last screen in the flow"}))}function w(o,e,n){const r=e.steps.filter(i=>i.kind==="capture"&&i.config.captureType===h.selfie);r.length===0?n.push(c.forJob({jobType:o,reason:"must include exactly one Capture screen with captureType = SELFIE",suggestedFix:"Add a capture { captureType = CaptureType.SELFIE } screen to your flow"})):r.length>1&&n.push(c.forJob({jobType:o,reason:`must include exactly one Capture screen with captureType = SELFIE, but found ${r.length}`,suggestedFix:"Remove the extra selfie capture screens \u2014 only one is allowed"}))}function S(o,e,n,r){e.ml?e.ml.analyzers.selfie||r.push(c.forJob({jobType:o,reason:"ml { } block must register analyzers for CaptureType.SELFIE",suggestedFix:"Add forCaptureType(CaptureType.SELFIE) { } inside the ml { } block"})):r.push(c.forJob({jobType:o,reason:"ml { } block is required and must register analyzers for CaptureType.SELFIE",suggestedFix:n}))}export class SelfieJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){u(e.steps,n),f(e.steps,n),m(e.steps,1,n),b(this.jobType,e,n),T(this.jobType,e,n),w(this.jobType,e,n),e.steps.some(i=>i.kind==="capture"&&i.config.captureType===h.document)&&n.push(c.forJob({jobType:this.jobType,reason:"must not include a Document capture screen",suggestedFix:`Remove the Document capture screen \u2014 ${y(this.jobType)} requires selfie capture only`})),j(this.jobType,e,n),S(this.jobType,e,"Add the ml { } block with forCaptureType(CaptureType.SELFIE) { } inside",n),e.ml?.analyzers.document&&n.push(c.forJob({jobType:this.jobType,reason:"ml { } block must not register analyzers for CaptureType.DOCUMENT",suggestedFix:`Remove forCaptureType(CaptureType.DOCUMENT) { } from the ml { } block \u2014 ${y(this.jobType)} does not use document capture`})),this.jobType===t.biometricKyc&&e.biometricKYCParams===void 0&&n.push(c.forJob({jobType:this.jobType,reason:"biometricKYCParams is required for BiometricKyc jobs",suggestedFix:"Set builder.biometricKYCParams with the identity fields before building"})),this.jobType===t.smartSelfieAuthentication&&(e.userId===void 0||e.userId.trim()==="")&&n.push(c.forJob({jobType:this.jobType,reason:"userId is required for SmartSelfieAuthentication jobs",suggestedFix:"Set builder.userId with the identifier returned by a prior Smart Selfie Enrollment before building"})),g(this.jobType,e,n)}}export class DocumentJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){u(e.steps,n),f(e.steps,n),m(e.steps,2,n),b(this.jobType,e,n),T(this.jobType,e,n),w(this.jobType,e,n);const r=e.steps.filter(i=>i.kind==="capture"&&i.config.captureType===h.document);if(r.length===0?n.push(c.forJob({jobType:this.jobType,reason:"must include exactly one Capture screen with captureType = DOCUMENT",suggestedFix:"Add a capture { captureType = CaptureType.DOCUMENT } screen to your flow"})):r.length>1&&n.push(c.forJob({jobType:this.jobType,reason:`must include exactly one Capture screen with captureType = DOCUMENT, but found ${r.length}`,suggestedFix:"Remove the extra document capture screens \u2014 only one is allowed"})),j(this.jobType,e,n),S(this.jobType,e,"Add the ml { } block with forCaptureType(CaptureType.SELFIE) { } inside \u2014 document capture uses NoopDocumentDetectorAnalyzer by default",n),this.appendParamsRule(e,n),this.jobType===t.enhancedDocumentVerification&&r.some(i=>i.kind==="capture"&&i.config.documentConfig?.documentType?.kind==="southAfricaGreenBook")&&n.push(c.forJob({jobType:this.jobType,reason:"South Africa Green Book is not supported for Enhanced Document Verification",suggestedFix:"Use JobType.documentVerification for the Green Book, or choose a supported document type for Enhanced Document Verification"})),this.jobType===t.residencyDocumentVerification)for(const i of r){if(i.kind!=="capture")continue;const a=i.config.documentConfig,s=a?.documentType;s?.kind!=="passport"&&n.push(c.forJob({jobType:this.jobType,reason:`documentType must be passport \u2014 ${s?.kind??"no document type"} is not a residency primary document`,suggestedFix:"Set documentType to the passport preset on the document capture screen"})),a&&a.allowSkipBack===!0&&n.push(c.forJob({jobType:this.jobType,reason:"allowSkipBack must be false \u2014 the visa capture is mandatory",suggestedFix:"Remove allowSkipBack from the document capture screen"}))}g(this.jobType,e,n)}appendParamsRule(e,n){this.jobType===t.documentVerification&&e.documentVerificationParams===void 0&&n.push(c.forJob({jobType:this.jobType,reason:"documentVerificationParams is required for DocumentVerification jobs",suggestedFix:"Set builder.documentVerificationParams with the country (and optional idType) before building"})),this.jobType===t.enhancedDocumentVerification&&e.enhancedDocumentVerificationParams===void 0&&n.push(c.forJob({jobType:this.jobType,reason:"enhancedDocumentVerificationParams is required for EnhancedDocumentVerification jobs",suggestedFix:"Set builder.enhancedDocumentVerificationParams with the country and idType before building"})),this.jobType===t.residencyDocumentVerification&&e.tokenPayload?.hasIdType!==!0&&e.residencyDocumentVerificationParams!==void 0&&e.residencyDocumentVerificationParams.idType!==l&&n.push(residencyIdTypeIssue()),this.jobType===t.residencyDocumentVerification&&e.residencyDocumentVerificationParams===void 0&&n.push(c.forJob({jobType:this.jobType,reason:"residencyDocumentVerificationParams is required for ResidencyDocumentVerification jobs",suggestedFix:"Set builder.residencyDocumentVerificationParams with the country and idType before building"}))}}export class NonCaptureJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){u(e.steps,n),f(e.steps,n),m(e.steps,0,n),b(this.jobType,e,n),T(this.jobType,e,n),j(this.jobType,e,n);const r=e.steps.filter(i=>i.kind!=="consent"&&i.kind!=="processing");if(r.length>0){const i=[...new Set(r.map(a=>a.kind))].join(", ");n.push(c.forJob({jobType:this.jobType,reason:`must only contain consent and processing screens, but found: ${i}`,suggestedFix:"Remove all screens except consent { } and processing { } from your flow"}))}this.jobType===t.enhancedKyc&&e.enhancedKYCParams===void 0&&n.push(c.forJob({jobType:this.jobType,reason:"enhancedKYCParams is required for EnhancedKyc jobs",suggestedFix:"Set builder.enhancedKYCParams with the identity fields before building"})),this.jobType===t.enhancedKyc&&g(this.jobType,e,n)}}export class DefaultStructuralValidator{validate(e,n){u(e.steps,n),f(e.steps,n),m(e.steps,1,n)}}export const jobTypeValidators=new Map([[t.biometricKyc,new SelfieJobTypeValidator(t.biometricKyc)],[t.smartSelfieAuthentication,new SelfieJobTypeValidator(t.smartSelfieAuthentication)],[t.smartSelfieEnrollment,new SelfieJobTypeValidator(t.smartSelfieEnrollment)],[t.documentVerification,new DocumentJobTypeValidator(t.documentVerification)],[t.enhancedDocumentVerification,new DocumentJobTypeValidator(t.enhancedDocumentVerification)],[t.residencyDocumentVerification,new DocumentJobTypeValidator(t.residencyDocumentVerification)],[t.enhancedKyc,new NonCaptureJobTypeValidator(t.enhancedKyc)],[t.bvn,new NonCaptureJobTypeValidator(t.bvn)]]);const k=new DefaultStructuralValidator;export function resolveJobTypeValidator(o){return o===void 0||o===t.unknown?k:jobTypeValidators.get(o)??k}export function collectJobTypeIssues(o,e){const n=o.network?.config?.jobType;resolveJobTypeValidator(n).validate(o,e)}export function residencyIdTypeIssue(){return c.forJob({jobType:t.residencyDocumentVerification,reason:`idType must be "${l}" \u2014 the backend rejects any other value for this job type`,suggestedFix:`Set builder.residencyDocumentVerificationParams with idType: "${l}"`})}
@@ -3,6 +3,7 @@ import type { DocumentAnalyzer, FaceAnalyzer } from "@smileid/usesmileid_platfor
3
3
  import type { CaptureReadyEvent, UseSmileIDScanState } from "@smileid/usesmileid_bridge";
4
4
  import { DocumentSide } from "@smileid/usesmileid_bridge";
5
5
  import { CaptureType } from "../../core/models/ml_config";
6
+ import type { UseSmileIDAnalyticsEventKind } from "../flow/analytics/use_smile_id_analytics_event_kind";
6
7
  import type { DocumentCaptureMode } from "../../core/models/document_capture_mode";
7
8
  import type { DocumentType } from "../../core/models/document_type";
8
9
  import { type UseSmileIDTheme } from "../theme/theme";
@@ -35,12 +36,16 @@ export type UseSmileIDCaptureScreenProps = {
35
36
  * Fires when the state machine reaches `finished` and `flush()` has
36
37
  * settled, with every `CaptureReadyEvent` of the session (one
37
38
  * `"selfie"` + N `"liveness"` for face, one `"document"` for document).
39
+ * `documentBorderFraction` is the hidden border an auto-captured document was saved with, as a
40
+ * fraction of its short side; 0 for a selfie, a manual-shutter still or a gallery import.
38
41
  */
39
- readonly onCaptureComplete?: (events: readonly CaptureReadyEvent[], telemetry: CaptureTelemetry) => void;
42
+ readonly onCaptureComplete?: (events: readonly CaptureReadyEvent[], telemetry: CaptureTelemetry, documentBorderFraction?: number) => void;
40
43
  /** Records how a document side was produced (auto/manual/gallery), fired just before delivery. */
41
44
  readonly onDocumentOrigin?: (origin: DocumentImageOrigin) => void;
42
45
  /** Fires when the state machine reaches `"timeout"`; the screen stops streaming. */
43
46
  readonly onCaptureTimeout?: (state: UseSmileIDScanState) => void;
47
+ /** Receives `selfie_session_restarted`, as Android and iOS emit it. */
48
+ readonly onAnalyticsEvent?: (event: UseSmileIDAnalyticsEventKind) => void;
44
49
  /**
45
50
  * Fires on a terminal kamera error. On iOS a denied camera permission arrives only here (the
46
51
  * permission gate reports granted optimistically), so without it the user is stranded on a dead preview.