@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.
- package/CHANGELOG.md +9 -1
- package/README.md +28 -17
- package/dist/camera/document_image_pipeline.d.ts +1 -1
- package/dist/camera/document_image_pipeline.js +1 -1
- package/dist/camera/use_smile_id_frame_stream.js +1 -1
- package/dist/camera/use_smile_id_kamera_camera_system.d.ts +6 -0
- package/dist/camera/use_smile_id_kamera_camera_system.js +1 -1
- package/dist/core/exception/invalid_job_type_screen_config_exception.js +1 -1
- package/dist/core/exception/missing_partner_config_exception.d.ts +2 -0
- package/dist/core/exception/missing_partner_config_exception.js +1 -1
- package/dist/core/exception/unused_job_params_exception.js +1 -1
- package/dist/core/exception/use_smile_id_error_code.d.ts +1 -0
- package/dist/core/exception/use_smile_id_error_code.js +1 -1
- package/dist/core/exception/use_smile_id_network_exception.d.ts +2 -1
- package/dist/core/exception/use_smile_id_network_exception.js +1 -1
- package/dist/core/models/document_type.d.ts +1 -1
- package/dist/core/models/job_type.d.ts +5 -0
- package/dist/core/models/job_type.js +1 -1
- package/dist/core/models/network_config.d.ts +2 -3
- package/dist/core/models/params.d.ts +18 -0
- package/dist/core/models/params.js +1 -1
- package/dist/data/api/interceptors/smile_auth_interceptor.js +1 -1
- package/dist/data/api/smile_id_api.d.ts +6 -12
- package/dist/data/api/smile_id_api.js +1 -1
- package/dist/data/compression/compressible_asset.d.ts +9 -1
- package/dist/data/compression/compressible_asset.js +1 -1
- package/dist/data/compression/payload_compression_runner.d.ts +3 -0
- package/dist/data/compression/payload_compression_runner.js +1 -1
- package/dist/data/compression/payload_compressor.js +1 -1
- package/dist/data/factory/networking_factory.d.ts +2 -0
- package/dist/data/factory/networking_factory.js +1 -1
- package/dist/data/model/job_request.d.ts +20 -1
- package/dist/data/model/token_payload.d.ts +17 -1
- package/dist/data/model/token_payload.js +1 -1
- package/dist/data/repository/use_smile_id_repository_impl.js +1 -1
- package/dist/debug/capture_debug_geometry.d.ts +29 -0
- package/dist/debug/capture_debug_geometry.js +1 -0
- package/dist/debug/document_preview_debug_geometry.d.ts +36 -0
- package/dist/debug/document_preview_debug_geometry.js +1 -0
- package/dist/debug/preview_debug_drawing.d.ts +51 -0
- package/dist/debug/preview_debug_drawing.js +1 -0
- package/dist/debug/selfie_preview_debug_geometry.d.ts +55 -0
- package/dist/debug/selfie_preview_debug_geometry.js +1 -0
- package/dist/debug/use_smile_id_debug.d.ts +61 -0
- package/dist/debug/use_smile_id_debug.js +1 -0
- package/dist/debug/use_smile_id_debug_tap.d.ts +25 -0
- package/dist/debug/use_smile_id_debug_tap.js +1 -0
- package/dist/debug/use_smile_id_document_capture_debug_overlay.d.ts +56 -0
- package/dist/debug/use_smile_id_document_capture_debug_overlay.js +1 -0
- package/dist/debug/use_smile_id_document_preview_debug_overlay.d.ts +32 -0
- package/dist/debug/use_smile_id_document_preview_debug_overlay.js +1 -0
- package/dist/debug/use_smile_id_selfie_capture_debug_overlay.d.ts +27 -0
- package/dist/debug/use_smile_id_selfie_capture_debug_overlay.js +1 -0
- package/dist/debug/use_smile_id_selfie_preview_debug_overlay.d.ts +60 -0
- package/dist/debug/use_smile_id_selfie_preview_debug_overlay.js +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +1 -1
- package/dist/l10n/strings_en.d.ts +2 -1
- package/dist/l10n/strings_en.js +2 -2
- package/dist/l10n/use_smile_id_localizations.d.ts +1 -0
- package/dist/l10n/use_smile_id_localizations.js +1 -1
- package/dist/metadata/document_image_origin.d.ts +1 -0
- package/dist/metadata/document_image_origin.js +1 -1
- package/dist/metadata/metadata_configuration_mappers.js +1 -1
- package/dist/metadata/use_smile_id_metadata_factory.d.ts +4 -4
- package/dist/metadata/use_smile_id_metadata_factory.js +1 -1
- package/dist/presentation/assets/si_document_visa_hero_animation.json +1 -0
- package/dist/presentation/components/helpers/document_interior_crop.d.ts +54 -0
- package/dist/presentation/components/helpers/document_interior_crop.js +1 -0
- package/dist/presentation/components/helpers/selfie_interior_crop.d.ts +60 -0
- package/dist/presentation/components/helpers/selfie_interior_crop.js +1 -0
- package/dist/presentation/components/use_smile_id_capture_content.d.ts +7 -1
- package/dist/presentation/components/use_smile_id_capture_content.js +1 -1
- package/dist/presentation/components/use_smile_id_captured_document_image.d.ts +16 -0
- package/dist/presentation/components/use_smile_id_captured_document_image.js +1 -1
- package/dist/presentation/components/use_smile_id_captured_image_oval.d.ts +12 -4
- package/dist/presentation/components/use_smile_id_captured_image_oval.js +1 -1
- package/dist/presentation/components/use_smile_id_document_overlay.d.ts +12 -1
- package/dist/presentation/components/use_smile_id_document_overlay.js +1 -1
- package/dist/presentation/components/use_smile_id_document_shimmer.d.ts +10 -5
- package/dist/presentation/components/use_smile_id_document_shimmer.js +1 -1
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event.d.ts +4 -1
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event.js +1 -1
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.d.ts +89 -0
- package/dist/presentation/flow/analytics/use_smile_id_analytics_event_kind.js +1 -0
- package/dist/presentation/flow/dsl/builder/screens_builder.d.ts +4 -2
- package/dist/presentation/flow/dsl/builder/screens_builder.js +1 -1
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +17 -13
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +1 -1
- package/dist/presentation/flow/flow_navigation_manager.d.ts +52 -6
- package/dist/presentation/flow/flow_navigation_manager.js +1 -1
- package/dist/presentation/flow/model/instructions_defaults.d.ts +1 -0
- package/dist/presentation/flow/model/instructions_defaults.js +1 -1
- package/dist/presentation/flow/screen/helpers/always_enabled_slot.d.ts +8 -0
- package/dist/presentation/flow/screen/helpers/always_enabled_slot.js +1 -0
- package/dist/presentation/flow/screen/orchestrated_capture_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_consent_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_instructions_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_preview_screen.js +1 -1
- package/dist/presentation/flow/screen/orchestrated_processing_screen.js +1 -1
- package/dist/presentation/flow/validation/flow_validator.d.ts +13 -12
- package/dist/presentation/flow/validation/flow_validator.js +1 -1
- package/dist/presentation/flow/validation/job_type_validator.d.ts +9 -1
- package/dist/presentation/flow/validation/job_type_validator.js +1 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.d.ts +6 -1
- package/dist/presentation/screen/use_smile_id_capture_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_document_back_instructions.js +1 -1
- package/dist/presentation/screen/use_smile_id_document_preview_screen.d.ts +5 -0
- package/dist/presentation/screen/use_smile_id_document_preview_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_preview_screen.js +1 -1
- package/dist/presentation/screen/use_smile_id_processing_screen.d.ts +5 -0
- package/dist/presentation/screen/use_smile_id_processing_screen.js +1 -1
- package/package.json +15 -15
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { SelfieSize } from "./selfie_interior_crop";
|
|
2
|
+
/** Where an image is drawn in its card: top-left corner plus size, in layout units. */
|
|
3
|
+
export type DocumentImageRect = {
|
|
4
|
+
readonly left: number;
|
|
5
|
+
readonly top: number;
|
|
6
|
+
readonly width: number;
|
|
7
|
+
readonly height: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* How a captured document is scaled into its preview card: the outline the user
|
|
11
|
+
* filled, and as far past it as auto-capture reaches, but none of the rest of
|
|
12
|
+
* the border, as `SelfieInteriorCrop` hides the selfie's.
|
|
13
|
+
*
|
|
14
|
+
* An auto-captured document is saved with a border around the outline the user
|
|
15
|
+
* filled, of `fullBorderFraction` of its short side on every edge, or less where
|
|
16
|
+
* the frame had no room (the provider's native `ResizingFrameStorage`, the rule of
|
|
17
|
+
* the native bridges' `DocumentCropGeometry`). The border is where
|
|
18
|
+
* presentation-attack cues live, and the backend reads it. Auto-capture accepts a
|
|
19
|
+
* document whose detected box reaches `guideEdgeSlack` of the outline's size past
|
|
20
|
+
* each edge, so showing the outline alone could cut a captured document off; the
|
|
21
|
+
* screens that show the document fit the outline grown by that slack into their
|
|
22
|
+
* card instead (`shownSize`). A manual-shutter still or a gallery import is saved
|
|
23
|
+
* without a border, so its border fraction is 0 and it is shown whole.
|
|
24
|
+
*/
|
|
25
|
+
export declare const DocumentInteriorCrop: {
|
|
26
|
+
/** The full border, as a fraction of the saved document's short side on every edge. */
|
|
27
|
+
readonly fullBorderFraction: 0.15;
|
|
28
|
+
/**
|
|
29
|
+
* How far past each edge of the outline an auto-captured document's detected
|
|
30
|
+
* box may reach, as a fraction of the outline's size on that axis. The native
|
|
31
|
+
* document validators the providers run accept a document this far out
|
|
32
|
+
* (`DocumentCropGeometry.GUIDE_EDGE_SLACK` on Android, `guideEdgeSlack` on
|
|
33
|
+
* iOS); keep it equal to theirs.
|
|
34
|
+
*/
|
|
35
|
+
readonly guideEdgeSlack: 0.1;
|
|
36
|
+
/**
|
|
37
|
+
* The part of an `imageSize` document the card shows: the interior inside its
|
|
38
|
+
* `borderFraction`, grown by `guideEdgeSlack` of the interior's size past each
|
|
39
|
+
* edge, and held to the image. The whole image when `borderFraction` is 0.
|
|
40
|
+
*/
|
|
41
|
+
readonly shownSize: (imageSize: SelfieSize, borderFraction: number) => SelfieSize;
|
|
42
|
+
/**
|
|
43
|
+
* Uniform scale at which an `imageSize` document is drawn in a `boxSize` card,
|
|
44
|
+
* centred: its `shownSize` part fitted to the card, or, with no border, the
|
|
45
|
+
* whole image covering it. Returns 1 when either size is degenerate.
|
|
46
|
+
*/
|
|
47
|
+
readonly cardScale: (imageSize: SelfieSize, boxSize: SelfieSize, borderFraction: number) => number;
|
|
48
|
+
/**
|
|
49
|
+
* Where to draw an `imageSize` document in a `boxSize` card at `cardScale`,
|
|
50
|
+
* centred. The border is uniform on every edge, so the interior sits at the
|
|
51
|
+
* image's centre and centring the image centres it.
|
|
52
|
+
*/
|
|
53
|
+
readonly imageRect: (imageSize: SelfieSize, boxSize: SelfieSize, borderFraction: number) => DocumentImageRect;
|
|
54
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";export const DocumentInteriorCrop={fullBorderFraction:.15,guideEdgeSlack:.1,shownSize(t,h){const n=Math.min(Math.max(h,0),DocumentInteriorCrop.fullBorderFraction)*Math.min(t.width,t.height),c=1+2*DocumentInteriorCrop.guideEdgeSlack;return{width:Math.min(t.width,(t.width-2*n)*c),height:Math.min(t.height,(t.height-2*n)*c)}},cardScale(t,h,r){if(t.width<=0||t.height<=0||h.width<=0||h.height<=0)return 1;if(r<=0)return Math.max(h.width/t.width,h.height/t.height);const n=DocumentInteriorCrop.shownSize(t,r);return Math.min(h.width/n.width,h.height/n.height)},imageRect(t,h,r){const n=DocumentInteriorCrop.cardScale(t,h,r),c=t.width*n,d=t.height*n;return{left:(h.width-c)/2,top:(h.height-d)/2,width:c,height:d}}};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** A width × height in layout units (or image pixels). */
|
|
2
|
+
export type SelfieSize = {
|
|
3
|
+
readonly width: number;
|
|
4
|
+
readonly height: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* How a captured selfie is scaled into the face oval: only the frame's interior
|
|
8
|
+
* is ever shown.
|
|
9
|
+
*
|
|
10
|
+
* The SDK captures, and uploads, a larger region than it displays. The outer
|
|
11
|
+
* ring, `borderFraction` of the frame's short side on every edge, is where
|
|
12
|
+
* presentation-attack cues live (a phone screen's bezels, a print's edge). The
|
|
13
|
+
* backend reads them, and an attacker who can't see them on screen can't
|
|
14
|
+
* adjust for them. So every screen that shows the captured selfie covers its
|
|
15
|
+
* oval box with the interior alone, centred, instead of the whole frame.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SelfieInteriorCrop: {
|
|
18
|
+
/** Border kept out of view, as a fraction of the frame's short side, trimmed from every edge. */
|
|
19
|
+
readonly borderFraction: 0.15;
|
|
20
|
+
/**
|
|
21
|
+
* Uniform scale at which the interior of an `imageSize` frame covers
|
|
22
|
+
* `boxSize`: the frame minus the border on every edge, cover-fitted. Returns
|
|
23
|
+
* 1 when either size is degenerate, so an unmeasured image draws unscaled
|
|
24
|
+
* rather than collapsing.
|
|
25
|
+
*/
|
|
26
|
+
readonly coverScale: (imageSize: SelfieSize, boxSize: SelfieSize) => number;
|
|
27
|
+
/**
|
|
28
|
+
* Where to slice-cover (`xMidYMid slice`) an image whose size can't be read:
|
|
29
|
+
* the `boxSize` box, centred, enlarged by 1 / (1 - 2 × `borderFraction`).
|
|
30
|
+
* For any portrait or square frame that crops at least as much as
|
|
31
|
+
* `coverScale` does, so the border ring still stays out of view, at the cost
|
|
32
|
+
* of a slightly tighter crop than the exact one.
|
|
33
|
+
*/
|
|
34
|
+
readonly unknownSizeRect: (boxSize: SelfieSize) => {
|
|
35
|
+
readonly x: number;
|
|
36
|
+
readonly y: number;
|
|
37
|
+
readonly width: number;
|
|
38
|
+
readonly height: number;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Reads the captured file's upright pixel size — the dimensions React Native
|
|
43
|
+
* shows on screen — or `null` when the file is missing or not a decodable
|
|
44
|
+
* image.
|
|
45
|
+
*/
|
|
46
|
+
export declare function decodeUprightImageSize(uri: string): Promise<SelfieSize | null>;
|
|
47
|
+
/** Where a size read stands: still pending, read, or unreadable. */
|
|
48
|
+
export type ImageSizeState = {
|
|
49
|
+
readonly kind: "pending";
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: "read";
|
|
52
|
+
readonly size: SelfieSize;
|
|
53
|
+
} | {
|
|
54
|
+
readonly kind: "unreadable";
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The upright size of the image at `uri`: `pending` until it has been read,
|
|
58
|
+
* then `read` or `unreadable`. A new `uri` starts over from `pending`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function useImageSize(uri: string | undefined): ImageSizeState;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{useEffect as o,useState as d}from"react";import{Image as i}from"react-native";export const SelfieInteriorCrop={borderFraction:.15,coverScale(t,e){if(t.width<=0||t.height<=0||e.width<=0||e.height<=0)return 1;const n=SelfieInteriorCrop.borderFraction*Math.min(t.width,t.height);return Math.max(e.width/(t.width-2*n),e.height/(t.height-2*n))},unknownSizeRect(t){const e=1/(1-2*SelfieInteriorCrop.borderFraction),n=t.width*e,r=t.height*e;return{x:(t.width-n)/2,y:(t.height-r)/2,width:n,height:r}}};export function decodeUprightImageSize(t){return new Promise(e=>{if(typeof i.getSize!="function"){e(null);return}i.getSize(t,(n,r)=>e(n>0&&r>0?{width:n,height:r}:null),()=>e(null))})}export function useImageSize(t){const[e,n]=d({uri:t,result:{kind:"pending"}});return o(()=>{if(!t)return;let r=!0;return decodeUprightImageSize(t).then(h=>{r&&n({uri:t,result:h?{kind:"read",size:h}:{kind:"unreadable"}})}),()=>{r=!1}},[t]),e.uri===t?e.result:{kind:"pending"}}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DocumentSide } from "@smileid/usesmileid_bridge";
|
|
1
2
|
import { type ReactNode } from "react";
|
|
2
3
|
import type { ScanHeadTurnDirection } from "@smileid/usesmileid_bridge";
|
|
3
4
|
import { CaptureType } from "../../core/models/ml_config";
|
|
@@ -21,7 +22,12 @@ export type UseSmileIDCaptureContentState = {
|
|
|
21
22
|
readonly knownIdAspectRatio?: number;
|
|
22
23
|
/** Document type — selects the in-frame shimmer skeleton. Ignored for selfie. */
|
|
23
24
|
readonly documentTypeKind?: DocumentType["kind"];
|
|
24
|
-
/**
|
|
25
|
+
/** Which document image is being framed (front, back or visa). Ignored for selfie. */
|
|
26
|
+
readonly documentSide?: DocumentSide;
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated Use `documentSide`, which also covers the visa page. Read only when
|
|
29
|
+
* `documentSide` is absent; `true` selects the back skeleton. Ignored for selfie.
|
|
30
|
+
*/
|
|
25
31
|
readonly isBackSide?: boolean;
|
|
26
32
|
/** Document manual shutter availability (capture-mode + fallback-timer driven). Ignored for selfie. */
|
|
27
33
|
readonly showManualShutter?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as i,Fragment as v,jsxs as c}from"react/jsx-runtime";import{memo as B,useState as A}from"react";import{Pressable as L,StyleSheet as T,Text as _,View as f}from"react-native";import{SafeAreaView as g}from"react-native-safe-area-context";import{CaptureType as U}from"../../core/models/ml_config";import{AttributionResolver as x}from"../flow/config/attribution_resolver";import{defaultLightTheme as b}from"../theme/theme";import{useUseSmileIDTheme as E}from"../theme/theme_context";import{isDocumentCaptureActive as y,UseSmileIDOverlayState as M}from"../state/use_smile_id_overlay_state";import{SiOutlineClose as K}from"./icons";import{useUseSmileIDLocalizations as F}from"../../l10n/use_smile_id_localizations_provider";import{resolveFeedbackCopy as H}from"../screen/helpers/capture_screen_helpers";import{ATTRIBUTION_TEST_ID as R,UseSmileIDAttribution as W}from"./use_smile_id_attribution";import{UseSmileIDBackButton as j}from"./use_smile_id_back_button";import{UseSmileIDButton as G}from"./use_smile_id_button";import{UseSmileIDDocumentOverlay as O}from"./use_smile_id_document_overlay";import{UseSmileIDDocumentCaptureControls as
|
|
1
|
+
"use strict";import{jsx as i,Fragment as v,jsxs as c}from"react/jsx-runtime";import{memo as B,useState as A}from"react";import{Pressable as L,StyleSheet as T,Text as _,View as f}from"react-native";import{SafeAreaView as g}from"react-native-safe-area-context";import{CaptureType as U}from"../../core/models/ml_config";import{AttributionResolver as x}from"../flow/config/attribution_resolver";import{defaultLightTheme as b}from"../theme/theme";import{useUseSmileIDTheme as E}from"../theme/theme_context";import{isDocumentCaptureActive as y,UseSmileIDOverlayState as M}from"../state/use_smile_id_overlay_state";import{SiOutlineClose as K}from"./icons";import{useUseSmileIDLocalizations as F}from"../../l10n/use_smile_id_localizations_provider";import{resolveFeedbackCopy as H}from"../screen/helpers/capture_screen_helpers";import{ATTRIBUTION_TEST_ID as R,UseSmileIDAttribution as W}from"./use_smile_id_attribution";import{UseSmileIDBackButton as j}from"./use_smile_id_back_button";import{UseSmileIDButton as G}from"./use_smile_id_button";import{UseSmileIDDocumentOverlay as O,resolveDocumentSide as z}from"./use_smile_id_document_overlay";import{UseSmileIDDocumentCaptureControls as P}from"./use_smile_id_document_capture_controls";import{UseSmileIDFaceOverlay as V}from"./use_smile_id_face_overlay";import{UseSmileIDCaptureInstructionAnimation as N}from"./use_smile_id_capture_instruction_animation";import{enhancedSmartSelfieInstructionAnimationKey as $}from"./helpers/capture_instruction_animation_spec";import{UseSmileIDEnhancedSmartSelfieDirectionAnimation as q}from"./use_smile_id_enhanced_smart_selfie_direction_animation";import{UseSmileIDEnhancedSmartSelfieFullAnimation as J}from"./use_smile_id_enhanced_smart_selfie_full_animation";import{FaceOvalDefaults as Q}from"./face_oval_defaults";const S=()=>{};function X(t){const C=E(),n=t.theme??C,a=F(),{state:e}=t,[o,D]=A({width:0,height:0}),I=m=>{const{width:d,height:u}=m.nativeEvent.layout;D(h=>h.width===d&&h.height===u?h:{width:d,height:u}),t.onViewportLayout?.({width:d,height:u})},r=e.captureType===U.selfie,w=x.resolveForCapture({captureType:e.captureType,showAttribution:t.showAttribution}),k=!r&&o.width>o.height,s=e.feedbackKey?H(e.feedbackKey,a):null,p=t.startButton;return c(f,{style:[l.root,{backgroundColor:n.colors.offBlack}],testID:"si_capture_screen",onLayout:I,children:[t.cameraPreview,o.width>0&&o.height>0?c(v,{children:[r?i(V,{state:e.overlayState,progress:t.captureProgress,containerWidth:o.width,containerHeight:o.height,enableEnhancedLiveness:e.enableEnhancedLiveness,theme:n}):i(O,{state:e.overlayState,containerWidth:o.width,containerHeight:o.height,theme:n,documentSide:z(e.documentSide,e.isBackSide),...e.documentTypeKind!==void 0?{documentTypeKind:e.documentTypeKind}:{},...e.knownIdAspectRatio!==void 0?{knownIdAspectRatio:e.knownIdAspectRatio}:{},...s!==null?{feedbackText:s}:{}}),r?i(N,{feedbackKey:e.enableEnhancedLiveness?$(e.feedbackKey):e.feedbackKey,containerWidth:o.width,containerHeight:o.height}):null,r&&e.enableEnhancedLiveness?e.animationDirection!==null?i(q,{direction:e.animationDirection,poseMatched:e.poseMatched,containerWidth:o.width,containerHeight:o.height}):e.overlayState===M.IDLE?i(J,{containerWidth:o.width,containerHeight:o.height}):null:null,r?null:i(P,{showManualShutter:(e.showManualShutter??!1)&&t.onManualShutter!==void 0&&!y(e.overlayState),showGallery:(e.showGalleryUpload??!1)&&t.onGalleryUpload!==void 0&&!y(e.overlayState),isLandscape:o.width>o.height,enabled:t.documentControlsEnabled??!0,onManualShutter:t.onManualShutter??S,onGallery:t.onGalleryUpload??S,shutterAccessibilityLabel:a.siCdDocumentCaptureShutter,galleryAccessibilityLabel:a.siCdDocumentGallery,closeAccessibilityLabel:a.siCdClose,onClose:t.onClose})]}):null,c(g,{edges:["top","left","right"],style:[l.topBar,{paddingHorizontal:n.spacing.medium,paddingTop:n.spacing.medium}],pointerEvents:"box-none",children:[t.onBack?i(j,{onClick:t.onBack,forceLightMode:!0,theme:n,testID:"si_capture_back_button"}):i(f,{style:l.chromeSpacer}),t.onClose&&!k?i(L,{onPress:t.onClose,accessibilityRole:"button",accessibilityLabel:a.siCdClose,testID:"si_capture_close_button",style:({pressed:m})=>[l.closeButton,{backgroundColor:b.colors.offBlack,borderColor:b.colors.backButtonBorder,opacity:m?.7:1}],children:i(K,{size:20,color:b.colors.offWhite})}):null]}),s!==null&&r?i(f,{style:[l.feedbackBubble,{paddingHorizontal:n.spacing.medium,paddingVertical:n.spacing.small}],pointerEvents:"none",children:i(_,{style:{...n.typography.cardTitle,color:n.colors.primary,textAlign:"center"},testID:"si_capture_feedback_text",children:s})}):null,c(g,{edges:["bottom","left","right"],style:[l.bottom,{paddingHorizontal:n.spacing.medium,paddingBottom:n.spacing.large,gap:n.spacing.small}],pointerEvents:"box-none",children:[e.showCaptureButton?p?p({onClick:t.onStartCapture,enabled:t.startCaptureEnabled}):i(G,{label:a.siCaptureStartButton,onClick:t.onStartCapture,variant:"primary",enabled:t.startCaptureEnabled,forceLightMode:!0,theme:n,testID:"si_capture_start_button"}):null,w?i(W,{forceLightMode:!0,theme:n,testID:R}):null]})]})}export const UseSmileIDCaptureContent=B(X);const l=T.create({root:{flex:1},topBar:{position:"absolute",top:0,start:0,end:0,flexDirection:"row",justifyContent:"space-between",alignItems:"flex-start"},closeButton:{width:40,height:40,borderRadius:20,borderWidth:1,alignItems:"center",justifyContent:"center"},chromeSpacer:{width:40,height:40},feedbackBubble:{position:"absolute",top:`${Math.round(Q.feedbackTopRatio*100)}%`,alignSelf:"center",maxWidth:"85%"},bottom:{position:"absolute",bottom:0,start:0,end:0,alignItems:"center"}});
|
|
@@ -5,6 +5,22 @@ export type UseSmileIDCapturedDocumentImageProps = {
|
|
|
5
5
|
readonly uri?: string;
|
|
6
6
|
/** Capture-frame aspect ratio (width / height) the box keeps while fitting the region. */
|
|
7
7
|
readonly aspectRatio: number;
|
|
8
|
+
/**
|
|
9
|
+
* The hidden border `uri` was saved with, as a fraction of its short side on every edge. The
|
|
10
|
+
* card shows the interior alone and hides exactly this much; 0, the default, shows the image
|
|
11
|
+
* whole. See `DocumentInteriorCrop`.
|
|
12
|
+
*/
|
|
13
|
+
readonly borderFraction?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Whether a dev build may mark the hidden border over the card while
|
|
16
|
+
* `UseSmileIDDebug.setDocumentDebugOverlaysEnabled(true)` is on. Only the preview screen sets it.
|
|
17
|
+
*/
|
|
18
|
+
readonly showDebugOverlay?: boolean;
|
|
19
|
+
/** Reports the card's laid-out size, for the preview screen's debug overlay. */
|
|
20
|
+
readonly onCardLayout?: (size: {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
}) => void;
|
|
8
24
|
/** Content centered over the document rect — e.g. the processing-screen status badge. */
|
|
9
25
|
readonly overlayChild?: ReactNode;
|
|
10
26
|
/** Accessible name for the image; when unset the image stays decorative. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as
|
|
1
|
+
"use strict";import{jsx as l,jsxs as L}from"react/jsx-runtime";import{useState as b}from"react";import{Image as j,StyleSheet as f,View as c}from"react-native";import{useUseSmileIDTheme as E}from"../theme/theme_context";import{fitCapturedDocumentImage as F}from"./helpers/captured_document_image_fit";import{DocumentInteriorCrop as B}from"./helpers/document_interior_crop";import{useImageSize as O}from"./helpers/selfie_interior_crop";import{useDocumentPreviewOverlayEnabled as k}from"../../debug/use_smile_id_debug";import{DocumentPreviewHiddenRegionsInPlace as A}from"../../debug/use_smile_id_document_preview_debug_overlay";const I=8;export function UseSmileIDCapturedDocumentImage(e){const w=E(),x=e.theme??w,u=e.aspectRatio,g=x.spacing.large,[h,v]=b({width:0,height:0}),D=d=>{const{width:i,height:o}=d.nativeEvent.layout;v(t=>t.width===i&&t.height===o?t:{width:i,height:o})},R=F(u,h.width-2*g,h.height)??{...n.fillWidth,aspectRatio:u},a=e.borderFraction??0,y=k()&&e.showDebugOverlay===!0,r=O(a>0||y?e.uri:void 0),[s,S]=b(null),C=d=>{const{width:i,height:o}=d.nativeEvent.layout;S(t=>t?.width===i&&t.height===o?t:{width:i,height:o}),e.onCardLayout?.({width:i,height:o})},m=a>0&&s!==null&&r.kind==="read"?B.imageRect(r.size,s,a):null,z=!!e.uri&&(a<=0||m!==null||r.kind==="unreadable");return l(c,{style:[n.region,{paddingHorizontal:g}],onLayout:D,testID:e.testID,children:L(c,{style:[n.imageBox,R],onLayout:C,children:[z?l(j,{source:{uri:e.uri},resizeMode:"cover",style:m?[n.interiorImage,m]:n.image,testID:e.imageTestID,...e.imageAccessibilityLabel?{accessible:!0,accessibilityRole:"image",accessibilityLabel:e.imageAccessibilityLabel}:{}}):null,y&&s!==null&&r.kind==="read"?l(c,{pointerEvents:"none",style:f.absoluteFill,children:l(A,{imageSize:r.size,cardSize:s,borderFraction:a})}):null,e.overlayChild?l(c,{pointerEvents:"box-none",style:n.overlay,children:e.overlayChild}):null]})})}const n=f.create({region:{flex:1,alignItems:"center",justifyContent:"center"},imageBox:{borderRadius:I,overflow:"hidden"},fillWidth:{width:"100%"},image:{width:"100%",height:"100%",borderRadius:I},interiorImage:{position:"absolute"},overlay:{position:"absolute",top:0,bottom:0,start:0,end:0,alignItems:"center",justifyContent:"center"}});
|
|
@@ -23,6 +23,12 @@ export type UseSmileIDCapturedImageOvalProps = {
|
|
|
23
23
|
* status badge. Positioned once the region has measured.
|
|
24
24
|
*/
|
|
25
25
|
readonly overlayChild?: ReactNode;
|
|
26
|
+
/**
|
|
27
|
+
* Whether a debug build may draw the selfie preview's hidden-region overlay
|
|
28
|
+
* here while `UseSmileIDDebug.setSelfieDebugOverlaysEnabled(true)` is on.
|
|
29
|
+
* Only the preview screen sets it; inert in release builds.
|
|
30
|
+
*/
|
|
31
|
+
readonly showDebugOverlay?: boolean;
|
|
26
32
|
readonly testID?: string;
|
|
27
33
|
readonly theme?: UseSmileIDTheme;
|
|
28
34
|
};
|
|
@@ -32,9 +38,11 @@ export type UseSmileIDCapturedImageOvalProps = {
|
|
|
32
38
|
* card (`flex: 1`) and measures its own height; the oval is then
|
|
33
39
|
* scaled to fit so the card below it is never overlapped.
|
|
34
40
|
*
|
|
35
|
-
* The image is drawn through `react-native-svg` (`<Image>`
|
|
36
|
-
*
|
|
37
|
-
* can't express the ~1:1.26 ellipse on Android.
|
|
38
|
-
* the
|
|
41
|
+
* The image is drawn through `react-native-svg` (`<Image>` sized so the
|
|
42
|
+
* frame's interior covers the oval bounds, clipped by an `<Ellipse>`) —
|
|
43
|
+
* RN's `borderRadius` can't express the ~1:1.26 ellipse on Android. Only
|
|
44
|
+
* the interior shows (`SelfieInteriorCrop`); the captured border ring stays
|
|
45
|
+
* out of view. Outside the oval is the uniform page background; no scrim
|
|
46
|
+
* is painted.
|
|
39
47
|
*/
|
|
40
48
|
export declare function UseSmileIDCapturedImageOval(props: UseSmileIDCapturedImageOvalProps): import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as i,jsxs as
|
|
1
|
+
"use strict";import{jsx as i,jsxs as v}from"react/jsx-runtime";import{useId as I,useState as D}from"react";import{StyleSheet as H,View as s}from"react-native";import C,{ClipPath as R,Defs as W,Ellipse as z,Image as E}from"react-native-svg";import{useSelfiePreviewOverlayEnabled as L}from"../../debug/use_smile_id_debug";import{UseSmileIDSelfiePreviewDebugOverlay as O}from"../../debug/use_smile_id_selfie_preview_debug_overlay";import{useUseSmileIDTheme as j}from"../theme/theme_context";import{computeScaledOvalGeometry as _}from"./helpers/scaled_oval_geometry";import{SelfieInteriorCrop as y,useImageSize as P}from"./helpers/selfie_interior_crop";export function UseSmileIDCapturedImageOval(e){const f=j(),b=e.theme??f,[c,x]=D(0),g=`si_oval_clip_${I().replace(/:/g,"")}`,S=L(),o=P(e.uri),w=l=>{x(l.nativeEvent.layout.height)},t=e.screenWidth>0&&e.screenHeight>0&&c>0?_(e.screenWidth,e.screenHeight,c,b.spacing.medium):null,a=t!==null&&t.scale>0,n=(()=>{if(!t||o.kind==="pending")return null;const l={width:t.ovalWidth,height:t.ovalHeight};if(o.kind==="unreadable")return y.unknownSizeRect(l);const m=y.coverScale(o.size,l),d=o.size.width*m,u=o.size.height*m;return{x:(l.width-d)/2,y:(l.height-u)/2,width:d,height:u}})(),r=t?{width:t.ovalWidth,height:t.ovalHeight,left:t.ovalLeft,top:t.ovalTop}:{width:0,height:0,left:0,top:0};return v(s,{style:h.region,onLayout:w,testID:e.testID,children:[e.uri?i(s,{pointerEvents:"none",style:[h.oval,r],testID:e.imageTestID,...e.imageAccessibilityLabel?{accessible:!0,accessibilityRole:"image",accessibilityLabel:e.imageAccessibilityLabel}:{},children:a&&t&&n?v(C,{width:"100%",height:"100%",viewBox:`0 0 ${t.ovalWidth} ${t.ovalHeight}`,children:[i(W,{children:i(R,{id:g,children:i(z,{cx:t.ovalWidth/2,cy:t.ovalHeight/2,rx:t.ovalWidth/2,ry:t.ovalHeight/2})})}),i(E,{href:{uri:e.uri},x:n.x,y:n.y,width:n.width,height:n.height,preserveAspectRatio:"xMidYMid slice",clipPath:`url(#${g})`})]}):null}):null,e.overlayChild&&a?i(s,{pointerEvents:"box-none",style:[h.overlay,r],children:e.overlayChild}):null,e.showDebugOverlay&&S&&e.uri&&a?i(O,{uri:e.uri,ovalRect:r}):null]})}const h=H.create({region:{flex:1},oval:{position:"absolute"},overlay:{position:"absolute",alignItems:"center",justifyContent:"center"}});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DocumentSide } from "@smileid/usesmileid_bridge";
|
|
1
2
|
import type { UseSmileIDTheme } from "../theme/theme";
|
|
2
3
|
import { type UseSmileIDOverlayState as OverlayState } from "../state/use_smile_id_overlay_state";
|
|
3
4
|
import type { DocumentType } from "../../core/models/document_type";
|
|
@@ -9,7 +10,12 @@ export type UseSmileIDDocumentOverlayProps = {
|
|
|
9
10
|
readonly knownIdAspectRatio?: number;
|
|
10
11
|
/** Document type — selects the in-frame shimmer skeleton. */
|
|
11
12
|
readonly documentTypeKind?: DocumentType["kind"];
|
|
12
|
-
/**
|
|
13
|
+
/** Which document image is being framed — selects the front, back or visa skeleton. */
|
|
14
|
+
readonly documentSide?: DocumentSide;
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated Use `documentSide`, which also covers the visa page. Read only when
|
|
17
|
+
* `documentSide` is absent; `true` selects the back skeleton.
|
|
18
|
+
*/
|
|
13
19
|
readonly isBackSide?: boolean;
|
|
14
20
|
/**
|
|
15
21
|
* Coaching copy shown in the dark pill below the frame. Rendered here so it
|
|
@@ -19,6 +25,11 @@ export type UseSmileIDDocumentOverlayProps = {
|
|
|
19
25
|
readonly testID?: string;
|
|
20
26
|
readonly theme?: UseSmileIDTheme;
|
|
21
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* The image an overlay is framing. `isBackSide` is the pre-visa boolean and is
|
|
30
|
+
* consulted only when `documentSide` is absent.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveDocumentSide(documentSide: DocumentSide | undefined, isBackSide: boolean | undefined): DocumentSide;
|
|
22
33
|
/** Default document aspect (CR-80 ID-1 card) when no known ratio is supplied. */
|
|
23
34
|
export declare const DEFAULT_DOCUMENT_ASPECT = 1.586;
|
|
24
35
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as
|
|
1
|
+
"use strict";import{jsx as i,jsxs as R}from"react/jsx-runtime";import{DocumentSide as $}from"@smileid/usesmileid_bridge";import{memo as D}from"react";import{StyleSheet as T,Text as S,View as m}from"react-native";import{useSafeAreaInsets as E}from"react-native-safe-area-context";import b,{Path as p}from"react-native-svg";import{useUseSmileIDTheme as v}from"../theme/theme_context";import{isDocumentCaptureActive as F}from"../state/use_smile_id_overlay_state";import{UseSmileIDOvalBorderPalette as H}from"./use_smile_id_oval_border_palette";import{UseSmileIDDocumentShimmer as C}from"./use_smile_id_document_shimmer";import{UseSmileIDCaptureSpinner as g}from"./use_smile_id_capture_spinner";export function resolveDocumentSide(t,n){return t!==void 0?t:n===!0?$.Back:$.Front}const M=16,O=.71,y=.61,I=16,u=4,f=48,P=24;export const DEFAULT_DOCUMENT_ASPECT=1.586;export function documentGuideRect(t,n,o){let c=n*O,e=c/t;const s=o*y;return e>s&&(e=s,c=e*t),{left:(n-c)/2,top:(o-e)/2,width:c,height:e}}const x="#00000026",W="#00000066";export function UseSmileIDDocumentOverlay(t){const n=v(),o=t.theme??n,c=E(),e=t.knownIdAspectRatio??DEFAULT_DOCUMENT_ASPECT,s=e<1,{left:r,top:d,width:l,height:a}=documentGuideRect(e,t.containerWidth,t.containerHeight),h=F(t.state),_=o.colors[H.tokenFor(t.state,!1)],A=o.colors.documentOverlayScrim??W;return t.containerWidth<=0||t.containerHeight<=0?null:R(m,{testID:t.testID,style:T.absoluteFill,pointerEvents:"none",children:[i(m,{style:[T.absoluteFill,{backgroundColor:x}]}),i(U,{containerWidth:t.containerWidth,containerHeight:t.containerHeight,frameLeft:r,frameTop:d,frameWidth:l,frameHeight:a,cornerRadius:I,color:A}),h?null:i(m,{style:{position:"absolute",left:r+l*.1,top:d+a*.1,width:l*.8,height:a*.8},pointerEvents:"none",children:i(C,{width:l*.8,height:a*.8,documentSide:resolveDocumentSide(t.documentSide,t.isBackSide),...t.documentTypeKind!==void 0?{documentTypeKind:t.documentTypeKind}:{}})}),i(m,{style:{position:"absolute",left:r-u/2,top:d-u/2,width:l+u,height:a+u,borderWidth:u,borderRadius:I+u/2,borderColor:_}}),h?s?i(m,{style:{position:"absolute",left:0,right:0,bottom:c.bottom+P,alignItems:"center"},pointerEvents:"none",children:i(g,{size:f,color:o.colors.ovalActive,testID:"si_document_capture_spinner"})}):i(m,{style:{position:"absolute",left:r+l+(t.containerWidth-(r+l)-f)/2,top:d+(a-f)/2},pointerEvents:"none",children:i(g,{size:f,color:o.colors.ovalActive,testID:"si_document_capture_spinner"})}):null,t.feedbackText?i(m,{style:{position:"absolute",top:d+a+M,start:0,end:0,alignItems:"center"},pointerEvents:"none",children:i(m,{style:{maxWidth:"85%",borderRadius:8,backgroundColor:o.colors.offBlack,paddingHorizontal:o.spacing.medium,paddingVertical:o.spacing.small},children:i(S,{style:{...o.typography.cardTitle,color:"#FFFFFF",textAlign:"center"},testID:"si_document_feedback_text",children:t.feedbackText})})}):null]})}const U=D(function(n){const{containerWidth:o,containerHeight:c,frameLeft:e,frameTop:s,frameWidth:r,frameHeight:d,cornerRadius:l,color:a}=n,h=Math.min(l,r/2,d/2),_=`M0 0H${o}V${c}H0Z`,A=k(e,s,r,d,h);return i(b,{width:"100%",height:"100%",viewBox:`0 0 ${o} ${c}`,preserveAspectRatio:"none",style:T.absoluteFill,children:i(p,{d:`${_} ${A}`,fill:a,fillRule:"evenodd"})})});function k(t,n,o,c,e){const s=t+o,r=n+c;return`M${t+e} ${n}H${s-e}A${e} ${e} 0 0 1 ${s} ${n+e}V${r-e}A${e} ${e} 0 0 1 ${s-e} ${r}H${t+e}A${e} ${e} 0 0 1 ${t} ${r-e}V${n+e}A${e} ${e} 0 0 1 ${t+e} ${n}Z`}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
import { DocumentSide } from "@smileid/usesmileid_bridge";
|
|
1
2
|
import type { DocumentType } from "../../core/models/document_type";
|
|
2
3
|
/** Translucent in-frame document shimmer — the per-type "align here" skeleton, scaled to fit the capture frame. */
|
|
3
4
|
export type UseSmileIDDocumentShimmerProps = {
|
|
4
5
|
readonly width: number;
|
|
5
6
|
readonly height: number;
|
|
6
|
-
/** Selects the skeleton; the green book and passport ignore `
|
|
7
|
+
/** Selects the skeleton; the green book and passport ignore `documentSide`. */
|
|
7
8
|
readonly documentTypeKind?: DocumentType["kind"];
|
|
8
|
-
/**
|
|
9
|
-
readonly
|
|
9
|
+
/** Which document image is being captured. The visa page has no skeleton. */
|
|
10
|
+
readonly documentSide?: DocumentSide;
|
|
10
11
|
readonly testID?: string;
|
|
11
12
|
};
|
|
12
|
-
/**
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Shimmer skeleton for a document type + image, or null for the visa page,
|
|
15
|
+
* which shows the bare frame. Otherwise only generic documents have
|
|
16
|
+
* front/back, and passport and green book ignore the side.
|
|
17
|
+
*/
|
|
18
|
+
export declare function documentShimmerXml(documentTypeKind: DocumentType["kind"] | undefined, documentSide: DocumentSide): string | null;
|
|
14
19
|
export declare const UseSmileIDDocumentShimmer: import("react").MemoExoticComponent<(props: UseSmileIDDocumentShimmerProps) => import("react").JSX.Element | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as
|
|
1
|
+
"use strict";import{jsx as S}from"react/jsx-runtime";import{memo as s}from"react";import{SvgXml as c}from"react-native-svg";import{DocumentSide as e}from"@smileid/usesmileid_bridge";import{SI_DOCUMENT_BACK_SHIMMER as _,SI_DOCUMENT_FRONT_SHIMMER as l,SI_DOCUMENT_GREEN_BOOK_SHIMMER as I,SI_DOCUMENT_PASSPORT_SHIMMER as f}from"../assets/svg/document_shimmer_strings";export function documentShimmerXml(m,t){if(t===e.Visa)return null;switch(m){case"passport":return f;case"southAfricaGreenBook":return I;default:return t===e.Back?_:l}}export const UseSmileIDDocumentShimmer=s(function(t){const{width:r,height:n,documentTypeKind:i,documentSide:u=e.Front}=t,o=documentShimmerXml(i,u);return r<=0||n<=0||o===null?null:S(c,{xml:o,width:r,height:n,...t.testID!==void 0?{testID:t.testID}:{}})});
|
|
@@ -31,7 +31,10 @@ export declare const UseSmileIDAnalyticsEventTypes: {
|
|
|
31
31
|
export type UseSmileIDAnalyticsEventType = (typeof UseSmileIDAnalyticsEventTypes)[keyof typeof UseSmileIDAnalyticsEventTypes];
|
|
32
32
|
/** Extra parameter key constants for `UseSmileIDAnalyticsEvent.extras`. */
|
|
33
33
|
export declare const UseSmileIDAnalyticsParamKeys: {
|
|
34
|
+
/** This SDK's job type label, frozen per SDK; group across platforms on `jobTypeId`. */
|
|
34
35
|
readonly jobType: "job_type";
|
|
36
|
+
/** The numeric job type id, the same on every SDK. */
|
|
37
|
+
readonly jobTypeId: "job_type_id";
|
|
35
38
|
readonly screenName: "screen_name";
|
|
36
39
|
readonly sessionId: "session_id";
|
|
37
40
|
readonly timestamp: "timestamp";
|
|
@@ -65,10 +68,10 @@ export declare const UseSmileIDAnalyticsReasons: {
|
|
|
65
68
|
readonly outOfFrame: "out_of_frame";
|
|
66
69
|
readonly timeout: "timeout";
|
|
67
70
|
readonly qualityDropTimeout: "quality_drop_timeout";
|
|
68
|
-
readonly multipleFacesLost: "multiple_faces_lost";
|
|
69
71
|
readonly motionBlur: "motion_blur";
|
|
70
72
|
readonly notSmiling: "not_smiling";
|
|
71
73
|
};
|
|
74
|
+
/** Read an event back as a typed `UseSmileIDAnalyticsEventKind` with `analyticsEventKind(event)`. */
|
|
72
75
|
export type UseSmileIDAnalyticsEvent = {
|
|
73
76
|
readonly type: UseSmileIDAnalyticsEventType;
|
|
74
77
|
/** Flat string→string map. Always includes `session_id` and `timestamp`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export const UseSmileIDAnalyticsEventTypes={flowStarted:"flow_started",screenViewed:"screen_viewed",consentCaptured:"consent_captured",selfieCaptured:"selfie_captured",documentCaptured:"document_captured",retakeRequested:"retake_requested",jobSubmitted:"job_submitted",flowCompleted:"flow_completed",selfieSessionRestarted:"selfie_session_restarted",selfieFrameRejected:"selfie_frame_rejected"},UseSmileIDAnalyticsParamKeys={jobType:"job_type",screenName:"screen_name",sessionId:"session_id",timestamp:"timestamp",documentSide:"document_side",decision:"decision",livenessImageCount:"liveness_image_count",attempt:"attempt",jobId:"job_id",result:"result",errorMessage:"error_message",reason:"reason",framesDiscarded:"frames_discarded",durationMs:"duration_ms",iou:"iou",probability:"probability"},UseSmileIDAnalyticsScreenNames={consent:"consent",instructions:"instructions",capture:"capture",preview:"preview",processing:"processing"},UseSmileIDAnalyticsReasons={outOfFrame:"out_of_frame",timeout:"timeout",qualityDropTimeout:"quality_drop_timeout",
|
|
1
|
+
"use strict";export const UseSmileIDAnalyticsEventTypes={flowStarted:"flow_started",screenViewed:"screen_viewed",consentCaptured:"consent_captured",selfieCaptured:"selfie_captured",documentCaptured:"document_captured",retakeRequested:"retake_requested",jobSubmitted:"job_submitted",flowCompleted:"flow_completed",selfieSessionRestarted:"selfie_session_restarted",selfieFrameRejected:"selfie_frame_rejected"},UseSmileIDAnalyticsParamKeys={jobType:"job_type",jobTypeId:"job_type_id",screenName:"screen_name",sessionId:"session_id",timestamp:"timestamp",documentSide:"document_side",decision:"decision",livenessImageCount:"liveness_image_count",attempt:"attempt",jobId:"job_id",result:"result",errorMessage:"error_message",reason:"reason",framesDiscarded:"frames_discarded",durationMs:"duration_ms",iou:"iou",probability:"probability"},UseSmileIDAnalyticsScreenNames={consent:"consent",instructions:"instructions",capture:"capture",preview:"preview",processing:"processing"},UseSmileIDAnalyticsReasons={outOfFrame:"out_of_frame",timeout:"timeout",qualityDropTimeout:"quality_drop_timeout",motionBlur:"motion_blur",notSmiling:"not_smiling"};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The typed form of a `UseSmileIDAnalyticsEvent`: one case per event
|
|
3
|
+
* type, discriminated by `kind`, carrying that event's fields.
|
|
4
|
+
* `analyticsEventKind(event)` reads a delivered event back into it, so a
|
|
5
|
+
* `switch` matches typed cases instead of `type` strings. A minor release
|
|
6
|
+
* can add a case for a new event, so keep a `default` branch.
|
|
7
|
+
*
|
|
8
|
+
* The wire form is the cross-SDK contract in `analytics-contract.json`,
|
|
9
|
+
* identical in the Android, iOS, Flutter and React Native repos; the
|
|
10
|
+
* `kind` values are that contract's case names.
|
|
11
|
+
*/
|
|
12
|
+
import { DocumentSide } from "@smileid/usesmileid_bridge";
|
|
13
|
+
import { JobType } from "../../../core/models/job_type";
|
|
14
|
+
import { UseSmileIDAnalyticsScreenNames, type UseSmileIDAnalyticsEventType } from "./use_smile_id_analytics_event";
|
|
15
|
+
/** A `screen_name` value. */
|
|
16
|
+
export type UseSmileIDAnalyticsScreenName = (typeof UseSmileIDAnalyticsScreenNames)[keyof typeof UseSmileIDAnalyticsScreenNames];
|
|
17
|
+
export type UseSmileIDAnalyticsEventKind =
|
|
18
|
+
/** The flow started. */
|
|
19
|
+
{
|
|
20
|
+
readonly kind: "flowStarted";
|
|
21
|
+
readonly jobType: JobType;
|
|
22
|
+
}
|
|
23
|
+
/** A screen became active. */
|
|
24
|
+
| {
|
|
25
|
+
readonly kind: "screenViewed";
|
|
26
|
+
readonly screen: UseSmileIDAnalyticsScreenName;
|
|
27
|
+
}
|
|
28
|
+
/** The user granted consent on the consent screen. */
|
|
29
|
+
| {
|
|
30
|
+
readonly kind: "consentCaptured";
|
|
31
|
+
}
|
|
32
|
+
/** The selfie and its liveness frames were captured. */
|
|
33
|
+
| {
|
|
34
|
+
readonly kind: "selfieCaptured";
|
|
35
|
+
readonly livenessImageCount: number;
|
|
36
|
+
}
|
|
37
|
+
/** One document image was captured: one event per image. */
|
|
38
|
+
| {
|
|
39
|
+
readonly kind: "documentCaptured";
|
|
40
|
+
readonly side: DocumentSide;
|
|
41
|
+
}
|
|
42
|
+
/** The user went back to redo a step. */
|
|
43
|
+
| {
|
|
44
|
+
readonly kind: "retakeRequested";
|
|
45
|
+
}
|
|
46
|
+
/** A submission started; `attempt` counts from 1 across retries. */
|
|
47
|
+
| {
|
|
48
|
+
readonly kind: "jobSubmitted";
|
|
49
|
+
readonly jobType: JobType;
|
|
50
|
+
readonly attempt: number;
|
|
51
|
+
}
|
|
52
|
+
/** Smile ID accepted the submission; `jobId` is sent when it is known. */
|
|
53
|
+
| {
|
|
54
|
+
readonly kind: "flowCompletedSuccess";
|
|
55
|
+
readonly jobId?: string;
|
|
56
|
+
}
|
|
57
|
+
/** The flow failed. `errorMessage` is free-form developer text, never a stable identifier. */
|
|
58
|
+
| {
|
|
59
|
+
readonly kind: "flowCompletedFailure";
|
|
60
|
+
readonly errorMessage: string;
|
|
61
|
+
}
|
|
62
|
+
/** The selfie session restarted after its requirements stopped being met. */
|
|
63
|
+
| {
|
|
64
|
+
readonly kind: "selfieSessionRestarted";
|
|
65
|
+
readonly reason: string;
|
|
66
|
+
readonly framesDiscarded: number;
|
|
67
|
+
readonly durationMs: number;
|
|
68
|
+
}
|
|
69
|
+
/** A liveness frame was rejected. */
|
|
70
|
+
| {
|
|
71
|
+
readonly kind: "selfieFrameRejected";
|
|
72
|
+
readonly reason: string;
|
|
73
|
+
};
|
|
74
|
+
/** The wire form of a kind: its event `type` and its own `extras`, before `session_id` and `timestamp`. */
|
|
75
|
+
export interface UseSmileIDAnalyticsWireEvent {
|
|
76
|
+
readonly type: UseSmileIDAnalyticsEventType;
|
|
77
|
+
readonly extras: Readonly<Record<string, string>>;
|
|
78
|
+
}
|
|
79
|
+
/** Encodes a kind to its wire `type` and `extras`. */
|
|
80
|
+
export declare function encodeAnalyticsEventKind(event: UseSmileIDAnalyticsEventKind): UseSmileIDAnalyticsWireEvent;
|
|
81
|
+
/**
|
|
82
|
+
* Reads a delivered event back into its kind, or `undefined` when its
|
|
83
|
+
* `type` is not an event this SDK emits or its `extras` lack a field the
|
|
84
|
+
* event carries. Extra keys, such as `session_id`, are ignored.
|
|
85
|
+
*/
|
|
86
|
+
export declare function analyticsEventKind(event: {
|
|
87
|
+
readonly type: string;
|
|
88
|
+
readonly extras: Readonly<Record<string, string>>;
|
|
89
|
+
}): UseSmileIDAnalyticsEventKind | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{DocumentSide as l}from"@smileid/usesmileid_bridge";import{JobType as m}from"../../../core/models/job_type";import{UseSmileIDAnalyticsEventTypes as s,UseSmileIDAnalyticsParamKeys as n,UseSmileIDAnalyticsScreenNames as S}from"./use_smile_id_analytics_event";const o="granted",u="success",a="failure";function c(e){return{[n.jobType]:String(e),[n.jobTypeId]:String(e)}}export function encodeAnalyticsEventKind(e){const t=s;switch(e.kind){case"flowStarted":return{type:t.flowStarted,extras:c(e.jobType)};case"screenViewed":return{type:t.screenViewed,extras:{[n.screenName]:e.screen}};case"consentCaptured":return{type:t.consentCaptured,extras:{[n.decision]:o}};case"selfieCaptured":return{type:t.selfieCaptured,extras:{[n.livenessImageCount]:String(e.livenessImageCount)}};case"documentCaptured":return{type:t.documentCaptured,extras:{[n.documentSide]:e.side}};case"retakeRequested":return{type:t.retakeRequested,extras:{}};case"jobSubmitted":return{type:t.jobSubmitted,extras:{...c(e.jobType),[n.attempt]:String(e.attempt)}};case"flowCompletedSuccess":return{type:t.flowCompleted,extras:{[n.result]:u,...e.jobId?{[n.jobId]:e.jobId}:{}}};case"flowCompletedFailure":return{type:t.flowCompleted,extras:{[n.result]:a,[n.errorMessage]:e.errorMessage}};case"selfieSessionRestarted":return{type:t.selfieSessionRestarted,extras:{[n.reason]:e.reason,[n.framesDiscarded]:String(e.framesDiscarded),[n.durationMs]:String(e.durationMs)}};case"selfieFrameRejected":return{type:t.selfieFrameRejected,extras:{[n.reason]:e.reason}}}}function d(e){if(!(e===void 0||!/^-?\d+$/.test(e)))return Number(e)}function f(e){const t=d(e[n.jobTypeId])??d(e[n.jobType]);return Object.values(m).find(r=>r===t)}const y=Object.values(S),b=Object.values(l),p={[s.flowStarted]:e=>{const t=f(e);return t===void 0?void 0:{kind:"flowStarted",jobType:t}},[s.screenViewed]:e=>{const t=e[n.screenName];return t!==void 0&&y.includes(t)?{kind:"screenViewed",screen:t}:void 0},[s.consentCaptured]:e=>e[n.decision]===o?{kind:"consentCaptured"}:void 0,[s.selfieCaptured]:e=>{const t=d(e[n.livenessImageCount]);return t===void 0?void 0:{kind:"selfieCaptured",livenessImageCount:t}},[s.documentCaptured]:e=>{const t=e[n.documentSide];return t!==void 0&&b.includes(t)?{kind:"documentCaptured",side:t}:void 0},[s.retakeRequested]:()=>({kind:"retakeRequested"}),[s.jobSubmitted]:e=>{const t=f(e),r=d(e[n.attempt]);return t===void 0||r===void 0?void 0:{kind:"jobSubmitted",jobType:t,attempt:r}},[s.flowCompleted]:e=>{switch(e[n.result]){case u:{const t=e[n.jobId];return t?{kind:"flowCompletedSuccess",jobId:t}:{kind:"flowCompletedSuccess"}}case a:{const t=e[n.errorMessage];return t===void 0?void 0:{kind:"flowCompletedFailure",errorMessage:t}}default:return}},[s.selfieSessionRestarted]:e=>{const t=e[n.reason],r=d(e[n.framesDiscarded]),i=d(e[n.durationMs]);return t===void 0||r===void 0||i===void 0?void 0:{kind:"selfieSessionRestarted",reason:t,framesDiscarded:r,durationMs:i}},[s.selfieFrameRejected]:e=>{const t=e[n.reason];return t===void 0?void 0:{kind:"selfieFrameRejected",reason:t}}};export function analyticsEventKind(e){return Object.prototype.hasOwnProperty.call(p,e.type)?p[e.type]?.(e.extras):void 0}
|
|
@@ -96,8 +96,6 @@ export declare class DocumentCaptureConfigBuilder {
|
|
|
96
96
|
* gallery. Defaults to false.
|
|
97
97
|
*/
|
|
98
98
|
allowGalleryUpload: boolean;
|
|
99
|
-
/** When true, captures both the front and back of the document. Defaults to true. */
|
|
100
|
-
captureBothSides: boolean;
|
|
101
99
|
/** When true, lets the user skip capturing the back side. Defaults to false. */
|
|
102
100
|
allowSkipBack: boolean;
|
|
103
101
|
/**
|
|
@@ -105,6 +103,10 @@ export declare class DocumentCaptureConfigBuilder {
|
|
|
105
103
|
* dimensions are known in advance — e.g. CR-80 ID card = 1.586.
|
|
106
104
|
*/
|
|
107
105
|
knownIdAspectRatio?: number;
|
|
106
|
+
private _captureBothSides?;
|
|
107
|
+
/** Captures the back too; defaults to false for a passport, else true. */
|
|
108
|
+
get captureBothSides(): boolean;
|
|
109
|
+
set captureBothSides(value: boolean);
|
|
108
110
|
/** The `documentType`'s aspect ratio when present, otherwise `knownIdAspectRatio`. */
|
|
109
111
|
get resolvedAspectRatio(): number | undefined;
|
|
110
112
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{CaptureType as n}from"../../../../core/models/ml_config";import{DocumentCaptureMode as i}from"../../../../core/models/document_capture_mode";export{n as CaptureType};export class ConsentConfigBuilder{partnerName="";partnerIcon;partnerPrivacyPolicyUrl;onConsentGranted;allowButton;denyButton}export class InstructionsConfigBuilder{titlePrefix;titleEmphasis;heroAnimation;guidelinesSectionTitle;guidelines=[];showHeroOval=!0;continueButton}export class SelfieCaptureConfigBuilder{allowAgentMode=!1;enableEnhancedLiveness=!1}export class DocumentCaptureConfigBuilder{documentType;captureMode=i.AutoCaptureWithManualFallback();allowGalleryUpload=!1;
|
|
1
|
+
"use strict";import{CaptureType as n}from"../../../../core/models/ml_config";import{DocumentCaptureMode as i}from"../../../../core/models/document_capture_mode";export{n as CaptureType};export class ConsentConfigBuilder{partnerName="";partnerIcon;partnerPrivacyPolicyUrl;onConsentGranted;allowButton;denyButton}export class InstructionsConfigBuilder{titlePrefix;titleEmphasis;heroAnimation;guidelinesSectionTitle;guidelines=[];showHeroOval=!0;continueButton}export class SelfieCaptureConfigBuilder{allowAgentMode=!1;enableEnhancedLiveness=!1}export class DocumentCaptureConfigBuilder{documentType;captureMode=i.AutoCaptureWithManualFallback();allowGalleryUpload=!1;allowSkipBack=!1;knownIdAspectRatio;_captureBothSides;get captureBothSides(){return this._captureBothSides??this.documentType?.kind!=="passport"}set captureBothSides(e){this._captureBothSides=e}get resolvedAspectRatio(){return this.documentType?.knownAspectRatio??this.knownIdAspectRatio}}export class CaptureConfigBuilder{captureType;startButton;_selfie;_document;selfie(e){this._selfie||(this._selfie=new SelfieCaptureConfigBuilder),e(this._selfie)}document(e){this._document||(this._document=new DocumentCaptureConfigBuilder),e(this._document)}get selfieConfig(){return this._selfie}get documentConfig(){return this._document}}export class PreviewConfigBuilder{allowRetake=!0}export class ProcessingConfigBuilder{showProgressPercentage=!0;continueButton;retryButton;exitButton}export class ScreensBuilder{_steps=[];consent(e){const t=new ConsentConfigBuilder;e?.(t),this._steps.push({kind:"consent",config:t})}instructions(e){const t=new InstructionsConfigBuilder;e?.(t),this._steps.push({kind:"instructions",config:t})}capture(e){const t=new CaptureConfigBuilder;e(t),this._steps.push({kind:"capture",config:t})}preview(e){const t=new PreviewConfigBuilder;e?.(t),this._steps.push({kind:"preview",config:t})}processing(e){const t=new ProcessingConfigBuilder;e?.(t),this._steps.push({kind:"processing",config:t})}build(){return Object.freeze({steps:Object.freeze([...this._steps])})}}
|
|
@@ -2,7 +2,7 @@ import type { ConfigOptions } from "../../../core/models/config";
|
|
|
2
2
|
import type { ThemeConfiguration } from "./builder/theme_builder";
|
|
3
3
|
import type { NetworkConfig } from "../../../core/models/network_config";
|
|
4
4
|
import type { MLConfig } from "../../../core/models/ml_config";
|
|
5
|
-
import type { ConsentInformation, EnhancedKYCParams, BiometricKYCParams, DocumentVerificationParams, EnhancedDocumentVerificationParams, UserDetails } from "../../../core/models/params";
|
|
5
|
+
import type { ConsentInformation, EnhancedKYCParams, BiometricKYCParams, DocumentVerificationParams, EnhancedDocumentVerificationParams, ResidencyDocumentVerificationParams, UserDetails } from "../../../core/models/params";
|
|
6
6
|
import type { UseSmileIDAnalyticsEvent } from "../analytics/use_smile_id_analytics_event";
|
|
7
7
|
import type { JobSubmissionResponse } from "../../../data/model/job_submission_response";
|
|
8
8
|
import type { UseSmileIDResult } from "../../../core/models/result";
|
|
@@ -39,6 +39,11 @@ export declare class UseSmileIDFlowBuilder {
|
|
|
39
39
|
documentVerificationParams?: DocumentVerificationParams;
|
|
40
40
|
/** Optional. Required only for `JobType.enhancedDocumentVerification`. */
|
|
41
41
|
enhancedDocumentVerificationParams?: EnhancedDocumentVerificationParams;
|
|
42
|
+
/**
|
|
43
|
+
* Identity fields required for a Residency Document Verification job. Must be
|
|
44
|
+
* set when the network config specifies `JobType.residencyDocumentVerification`.
|
|
45
|
+
*/
|
|
46
|
+
residencyDocumentVerificationParams?: ResidencyDocumentVerificationParams;
|
|
42
47
|
/**
|
|
43
48
|
* Required for any job that submits the `user_details` multipart
|
|
44
49
|
* part — `JobType.enhancedKyc`, `JobType.biometricKyc`,
|
|
@@ -143,18 +148,8 @@ export declare class UseSmileIDFlowBuilder {
|
|
|
143
148
|
/**
|
|
144
149
|
* Runs a pre-flight check of the current builder configuration and
|
|
145
150
|
* returns a `ValidationState` describing any blocking issues, without
|
|
146
|
-
* throwing.
|
|
147
|
-
*
|
|
148
|
-
* builder can produce a flow.
|
|
149
|
-
*
|
|
150
|
-
* The full rule set (screen ordering, dependency rules, KYC and
|
|
151
|
-
* consent payload validation, etc.) runs inside `build()` and is
|
|
152
|
-
* reflected in the [FlowBuildResult] surface; partners that want
|
|
153
|
-
* the full picture during configuration should call
|
|
154
|
-
* `useSmileIDBuilder(...)` and pattern-match on the returned
|
|
155
|
-
* `result.kind`.
|
|
156
|
-
*
|
|
157
|
-
* @returns a `ValidationState` describing any blocking issues.
|
|
151
|
+
* throwing. Checks the screens and their own settings; job-level rules
|
|
152
|
+
* run only in `build()`.
|
|
158
153
|
*/
|
|
159
154
|
validate(): ValidationState;
|
|
160
155
|
/**
|
|
@@ -188,6 +183,14 @@ export declare class UseSmileIDFlowBuilder {
|
|
|
188
183
|
* `country` and `idType` are required.
|
|
189
184
|
*/
|
|
190
185
|
validateEnhancedDocumentVerificationParams(params: EnhancedDocumentVerificationParams): ValidationState;
|
|
186
|
+
/**
|
|
187
|
+
* Validates a `ResidencyDocumentVerificationParams` payload in
|
|
188
|
+
* isolation and returns a `ValidationState`, without throwing. Both
|
|
189
|
+
* `country` and `idType` are required, and `idType` must be `"PASSPORT"`,
|
|
190
|
+
* the one value the backend accepts, so this pre-check agrees with
|
|
191
|
+
* `build()`.
|
|
192
|
+
*/
|
|
193
|
+
validateResidencyDocumentVerificationParams(params: ResidencyDocumentVerificationParams): ValidationState;
|
|
191
194
|
/**
|
|
192
195
|
* Validates a `UserDetails` payload in isolation and returns a
|
|
193
196
|
* `ValidationState`, without throwing. Useful when the partner pulls
|
|
@@ -253,6 +256,7 @@ export type FlowConfigurationSnapshot = {
|
|
|
253
256
|
readonly biometricKYCParams?: BiometricKYCParams;
|
|
254
257
|
readonly documentVerificationParams?: DocumentVerificationParams;
|
|
255
258
|
readonly enhancedDocumentVerificationParams?: EnhancedDocumentVerificationParams;
|
|
259
|
+
readonly residencyDocumentVerificationParams?: ResidencyDocumentVerificationParams;
|
|
256
260
|
readonly userDetails?: UserDetails;
|
|
257
261
|
readonly userId?: string;
|
|
258
262
|
readonly config?: ConfigOptions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{UseSmileIDConfigBuilder as l}from"./builder/config_builder";import{ThemeConfigBuilder as
|
|
1
|
+
"use strict";import{RESIDENCY_ID_TYPE as d}from"../../../core/models/params";import{UseSmileIDConfigBuilder as l}from"./builder/config_builder";import{ThemeConfigBuilder as u}from"./builder/theme_builder";import{UseSmileIDNetworkBuilder as h}from"../../../data/dsl/use_smile_id_network_builder";import{UseSmileIDMLBuilder as f}from"./builder/ml_builder";import{ScreensBuilder as P}from"./builder/screens_builder";import{IssueSeverity as D}from"../../../core/exception/issue_severity";import{runBiometricKYCParamsValidation as V,runBuilderValidation as v,runConsentValidation as _,runDocumentVerificationParamsValidation as I,runEnhancedDocumentVerificationParamsValidation as g,runEnhancedKYCParamsValidation as w,runFlowValidation as y,runResidencyDocumentVerificationParamsValidation as C,runUserDetailsValidation as p}from"../validation/flow_validator";import{ValidationState as n}from"../validation/validation_state";import{residencyIdTypeIssue as Y}from"../validation/job_type_validator";import{decodeSmileIDToken as K}from"../../../data/util/use_smile_id_token_decoder";export class UseSmileIDFlowBuilder{consentInformation;enhancedKYCParams;biometricKYCParams;documentVerificationParams;enhancedDocumentVerificationParams;residencyDocumentVerificationParams;userDetails;userId;localizations;onResult=()=>{};onAnalyticsEvent;_config;_theme;_network;_ml;_screens;config(i){this._config||(this._config=new l),i(this._config)}theme(i){this._theme=new u,i(this._theme)}network(i){this._network=new h,i(this._network)}ml(i){this._ml||(this._ml=new f),i(this._ml)}screens(i){this._screens||(this._screens=new P),i(this._screens)}validate(){return n.invalid(v(this._screens?.build().steps??[]))}validateConsent(i){return n.invalid(_(i))}validateEnhancedKYCParams(i){return n.invalid(w(i))}validateBiometricKYCParams(i){return n.invalid(V(i))}validateDocumentVerificationParams(i){return n.invalid(I(i))}validateEnhancedDocumentVerificationParams(i){return n.invalid(g(i))}validateResidencyDocumentVerificationParams(i){const e=i.idType.trim()!==""&&i.idType!==d?[Y()]:[];return n.invalid([...C(i),...e])}validateUserDetails(i){return n.invalid(p(i))}build(){const i=this._screens?.build(),e=this._network?.build(),a=this._ml?.build(),s=this._config?.build(),m=k(s?.allowOfflineMode,e),o=y({steps:i?.steps??[],network:e,ml:a,consentInformation:this.consentInformation,enhancedKYCParams:this.enhancedKYCParams,biometricKYCParams:this.biometricKYCParams,documentVerificationParams:this.documentVerificationParams,enhancedDocumentVerificationParams:this.enhancedDocumentVerificationParams,residencyDocumentVerificationParams:this.residencyDocumentVerificationParams,userDetails:this.userDetails,userId:this.userId,tokenPayload:m}),c=n.invalid(o);if(!c.valid)return{kind:"invalid",validation:c};for(const t of o)t.severity===D.warning&&(console.warn(`[UseSmileIDFlowBuilder] ${t.message}`),t.suggestedFix&&console.warn(`[UseSmileIDFlowBuilder] Suggested fix: ${t.suggestedFix}`));if(!i)throw new Error("UseSmileIDFlowBuilder: internal invariant violated \u2014 screens missing after validation passed.");return{kind:"success",configuration:Object.freeze({...this.consentInformation?{consentInformation:this.consentInformation}:{},...this.enhancedKYCParams?{enhancedKYCParams:this.enhancedKYCParams}:{},...this.biometricKYCParams?{biometricKYCParams:this.biometricKYCParams}:{},...this.documentVerificationParams?{documentVerificationParams:this.documentVerificationParams}:{},...this.enhancedDocumentVerificationParams?{enhancedDocumentVerificationParams:this.enhancedDocumentVerificationParams}:{},...this.residencyDocumentVerificationParams?{residencyDocumentVerificationParams:this.residencyDocumentVerificationParams}:{},...this.userDetails?{userDetails:this.userDetails}:{},...this.userId?{userId:this.userId}:{},...s?{config:s}:{},...this._theme?{theme:this._theme.build()}:{},...e?{network:e}:{},...a?{ml:a}:{},screens:i,...this.localizations?{localizations:this.localizations}:{},onResult:this.onResult,...this.onAnalyticsEvent?{onAnalyticsEvent:this.onAnalyticsEvent}:{}})}}}function k(r,i){if(r)return;const e=i?.config?.token;if(!(!e||e.length===0))return K(e)?.tokenPayload}
|