@smileid/usesmileid 12.3.1-SNAPSHOT.69 → 12.3.1-SNAPSHOT.71
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/dist/data/api/bridge_submissions.js +1 -1
- package/dist/data/compression/payload_compression_runner.d.ts +3 -1
- package/dist/data/compression/payload_compression_runner.js +1 -1
- package/dist/data/compression/uri_compressible_asset.d.ts +30 -4
- package/dist/data/compression/uri_compressible_asset.js +1 -1
- package/dist/data/factory/networking_factory.d.ts +23 -11
- package/dist/data/factory/networking_factory.js +1 -1
- package/dist/data/repository/use_smile_id_repository_impl.js +1 -1
- package/dist/data/util/scratch_directory.d.ts +12 -0
- package/dist/data/util/scratch_directory.js +1 -0
- package/dist/metadata/use_smile_id_metadata.d.ts +2 -0
- package/dist/metadata/use_smile_id_metadata.js +1 -1
- package/dist/metadata/use_smile_id_metadata_factory.d.ts +15 -4
- package/dist/metadata/use_smile_id_metadata_factory.js +1 -1
- package/dist/presentation/flow/config/flow_configuration_camera.d.ts +0 -2
- package/dist/presentation/flow/config/flow_configuration_camera.js +1 -1
- package/dist/presentation/flow/config/flow_configuration_screens.d.ts +10 -0
- package/dist/presentation/flow/config/flow_configuration_screens.js +1 -0
- package/dist/presentation/flow/dsl/use_smile_id_builder.js +1 -1
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +18 -36
- package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +1 -1
- package/dist/presentation/flow/flow_navigation_graph.d.ts +4 -4
- package/dist/presentation/flow/flow_navigation_graph.js +1 -1
- package/dist/presentation/flow/flow_navigation_manager.js +1 -1
- package/dist/presentation/flow/flow_navigation_manager_routing.js +1 -1
- 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_partner_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/screen/use_smile_id_error_code_failure_message.d.ts +8 -0
- package/dist/presentation/flow/screen/use_smile_id_error_code_failure_message.js +1 -0
- package/dist/presentation/flow/screen/use_smile_id_orchestrated_screen_host.d.ts +14 -1
- package/dist/presentation/flow/screen/use_smile_id_orchestrated_screen_host.js +1 -1
- package/dist/presentation/flow/validation/flow_validator.d.ts +14 -16
- package/dist/presentation/flow/validation/flow_validator.js +1 -1
- package/dist/presentation/flow/validation/job_type_validator.d.ts +15 -15
- package/dist/presentation/flow/validation/job_type_validator.js +1 -1
- package/package.json +6 -6
- package/dist/presentation/flow/screen/helpers/failure_message.d.ts +0 -3
- package/dist/presentation/flow/screen/helpers/failure_message.js +0 -1
- package/dist/presentation/screen/helpers/processing_screen_helpers.d.ts +0 -12
- package/dist/presentation/screen/helpers/processing_screen_helpers.js +0 -1
- package/dist/presentation/screen/use_smile_id_permission_screen.d.ts +0 -15
- package/dist/presentation/screen/use_smile_id_permission_screen.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{
|
|
1
|
+
"use strict";import{File as S}from"expo-file-system";import{filePath as w}from"../util/file_path";import{withScratchDirectory as x}from"../util/scratch_directory";import{sourceSdkIdentifier as k}from"@smileid/usesmileid_bridge";import{NativeNetworkClient as b,NativeNetworkError as y,toNativeNetworkError as E}from"@smileid/usesmileid_bridge/internal";import{usesSandbox as C}from"./environment";import{testRoutePort as D}from"./test_route";import{LogLevel as d,RetryStrategy as m}from"../../core/models/network_config";import{decodeSecurityPolicyBitCode as M}from"../util/use_smile_id_token_decoder";import{NetworkServerException as T,NetworkTimeoutException as _,NetworkTransportException as v}from"../../core/exception/use_smile_id_network_exception";import{UseSmileIDErrorCode as c}from"../../core/exception/use_smile_id_error_code";const I="12.3.0";export class BridgeSubmissions{_client;_routed;constructor(t,n){this._client=t,this._routed=n}static configure(t,n=b.shared(),i=D(t)??null){const a=t.config?.logging,o=t.config?.onTokenExpired,{onRequest:s,onResponse:l}=t,h={...BridgeSubmissions.wireConfig(t),...i===null?{}:{testRoute:{port:i}}},N={onRequest:s?r=>s({method:String(r.method??""),url:String(r.url??""),headers:g(r.headers)}):void 0,onResponse:l?r=>l({method:String(r.method??""),url:String(r.url??""),status:Number(r.status??0),durationMillis:Number(r.durationMillis??0),headers:g(r.headers)}):void 0,onTokenExpired:o,onLog:a?.enabled&&a.level!==d.none?r=>console.log(`[UseSmileID] ${r}`):void 0};try{n.configure(h,N)}catch(r){throw E(r)}return new BridgeSubmissions(n,i!==null)}static wireConfig(t){const n=t.config,i=n?.token??"",a=t.timeouts,o=n?.logging;return{partnerId:n?.partnerConfig?.partnerId??"",environment:C(n?.partnerConfig)?"sandbox":"production",token:i,sourceSdk:k(),sourceSdkVersion:I,policy:M(i)??null,timeouts:{connectSeconds:a?.connect??60,readSeconds:a?.read??60,writeSeconds:a?.write??60,callSeconds:a?.call??120},retry:R(t.retry),cache:{enabled:t.cache?.enabled??!0,maxSizeBytes:t.cache?.maxSize??50*1024*1024},logging:{enabled:o?.enabled??!0,level:(o?.level??d.basic).toUpperCase(),redactHeaders:o?.redactHeaders??[]}}}warm(){this._routed||this._client.warm()}async submit(t,n){try{return await this._client.submit({path:t,parts:n.toWire()})}catch(i){throw p(i)}}async seal(t,n){return x("si_sealed",async i=>{const a=new S(i,"package.json");try{await this._client.seal({path:t,parts:n.toWire()},w(a.uri))}catch(s){throw p(s)}const o=JSON.parse(await a.text());return{headers:Object.fromEntries(Object.entries(o.headers??{}).map(([s,l])=>[s,String(l)])),encryptedPackage:o.encryptedPackage,packageNonce:o.packageNonce,packageTag:o.packageTag}})}}function p(e){if(!(e instanceof y))return e;const t=O.find(n=>n===e.code);return e.status!==void 0?new T({statusCode:e.status,message:e.message,cause:e,...t===c.Network.CallbackUrlRequired?{errorCode:t}:{}}):t===c.Network.Timeout?new _({message:e.message,cause:e}):new v({message:e.message,cause:e,errorCode:t??(e.retryable===!0?c.Network.ConnectionFailed:c.Network.RequestFailed)})}const O=Object.values(c.Network);export class BridgeSealed extends Error{sealed;constructor(t){super("Sealed by the native client"),this.sealed=t,this.name="BridgeSealed"}}function g(e){const t={};if(e===null||typeof e!="object")return t;for(const[n,i]of Object.entries(e))t[n]=Array.isArray(i)?i.map(String).join(", "):String(i);return t}function R(e){if(e===void 0||e.strategy===m.none)return{strategy:"none"};const t=Number.isNaN(e.maxAttempts)?1:Math.min(j,Math.max(1,Math.ceil(e.maxAttempts)));return e.strategy===m.fixed?{strategy:"fixed",maxAttempts:t,fixedDelaySeconds:u(e.fixedDelay)}:{strategy:"exponential",maxAttempts:t,initialDelaySeconds:u(e.initialDelay),multiplier:F(e),maxDelaySeconds:f(e),jitter:Number.isNaN(e.jitter)?0:Math.min(1,Math.max(0,e.jitter))}}const j=2**31-1,A=86400;function f(e){return e.maxDelay===Number.POSITIVE_INFINITY?A:u(e.maxDelay)}function u(e){return Number.isFinite(e)?Math.max(0,e):0}function F(e){const t=u(e.initialDelay);return t===0||Number.isNaN(e.multiplier)?1:Number.isFinite(e.multiplier)?Math.max(0,e.multiplier):e.multiplier>0?Math.max(1,f(e)/t):0}
|
|
@@ -18,7 +18,9 @@ export interface PayloadCompressionSizes {
|
|
|
18
18
|
* Builds `CompressibleAssets` from a submission's captured JPEG URIs and runs `PayloadCompressor`
|
|
19
19
|
* to bring the bundle under the transmission cap, re-stamping the Smile EXIF `image_digest` (and
|
|
20
20
|
* re-encoding natively) per step. Best-effort: it compresses each image down to its quality floor
|
|
21
|
-
* and resolves — it never throws on size, so submission is never blocked.
|
|
21
|
+
* and resolves — it never throws on size, so submission is never blocked. Each pass keeps the
|
|
22
|
+
* sources it re-encodes from in a scratch folder of its own under the cache directory
|
|
23
|
+
* (`withScratchDirectory`), which goes when the pass ends, whether the pass succeeded or threw.
|
|
22
24
|
*/
|
|
23
25
|
export declare const PayloadCompressionRunner: {
|
|
24
26
|
compress(args: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{File as
|
|
1
|
+
"use strict";import{File as l}from"expo-file-system";import{ImageType as t}from"@smileid/usesmileid_bridge";import{resolveCaptureQuality as d}from"../model/sdk_config_capture";import{withScratchDirectory as b}from"../util/scratch_directory";import{CompressibleAssets as p}from"./compressible_asset";import{PayloadCompressor as D}from"./payload_compressor";import{UriCompressibleAsset as v}from"./uri_compressible_asset";const M=1024*1024;function n(e){if(e===void 0)return;const i=new l(e).size??0;return i>0?i/M:void 0}export const PayloadCompressionRunner={async compress(e){const i=n(e.selfieImage),s=n(e.frontDocument),c=n(e.backDocument),r=n(e.visaDocument);return await b("si_compress",u=>{const o=(m,a)=>new v(m,a,d(e.sdkConfig,m),u),f=new p({selfie:e.selfieImage?o(t.Selfie,e.selfieImage):void 0,liveness:(e.livenessImages??[]).map(m=>o(t.Liveness,m)),documentFront:e.frontDocument?o(t.DocumentFront,e.frontDocument):void 0,documentBack:e.backDocument?o(t.DocumentBack,e.backDocument):void 0,documentVisa:e.visaDocument?o(t.DocumentVisa,e.visaDocument):void 0});return D.prepareBundleForTransmission(f,e.metadataBytes)}),{selfieOriginalMb:i,selfieFinalMb:n(e.selfieImage),frontOriginalMb:s,frontFinalMb:n(e.frontDocument),backOriginalMb:c,backFinalMb:n(e.backDocument),visaOriginalMb:r,visaFinalMb:n(e.visaDocument)}}};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Directory } from "expo-file-system";
|
|
1
2
|
import type { ImageTypeId } from "@smileid/usesmileid_bridge";
|
|
2
3
|
import type { CompressibleAsset } from "./compressible_asset";
|
|
3
4
|
/**
|
|
@@ -5,18 +6,43 @@ import type { CompressibleAsset } from "./compressible_asset";
|
|
|
5
6
|
*
|
|
6
7
|
* The re-encode is **native** (`expo-image-manipulator`, per the spec's "do not reimplement
|
|
7
8
|
* compression in JS" rule), and the Smile EXIF `image_digest` re-stamp is **native**
|
|
8
|
-
* (`SmileExifTagger.finalize`). The
|
|
9
|
-
*
|
|
9
|
+
* (`SmileExifTagger.finalize`). The first `recompress` copies the capture into `scratch`, the
|
|
10
|
+
* pass's own folder that `PayloadCompressionRunner` deletes when the pass ends, and every step
|
|
11
|
+
* re-encodes from that copy (no generational stacking; deterministic retries). Nothing is ever
|
|
12
|
+
* written beside the capture.
|
|
13
|
+
*
|
|
14
|
+
* The capture is replaced only by a re-encoded file that exists. When the replacement or the
|
|
15
|
+
* re-stamp fails, the source copy is put back at the capture's path, so the request always points
|
|
16
|
+
* at a readable image; those bytes keep their capture-time stamp. A step whose source copy fails
|
|
17
|
+
* leaves the capture as it was rather than re-encode it without a backup. Every step records the
|
|
18
|
+
* requested quality, whatever happened, so the compressor's loops always advance; an encoder or
|
|
19
|
+
* re-stamp failure still reaches the caller.
|
|
10
20
|
*/
|
|
11
21
|
export declare class UriCompressibleAsset implements CompressibleAsset {
|
|
12
22
|
readonly imageType: ImageTypeId;
|
|
13
23
|
private readonly uri;
|
|
24
|
+
private readonly scratch;
|
|
25
|
+
private static _nextSourceId;
|
|
14
26
|
private _quality;
|
|
15
27
|
private _size;
|
|
16
|
-
private
|
|
17
|
-
constructor(imageType: ImageTypeId, uri: string, captureQuality: number);
|
|
28
|
+
private _source;
|
|
29
|
+
constructor(imageType: ImageTypeId, uri: string, captureQuality: number, scratch: Directory);
|
|
18
30
|
encodedSize(): number;
|
|
19
31
|
quality(): number;
|
|
20
32
|
recompress(quality: number): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* The copy of the capture every step re-encodes from, made in `scratch` on the first step that
|
|
35
|
+
* finds the capture. `undefined` when there is no capture, or when the copy failed: the step
|
|
36
|
+
* then leaves the capture alone.
|
|
37
|
+
*/
|
|
38
|
+
private _retainedSource;
|
|
39
|
+
/**
|
|
40
|
+
* Moves `result` over the capture and answers true. On failure, puts the captured bytes back
|
|
41
|
+
* from `source`, deletes `result`, and answers false.
|
|
42
|
+
*/
|
|
43
|
+
private _replaceCapture;
|
|
44
|
+
/** Puts the captured bytes kept in `source` back at the capture's path. */
|
|
45
|
+
private _restoreCapture;
|
|
46
|
+
private static _deleteQuietly;
|
|
21
47
|
private static _fileSize;
|
|
22
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{File as
|
|
1
|
+
"use strict";import{File as i}from"expo-file-system";import{ImageManipulator as n,SaveFormat as u}from"expo-image-manipulator";import{SmileExifTagger as o}from"@smileid/usesmileid_bridge/exif/smile_exif_tagger";export class UriCompressibleAsset{imageType;uri;scratch;static _nextSourceId=0;_quality;_size;_source;constructor(t,e,r,a){this.imageType=t,this.uri=e,this.scratch=a,this._quality=Math.min(Math.max(r,0),1),this._size=UriCompressibleAsset._fileSize(e)}encodedSize(){return this._size}quality(){return this._quality}async recompress(t){try{const e=await this._retainedSource();if(e===void 0)return;const c=await(await n.manipulate(e.uri).renderAsync()).saveAsync({compress:Math.min(Math.max(t,0),1),format:u.JPEG});if(!await this._replaceCapture(new i(c.uri),e))return;try{await o.finalize(this.uri,this.imageType)}catch(s){throw await this._restoreCapture(e),s}}finally{this._quality=t,this._size=UriCompressibleAsset._fileSize(this.uri)}}async _retainedSource(){const t=new i(this.uri);if(!t.exists)return;if(this._source!==void 0)return this._source;const e=new i(this.scratch,`source_${UriCompressibleAsset._nextSourceId++}.jpg`);try{await t.copy(e)}catch{UriCompressibleAsset._deleteQuietly(e);return}return this._source=e,e}async _replaceCapture(t,e){try{if(!t.exists)return!1;const r=new i(this.uri);return r.exists&&r.delete(),await t.move(r),!0}catch{return await this._restoreCapture(e),UriCompressibleAsset._deleteQuietly(t),!1}}async _restoreCapture(t){try{const e=new i(this.uri);e.exists&&e.delete(),await t.copy(e)}catch{}}static _deleteQuietly(t){try{t.exists&&t.delete()}catch{}}static _fileSize(t){try{return new i(t).size??0}catch{return 0}}}
|
|
@@ -3,15 +3,7 @@ import { SmileIDAPI } from "../api/smile_id_api";
|
|
|
3
3
|
import type { NativeNetworkClient } from "@smileid/usesmileid_bridge/internal";
|
|
4
4
|
import { BridgeSubmissions } from "../api/bridge_submissions";
|
|
5
5
|
import type { MetadataProvider } from "../../metadata/use_smile_id_metadata_factory";
|
|
6
|
-
|
|
7
|
-
* Validates `config` and returns a `SmileIDAPI` on the native network client, which signs,
|
|
8
|
-
* attests, retries and sends each job.
|
|
9
|
-
*
|
|
10
|
-
* With `capturing`, the client is the one a skipped submission seals its request through: the
|
|
11
|
-
* native client signs and seals each job instead of sending it.
|
|
12
|
-
*/
|
|
13
|
-
export declare function makeNetworkingFactory(config: NetworkConfig, options?: {
|
|
14
|
-
readonly capturing?: boolean;
|
|
6
|
+
type NetworkingFactoryOptions = {
|
|
15
7
|
/** The native client, the shared one unless a test hands over a fake. */
|
|
16
8
|
readonly nativeClient?: NativeNetworkClient;
|
|
17
9
|
/**
|
|
@@ -19,11 +11,31 @@ export declare function makeNetworkingFactory(config: NetworkConfig, options?: {
|
|
|
19
11
|
* seals through the same one rather than configure the shared client again.
|
|
20
12
|
*/
|
|
21
13
|
readonly bridge?: BridgeSubmissions;
|
|
22
|
-
}
|
|
14
|
+
};
|
|
15
|
+
/** A capture client: the API that seals each job, and the native client it seals through. */
|
|
16
|
+
type CapturingNetworking = {
|
|
23
17
|
readonly api: SmileIDAPI;
|
|
24
|
-
readonly metadata: MetadataProvider;
|
|
25
18
|
/** The native client this flow's requests go through. */
|
|
26
19
|
readonly bridge: BridgeSubmissions;
|
|
27
20
|
};
|
|
21
|
+
/** A submission client, with the flow's one metadata provider. */
|
|
22
|
+
type SubmittingNetworking = CapturingNetworking & {
|
|
23
|
+
readonly metadata: MetadataProvider;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Validates `config` and returns a `SmileIDAPI` on the native network client, which signs,
|
|
27
|
+
* attests, retries and sends each job, with the flow's metadata provider.
|
|
28
|
+
*
|
|
29
|
+
* With `capturing`, the client is the one a skipped submission seals its request through: the
|
|
30
|
+
* native client signs and seals each job instead of sending it. It builds no metadata provider,
|
|
31
|
+
* since the flow's submission client already owns the one whose warm-up runs once per flow.
|
|
32
|
+
*/
|
|
33
|
+
export declare function makeNetworkingFactory(config: NetworkConfig, options?: NetworkingFactoryOptions & {
|
|
34
|
+
readonly capturing?: false;
|
|
35
|
+
}): SubmittingNetworking;
|
|
36
|
+
export declare function makeNetworkingFactory(config: NetworkConfig, options: NetworkingFactoryOptions & {
|
|
37
|
+
readonly capturing: true;
|
|
38
|
+
}): CapturingNetworking;
|
|
28
39
|
/** Mirrors the server's resolver: blank, a callback ID, or an absolute http(s) URL with a host. */
|
|
29
40
|
export declare function isResolvableCallbackUrl(value: string): boolean;
|
|
41
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{SmileIDAPI as
|
|
1
|
+
"use strict";import{SmileIDAPI as u}from"../api/smile_id_api";import{Attestation as l}from"@smileid/usesmileid_bridge";import{BridgeSubmissions as m}from"../api/bridge_submissions";import{testRoutePort as f}from"../api/test_route";import{MissingPartnerConfigException as n}from"../exception/missing_partner_config_exception";import{UseSmileIDMetadataFactory as p}from"../../metadata/use_smile_id_metadata_factory";export function makeNetworkingFactory(t,e={}){const r=t.config?.partnerConfig;if(!r||r.partnerId.trim()==="")throw n.missingPartnerId();if(!t.config?.token||t.config.token.trim()==="")throw n.missingAuthToken();const i=/[\u0000-\u001F\u007F-\u009F]/;if(i.test(r.partnerId))throw n.controlCharacterPartnerId();if(i.test(t.config.token))throw n.controlCharacterAuthToken();if(!isResolvableCallbackUrl(r.callbackUrl??""))throw n.unresolvableCallbackUrl();const o=e.capturing===!0,c=f(t)??null,s=e.bridge??m.configure(t,e.nativeClient,c),a=new u(r,s,o);return o?{api:a,bridge:s}:(l.warmUp().catch(()=>{}),{api:a,metadata:new p,bridge:s})}const h=/^callback_[0-9a-hjkmnp-tv-z]{26}$/;export function isResolvableCallbackUrl(t){if(t===""||h.test(t))return!0;const e=t.indexOf("://");if(e<=0)return!1;const r=t.slice(0,e).toLowerCase();if(r!=="http"&&r!=="https")return!1;const o=((t.slice(e+3).split(/[/?#]/)[0]??"").split("@").pop()??"").split(":")[0]??"";return o!==""&&!/[\s\u0000-\u001F\u007F-\u009F]/.test(o)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{failure as
|
|
1
|
+
"use strict";import{failure as c,success as l}from"../../core/models/result";import{JobType as t}from"../../core/models/job_type";import{InvalidFieldValueException as f}from"../../core/exception/job_validation_exceptions";import{UseSmileIDException as d}from"../../core/exception/use_smile_id_exception";import{NetworkUnknownException as o}from"../../core/exception/use_smile_id_network_exception";import{PayloadCompressionRunner as m}from"../compression/payload_compression_runner";import{BridgeSealed as _}from"../api/bridge_submissions";function p(i){const e=[],n=(a,s)=>{s!==void 0&&s>0&&e.push({name:a,value:String(s)})};return n("selfie_original_size",i.selfieOriginalMb),n("selfie_final_compressed_size",i.selfieFinalMb),n("document_front_original_size",i.frontOriginalMb),n("document_front_final_compressed_size",i.frontFinalMb),n("document_back_original_size",i.backOriginalMb),n("document_back_final_compressed_size",i.backFinalMb),n("document_visa_original_size",i.visaOriginalMb),n("document_visa_final_compressed_size",i.visaFinalMb),e}export class UseSmileIDRepositoryImpl{_api;_captureApi;constructor(e,n){this._api=e,this._captureApi=n}async seal(e,n){if(e.jobType===t.bvn)return c(new f({fieldName:"jobType",reason:"BVN submission is not yet supported"}));const a=this._captureApi;if(!a)return c(new o({message:"No capture client to seal the request through"}));const s=await this._compressed(e,n);try{return await UseSmileIDRepositoryImpl._send(a,s),c(new o({message:"The capture client sent the request instead of sealing it"}))}catch(r){return r instanceof _?l(r.sealed):c(u(r))}}async submit(e,n){if(e.jobType===t.bvn)return c(new f({fieldName:"jobType",reason:"BVN submission is not yet supported"}));const a=await this._compressed(e,n);try{return l(await UseSmileIDRepositoryImpl._send(this._api,a))}catch(s){return c(u(s))}}async _compressed(e,n){try{const a=await this._compressPayload(e,n),s=a?p(a):[];if(s.length>0)return{...e,metadata:e.metadata.withAdditionalEntries(s)}}catch{}return e}async _compressPayload(e,n){const a=JSON.stringify(e.metadata.toEntries()).length;switch(e.jobType){case t.smartSelfieEnrollment:case t.smartSelfieAuthentication:case t.biometricKyc:return m.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,metadataBytes:a,sdkConfig:n});case t.documentVerification:case t.enhancedDocumentVerification:return m.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,backDocument:e.backDocument,metadataBytes:a,sdkConfig:n});case t.residencyDocumentVerification:return m.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,visaDocument:e.visaDocument,metadataBytes:a,sdkConfig:n});default:return}}static _send(e,n){switch(n.jobType){case t.smartSelfieEnrollment:return e.smartSelfieEnrollment(n);case t.smartSelfieAuthentication:return e.smartSelfieAuthentication(n);case t.enhancedKyc:return e.enhancedKyc(n);case t.biometricKyc:return e.biometricKyc(n);case t.documentVerification:return e.documentVerification(n);case t.enhancedDocumentVerification:return e.enhancedDocumentVerification(n);case t.residencyDocumentVerification:return e.residencyDocumentVerification(n)}}}function u(i){return i instanceof d?i:new o({message:i instanceof Error?i.message:String(i),cause:i})}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Directory } from "expo-file-system";
|
|
2
|
+
/**
|
|
3
|
+
* Runs `body` with a new folder of its own under the cache directory, named
|
|
4
|
+
* `<prefix>_<time>_<random>`, and deletes the folder once `body` settles, whether it resolved or
|
|
5
|
+
* threw. A failed delete does not replace `body`'s result or error: the OS purges the cache
|
|
6
|
+
* directory.
|
|
7
|
+
*
|
|
8
|
+
* A process killed while `body` runs never reaches that delete. So the first folder a JS runtime
|
|
9
|
+
* makes for `prefix` first deletes every `<prefix>_` folder in the cache directory: none of them can
|
|
10
|
+
* belong to this runtime yet, so only an earlier one's leftovers go.
|
|
11
|
+
*/
|
|
12
|
+
export declare function withScratchDirectory<T>(prefix: string, body: (directory: Directory) => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{Directory as c,Paths as r}from"expo-file-system";const a=new Set;export async function withScratchDirectory(t,e){o(t);const n=new c(r.cache,`${t}_${Date.now()}_${Math.random().toString(36).slice(2)}`);n.create({intermediates:!0});try{return await e(n)}finally{i(n)}}function o(t){if(!a.has(t)){a.add(t);try{for(const e of r.cache.list())e instanceof c&&e.name.startsWith(`${t}_`)&&i(e)}catch{}}}function i(t){try{t.exists&&t.delete()}catch{}}
|
|
@@ -84,6 +84,8 @@ export type UseSmileIDMetadataFields = {
|
|
|
84
84
|
readonly geolocation?: Geolocation;
|
|
85
85
|
/** Obfuscated device security-state signal (computed natively) → `privileges`. Omitted when undefined/blank. */
|
|
86
86
|
readonly privileges?: string;
|
|
87
|
+
/** Sealed runtime report (computed natively) → `runtime_integrity`, verbatim. Omitted when undefined/blank. */
|
|
88
|
+
readonly runtimeIntegrity?: string;
|
|
87
89
|
/** kamera's device tier (`entry`, `standard` or `high`, design 61) → `device_tier`. Omitted when undefined/blank. */
|
|
88
90
|
readonly deviceTier?: string;
|
|
89
91
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export class UseSmileIDMetadata{_fields;_extraEntries;constructor(e,i=[]){this._fields=e,this._extraEntries=i}toFields(){return this._fields}withAdditionalEntries(e){return new UseSmileIDMetadata(this._fields,[...this._extraEntries,...e])}toEntries(){const e=this._fields,i=[];if(i.push({name:"sdk_version",value:e.sdkVersion}),i.push({name:"sdk",value:e.sdk}),i.push({name:"device_os",value:e.deviceOs}),i.push({name:"device_model",value:e.deviceModel}),i.push({name:"app_name",value:e.appName}),i.push({name:"session_id",value:e.sessionId}),e.proxy!==void 0&&i.push({name:"proxy",value:String(e.proxy)}),e.proximitySensor!==void 0&&i.push({name:"proximity_sensor",value:String(e.proximitySensor)}),
|
|
1
|
+
"use strict";export class UseSmileIDMetadata{_fields;_extraEntries;constructor(e,i=[]){this._fields=e,this._extraEntries=i}toFields(){return this._fields}withAdditionalEntries(e){return new UseSmileIDMetadata(this._fields,[...this._extraEntries,...e])}toEntries(){const e=this._fields,i=[];if(i.push({name:"sdk_version",value:e.sdkVersion}),i.push({name:"sdk",value:e.sdk}),i.push({name:"device_os",value:e.deviceOs}),i.push({name:"device_model",value:e.deviceModel}),i.push({name:"app_name",value:e.appName}),i.push({name:"session_id",value:e.sessionId}),e.proxy!==void 0&&i.push({name:"proxy",value:String(e.proxy)}),e.proximitySensor!==void 0&&i.push({name:"proximity_sensor",value:String(e.proximitySensor)}),t(i,"client_ip",e.clientIp),t(i,"network_connection",e.networkConnection),t(i,"carrier_info",e.carrierInfo),t(i,"timezone",e.timezone),t(i,"locale",e.locale),t(i,"screen_resolution",e.screenResolution),t(i,"device_orientation",e.deviceOrientation),t(i,"system_architecture",e.systemArchitecture),t(i,"host_application",e.hostApplication),r(i,"memory_info",e.memoryInfoMb),r(i,"number_of_cameras",e.numberOfCameras),t(i,"device_tier",e.deviceTier??""),t(i,"device_profile",e.deviceProfile??""),i.push({name:"security_policy_version",value:e.securityPolicyVersion}),e.hardwareAttestationSecurityLevel!==void 0){i.push({name:"supports_hardware_attestation",value:String(e.hardwareAttestationSecurityLevel)});const s=e.attestationCertificateChain;s&&s.length>0&&i.push({name:"attestation_certificate_chain",value:JSON.stringify(s)})}return e.supportsAppAttest!==void 0&&i.push({name:"supports_app_attest",value:String(e.supportsAppAttest)}),i.push({name:"sdk_config_source",value:e.sdkConfigSource}),e.imageWidth>0&&e.imageHeight>0&&i.push({name:"selfie_image_dimensions",value:`${e.imageWidth}x${e.imageHeight}`}),e.livenessImageWidth>0&&e.livenessImageHeight>0&&i.push({name:"liveness_image_dimensions",value:`${e.livenessImageWidth}x${e.livenessImageHeight}`}),e.timezoneOffsetMinutes!==void 0&&i.push({name:"timezone_offset",value:String(e.timezoneOffsetMinutes)}),t(i,"build_brand",e.buildBrand??""),t(i,"build_device",e.buildDevice??""),t(i,"build_fingerprint",e.buildFingerprint??""),t(i,"build_product",e.buildProduct??""),t(i,"build_hardware",e.buildHardware??""),t(i,"build_source",e.buildSource??""),t(i,"package_name",e.packageName??""),t(i,"fingerprint",e.fingerprint??""),t(i,"local_time_of_enrolment",e.localTimeOfEnrolment??""),e.batteryStatus!==void 0&&i.push({name:"battery_status",value:String(e.batteryStatus)}),e.vpn!==void 0&&i.push({name:"vpn",value:String(e.vpn)}),e.certificateDigest!==void 0&&e.certificateDigest.length>0&&i.push({name:"certificate_digest",value:JSON.stringify(e.certificateDigest)}),t(i,"wrapper_name",e.wrapperName??""),t(i,"wrapper_version",e.wrapperVersion??""),e.sdkLaunchCount!==void 0&&e.sdkLaunchCount>0&&i.push({name:"sdk_launch_count",value:String(e.sdkLaunchCount)}),e.geolocation!==void 0&&i.push({name:"geolocation",value:JSON.stringify(e.geolocation)}),e.privileges!==void 0&&e.privileges.length>0&&i.push({name:"privileges",value:e.privileges}),e.runtimeIntegrity!==void 0&&e.runtimeIntegrity.length>0&&i.push({name:"runtime_integrity",value:e.runtimeIntegrity}),i.push(...this._extraEntries),Object.freeze(i)}}function t(n,e,i){i&&i.length>0&&n.push({name:e,value:i})}function r(n,e,i){i!==0&&n.push({name:e,value:String(i)})}
|
|
@@ -53,11 +53,13 @@ export declare class UseSmileIDMetadataFactory implements MetadataProvider {
|
|
|
53
53
|
private _sdkLaunchCount?;
|
|
54
54
|
private _geolocation?;
|
|
55
55
|
private _privileges?;
|
|
56
|
+
private _runtimeIntegrity?;
|
|
56
57
|
private _deviceIntegrity?;
|
|
57
58
|
private _clientIp;
|
|
58
59
|
private _networkConnection;
|
|
59
60
|
private _hardwareAttestation?;
|
|
60
61
|
private _deviceProfile?;
|
|
62
|
+
private readonly _warmup;
|
|
61
63
|
constructor(options?: MetadataFactoryOptions);
|
|
62
64
|
/**
|
|
63
65
|
* Resolve kamera's device profile into the cache. kamera says it never rejects (a device it
|
|
@@ -73,13 +75,16 @@ export declare class UseSmileIDMetadataFactory implements MetadataProvider {
|
|
|
73
75
|
*/
|
|
74
76
|
get sessionId(): string;
|
|
75
77
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* `await` it explicitly when they want to guarantee populated
|
|
79
|
-
* network fields on the first submission.
|
|
78
|
+
* Settles once the warm-up the constructor started has resolved the async fields. It never
|
|
79
|
+
* starts another, so awaiting it neither reruns the native probes nor counts another launch.
|
|
80
80
|
*/
|
|
81
81
|
warmup(): Promise<void>;
|
|
82
82
|
private _warmupAsync;
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the async `expo-network` fields (`network_connection`, `client_ip`) into the cache.
|
|
85
|
+
* Fail-open: leaves the cached empty strings when the module is absent or a call rejects.
|
|
86
|
+
*/
|
|
87
|
+
private _warmUpNetwork;
|
|
83
88
|
/**
|
|
84
89
|
* Resolve the last-known location (expo-location) into the cache — only when a permission
|
|
85
90
|
* is already granted (never prompts). Fail-open: leaves `_geolocation` unset otherwise.
|
|
@@ -90,6 +95,12 @@ export declare class UseSmileIDMetadataFactory implements MetadataProvider {
|
|
|
90
95
|
* Fail-open: `PrivilegesProvider` returns `"{}"` when the native module is unavailable.
|
|
91
96
|
*/
|
|
92
97
|
private _warmUpPrivileges;
|
|
98
|
+
/**
|
|
99
|
+
* Resolve the sealed runtime report from the native bridge into the cache. Fail-open:
|
|
100
|
+
* `RuntimeIntegrityProvider` returns undefined when the device can't seal it or the native
|
|
101
|
+
* module is absent.
|
|
102
|
+
*/
|
|
103
|
+
private _warmUpRuntimeIntegrity;
|
|
93
104
|
/**
|
|
94
105
|
* Resolve the native device-integrity signals (fingerprint, package name, vpn, build
|
|
95
106
|
* hardware, install source, cert digests) from the bridge into the cache. Fail-open:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{KameraKit as I}from"@smileid/kamera";import{Dimensions as d,Platform as r}from"react-native";import{Attestation as b,DeviceIntegrityProvider as S,PrivilegesProvider as A}from"@smileid/usesmileid_bridge";import{
|
|
1
|
+
"use strict";import{KameraKit as I}from"@smileid/kamera";import{Dimensions as d,Platform as r}from"react-native";import{Attestation as b,DeviceIntegrityProvider as S,PrivilegesProvider as A}from"@smileid/usesmileid_bridge";import{RuntimeIntegrityProvider as D}from"@smileid/usesmileid_bridge/internal";import{UseSmileIDMetadata as O}from"./use_smile_id_metadata";import{loadAsyncStorage as C,loadExpoBattery as P,loadExpoCellular as U,loadExpoDevice as l,loadExpoNetwork as M}from"./optional_native_modules";import{resolveGeolocation as k}from"./geolocation";import{resolveDimensions as u}from"../data/model/sdk_config_capture";import{SdkConfigSource as h}from"../data/model/sdk_config_source";import{CameraDefaults as o}from"../camera/core/camera_defaults";const m="12.3.0",N="12.0.0",x="react-native-expo",p="com.usesmileid.metadata.sdk_launch_count";export class UseSmileIDMetadataFactory{_sessionId;_deviceOs;_deviceModel;_appName;_hostApplication;_systemArchitecture;_memoryInfoMb;_buildBrand;_buildDevice;_buildFingerprint;_buildProduct;_carrierInfo;_batteryStatus;_sdkLaunchCount;_geolocation;_privileges;_runtimeIntegrity;_deviceIntegrity;_clientIp="";_networkConnection="";_hardwareAttestation;_deviceProfile;_warmup;constructor(t={}){this._sessionId=generateSessionId(),this._deviceOs=R(),this._appName=t.appName??"",this._hostApplication=t.hostApplication??"";const e=l();e?(this._deviceModel=resolveDeviceModel(e),this._systemArchitecture=e.supportedCpuArchitectures?.[0]?.trim()??"",this._memoryInfoMb=typeof e.totalMemory=="number"&&e.totalMemory>0?Math.round(e.totalMemory/(1024*1024)):0,this._buildBrand=e.brand?.trim()??"",this._buildDevice=(r.OS==="ios"?e.modelId:e.designName)?.trim()??"",this._buildFingerprint=e.osBuildFingerprint?.trim()??"",this._buildProduct=e.productName?.trim()??""):(this._deviceModel="",this._systemArchitecture="",this._memoryInfoMb=0,this._buildBrand="",this._buildDevice="",this._buildFingerprint="",this._buildProduct=""),this._carrierInfo=U()?.carrier?.trim()??"",this._warmup=this._warmupAsync(),this._warmUpHardwareAttestation(),this._warmUpDeviceProfile()}async _warmUpDeviceProfile(){try{this._deviceProfile=await I.deviceProfile()}catch{}}get sessionId(){return this._sessionId}warmup(){return this._warmup}async _warmupAsync(){await Promise.allSettled([this._warmUpNetwork(),this._warmUpBatteryStatus(),this._warmUpLaunchCount(),this._warmUpGeolocation(),this._warmUpPrivileges(),this._warmUpDeviceIntegrity()]),await this._warmUpRuntimeIntegrity()}async _warmUpNetwork(){const t=M();if(!t)return;const e=t.getNetworkStateAsync().then(s=>{this._networkConnection=(s.type??"").toString()}).catch(()=>{}),n=t.getIpAddressAsync?t.getIpAddressAsync().then(s=>{this._clientIp=s??""}).catch(()=>{}):Promise.resolve();await Promise.all([e,n])}async _warmUpGeolocation(){const t=await k();t&&(this._geolocation=t)}async _warmUpPrivileges(){const t=await A.privileges();t.length>0&&(this._privileges=t)}async _warmUpRuntimeIntegrity(){const t=await D.runtimeIntegrity();t!==void 0&&(this._runtimeIntegrity=t)}async _warmUpDeviceIntegrity(){this._deviceIntegrity=await S.signals()}async _warmUpLaunchCount(){const t=C();if(t)try{const n=(Number(await t.getItem(p))||0)+1;await t.setItem(p,String(n)),this._sdkLaunchCount=n}catch{}}async _warmUpBatteryStatus(){const t=P();if(t)try{switch(await t.getBatteryStateAsync()){case 1:this._batteryStatus=0;break;case 2:case 3:this._batteryStatus=1;break;default:this._batteryStatus=-1}}catch{}}async _warmUpHardwareAttestation(){try{this._hardwareAttestation=await b.generateHardwareAttestation(this._sessionId)}catch{}}make(t){const e=this._hardwareAttestation,n=r.OS==="android"&&e?{hardwareAttestationSecurityLevel:e.securityLevel,...e.certificateChain&&e.certificateChain.length>0?{attestationCertificateChain:e.certificateChain}:{}}:{},s=r.OS==="ios"&&e?{supportsAppAttest:e.supportsAppAttest}:{},a=u(t?.selfie),_=t!==void 0?h.sdkConfig:h.defaultSource,f=a?.width??o.selfieJpegTargetWidth,g=a?.height??o.selfieJpegTargetHeight,c=u(t?.liveness),y=c?.width??o.selfieJpegTargetWidth,v=c?.height??o.selfieJpegTargetHeight,w={sdkVersion:m,sdk:x,deviceOs:this._deviceOs,deviceModel:this._deviceModel,appName:this._appName,sessionId:this._sessionId,clientIp:this._clientIp,networkConnection:this._networkConnection,carrierInfo:this._carrierInfo,timezone:B(),timezoneOffsetMinutes:E(),locale:H(),screenResolution:F(),memoryInfoMb:this._memoryInfoMb,deviceOrientation:V(),systemArchitecture:this._systemArchitecture,numberOfCameras:0,hostApplication:this._hostApplication,securityPolicyVersion:N,...n,...s,sdkConfigSource:_,imageWidth:f,imageHeight:g,livenessImageWidth:y,livenessImageHeight:v,buildBrand:this._buildBrand,buildDevice:this._buildDevice,buildFingerprint:this._buildFingerprint,buildProduct:this._buildProduct,buildHardware:this._deviceIntegrity?.buildHardware??"",buildSource:this._deviceIntegrity?.buildSource??"",packageName:this._deviceIntegrity?.packageName??"",fingerprint:this._deviceIntegrity?.fingerprint??"",localTimeOfEnrolment:L(),wrapperName:"react_native_expo",wrapperVersion:m,...this._deviceIntegrity?.vpn!==void 0?{vpn:this._deviceIntegrity.vpn}:{},...this._deviceIntegrity?.certificateDigest!==void 0?{certificateDigest:this._deviceIntegrity.certificateDigest}:{},...this._batteryStatus!==void 0?{batteryStatus:this._batteryStatus}:{},...this._sdkLaunchCount!==void 0?{sdkLaunchCount:this._sdkLaunchCount}:{},...this._geolocation!==void 0?{geolocation:this._geolocation}:{},...this._privileges!==void 0?{privileges:this._privileges}:{},...this._runtimeIntegrity!==void 0?{runtimeIntegrity:this._runtimeIntegrity}:{},...this._deviceProfile!==void 0?{deviceTier:this._deviceProfile.tier,deviceProfile:deviceProfileMetadataJson(this._deviceProfile)}:{}};return new O(w)}}export function deviceProfileMetadataJson(i){const t={lowRamDevice:i.lowRamDevice,maxCpuFreqKHz:i.maxCpuFreqKHz,mediaPerformanceClass:i.mediaPerformanceClass,modelIdentifier:i.modelIdentifier,possibleCores:i.possibleCores,socModel:i.socModel,tier:i.tier,totalRamBytes:i.totalRamBytes};return JSON.stringify(Object.fromEntries(Object.entries(t).filter(([,e])=>e!==null)))}export function resolveDeviceModel(i=l()){if(!i)return"";const t=i.brand?.trim()??"",e=i.modelName?.trim()??"";return[t,e].filter(Boolean).join(" ")}function R(){const i=r.OS==="ios"?"iOS":r.OS==="android"?"Android":r.OS,t=T();return t?`${i} ${t}`:i}function T(){if(r.OS==="android"){const i=r.constants?.Release;if(i!=null&&`${i}`!="")return`${i}`}return typeof r.Version<"u"?String(r.Version):""}function L(){try{return new Date().toISOString()}catch{return""}}function B(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone??""}catch{return""}}function E(){return-new Date().getTimezoneOffset()}function H(){try{return Intl.DateTimeFormat().resolvedOptions().locale??""}catch{return""}}function F(){try{const{width:i,height:t,scale:e}=d.get("screen");if(!i||!t)return"";const n=Math.round(i*(e||1)),s=Math.round(t*(e||1));return`${n} \xD7 ${s}`}catch{return""}}function V(){try{const{width:i,height:t}=d.get("window");return!i||!t?"":t>=i?"Portrait":"Landscape"}catch{return""}}export function generateSessionId(){const i=globalThis.crypto;if(i&&typeof i.randomUUID=="function")return i.randomUUID();const t=()=>typeof i?.getRandomValues=="function"?i.getRandomValues(new Uint16Array(1))[0]??0:Math.floor(Math.random()*65536),e=()=>t().toString(16).padStart(4,"0");return[e()+e(),e(),"4"+e().slice(1),(parseInt(e(),16)&16383|32768).toString(16),e()+e()+e()].join("-")}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type { FlowConfiguration } from "./flow_configuration";
|
|
2
|
-
/** Whether a step of `configuration` captures with the camera, selfie or document. */
|
|
3
|
-
export declare function opensCamera(configuration: FlowConfiguration): boolean;
|
|
4
2
|
/**
|
|
5
3
|
* Starts the camera stack's one-off set-up now when a step of `configuration` captures, so the
|
|
6
4
|
* capture screen's camera doesn't wait for it. Opens no camera, needs no permission, and kamera's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{KameraSession as
|
|
1
|
+
"use strict";import{KameraSession as a}from"@smileid/kamera";import{hasCaptureScreen as m}from"./flow_configuration_screens";export function prewarmCameraIfNeeded(e,r=()=>a.prewarm()){m(e.screens.steps)&&r()}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CaptureConfigBuilder, ScreenSpec } from "../dsl/screen_builders";
|
|
2
|
+
export declare function hasConsentScreen(steps: readonly ScreenSpec[]): boolean;
|
|
3
|
+
export declare function hasProcessingScreen(steps: readonly ScreenSpec[]): boolean;
|
|
4
|
+
/** Whether a step of the flow captures with the camera, selfie or document. */
|
|
5
|
+
export declare function hasCaptureScreen(steps: readonly ScreenSpec[]): boolean;
|
|
6
|
+
export declare function hasDocumentCaptureScreen(steps: readonly ScreenSpec[]): boolean;
|
|
7
|
+
export declare function resolveSelfieCaptures(steps: readonly ScreenSpec[]): readonly CaptureConfigBuilder[];
|
|
8
|
+
export declare function resolveDocumentCaptures(steps: readonly ScreenSpec[]): readonly CaptureConfigBuilder[];
|
|
9
|
+
/** The SDK's own screens, without the partner's, in the order the flow declares them. */
|
|
10
|
+
export declare function resolveSdkScreens(steps: readonly ScreenSpec[]): readonly ScreenSpec[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{CaptureType as t}from"../../../core/models/ml_config";export function hasConsentScreen(e){return e.some(n=>n.kind==="consent")}export function hasProcessingScreen(e){return e.some(n=>n.kind==="processing")}export function hasCaptureScreen(e){return e.some(n=>n.kind==="capture")}export function hasDocumentCaptureScreen(e){return resolveDocumentCaptures(e).length>0}export function resolveSelfieCaptures(e){return o(e,t.selfie)}export function resolveDocumentCaptures(e){return o(e,t.document)}export function resolveSdkScreens(e){return e.filter(n=>n.kind!=="partner")}function o(e,n){return e.flatMap(r=>r.kind==="capture"&&r.config.captureType===n?[r.config]:[])}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as r}from"react/jsx-runtime";import{useMemo as
|
|
1
|
+
"use strict";import{jsx as r}from"react/jsx-runtime";import{useMemo as u}from"react";import{FlowInvalidDispatcher as t,FlowSuccessRenderer as s}from"../use_smile_id_flow";import{UseSmileIDFlowBuilder as n}from"./use_smile_id_flow_builder";export function UseSmileIDBuilder({builder:i}){const{result:e,invalidMeta:l}=u(()=>{const o=new n;return i(o),{result:o.build(),invalidMeta:{onResult:o.onResult,enableDebugMode:o.isDebugModeEnabled}}},[]);return e.kind==="invalid"?r(t,{result:e,onResult:l.onResult,enableDebugMode:l.enableDebugMode}):r(s,{configuration:e.configuration})}export function useSmileIDBuilder(i){const e=new n;return i(e),e.build()}export{UseSmileIDFlowBuilder}from"./use_smile_id_flow_builder";
|
|
@@ -145,13 +145,16 @@ export declare class UseSmileIDFlowBuilder {
|
|
|
145
145
|
* flow list in declaration order.
|
|
146
146
|
*/
|
|
147
147
|
screens(block: (s: ScreensBuilder) => void): void;
|
|
148
|
+
/** Returns the `ValidationState` `build()` would, without building. */
|
|
149
|
+
validate(): ValidationState;
|
|
148
150
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
151
|
+
* Whether `enableDebugMode` is on in the current config block; `false`
|
|
152
|
+
* until a config block sets it. `<UseSmileIDBuilder>` reads it to decide
|
|
153
|
+
* whether an invalid flow also renders the debug screen.
|
|
154
|
+
*
|
|
155
|
+
* @internal
|
|
153
156
|
*/
|
|
154
|
-
|
|
157
|
+
get isDebugModeEnabled(): boolean;
|
|
155
158
|
/**
|
|
156
159
|
* Validates a `ConsentInformation` payload in isolation and returns a
|
|
157
160
|
* `ValidationState`, without throwing. Useful when the payload comes
|
|
@@ -194,45 +197,24 @@ export declare class UseSmileIDFlowBuilder {
|
|
|
194
197
|
/**
|
|
195
198
|
* Snapshot the current builder state and report the validation
|
|
196
199
|
* outcome as a [FlowBuildResult]. Returns `{kind: 'success', ...}`
|
|
197
|
-
* when
|
|
198
|
-
*
|
|
199
|
-
* `enableDebugMode` flag so the wrapper can route the failure
|
|
200
|
-
* through `onResult.failure` and decide whether to render the
|
|
201
|
-
* debug screen.
|
|
200
|
+
* when no issue is an error, logging each warning, or
|
|
201
|
+
* `{kind: 'invalid', ...}` carrying every issue, warnings included.
|
|
202
202
|
*
|
|
203
203
|
* Internal — partners use `<UseSmileIDBuilder>`, which calls this
|
|
204
204
|
* method and dispatches on `kind` (success → render flow, invalid →
|
|
205
205
|
* fire `onResult.failure(BuilderValidationException)` and optionally
|
|
206
|
-
* render the debug screen).
|
|
207
|
-
*
|
|
208
|
-
* Validation rules (see `validate()` for non-throwing access from
|
|
209
|
-
* inside a builder closure):
|
|
210
|
-
* 1. The screens list must not be empty.
|
|
211
|
-
* 2. Screen ordering: consent must be first if present; processing
|
|
212
|
-
* must be last if present; instructions must come before any
|
|
213
|
-
* capture; every preview must be preceded by a capture.
|
|
214
|
-
* 3. Duplicate screen types are rejected (capture is exempt — a
|
|
215
|
-
* flow may have both selfie and document capture).
|
|
216
|
-
* 4. ML configuration is required when any capture screen is
|
|
217
|
-
* present.
|
|
218
|
-
* 5. Network configuration is required when a processing screen
|
|
219
|
-
* is present.
|
|
220
|
-
* 6. The configured `jobType` may not be `unknown` once a
|
|
221
|
-
* processing screen is wired.
|
|
222
|
-
* 7. Pre-supplied `consentInformation` and an in-flow consent
|
|
223
|
-
* screen are mutually exclusive — exactly one source.
|
|
224
|
-
* 8. When set, `consentInformation` must have `granted: true`,
|
|
225
|
-
* non-blank `grantedAt`, `noticeLanguage`, and
|
|
226
|
-
* `noticePrivacyPolicyUrl`, and `grantedAt` must not parse to
|
|
227
|
-
* a future timestamp.
|
|
228
|
-
* 9. When set, `EnhancedKYCParams` and `BiometricKYCParams` must
|
|
229
|
-
* have non-blank `idType`, `idNumber`, `country`, and a
|
|
230
|
-
* `userDetails` whose `givenNames` and `lastName` are non-blank
|
|
231
|
-
* and whose `email` or `phoneNumber` (at least one) is set.
|
|
206
|
+
* render the debug screen). `validate()` reports the same issues
|
|
207
|
+
* without building.
|
|
232
208
|
*
|
|
233
209
|
* @internal
|
|
234
210
|
*/
|
|
235
211
|
build(): FlowBuildResult;
|
|
212
|
+
/**
|
|
213
|
+
* The snapshot this builder describes, with every issue it has, in one
|
|
214
|
+
* pass and before the severity gate. The snapshot is `undefined` until a
|
|
215
|
+
* `screens` block runs.
|
|
216
|
+
*/
|
|
217
|
+
private verdict;
|
|
236
218
|
}
|
|
237
219
|
/**
|
|
238
220
|
* Frozen snapshot produced by `UseSmileIDFlowBuilder.build()`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{
|
|
1
|
+
"use strict";import{UseSmileIDConfigBuilder as c}from"../config/use_smile_id_config_builder";import{ThemeConfigBuilder as m}from"./theme_config_builder";import{UseSmileIDNetworkBuilder as d}from"../../../data/dsl/use_smile_id_network_builder";import{UseSmileIDMLBuilder as l}from"../../../bridge/dsl/use_smile_id_ml_builder";import{ScreensBuilder as u}from"./screen_builders";import{IssueSeverity as h}from"../../../core/exception/issue_severity";import{runBiometricKYCParamsValidation as f,runConsentValidation as P,runDocumentVerificationParamsValidation as D,runEnhancedDocumentVerificationParamsValidation as V,runEnhancedKYCParamsValidation as v,runFlowValidation as g,runResidencyDocumentVerificationParamsValidation as _}from"../validation/flow_validator";import{ValidationState as t}from"../validation/validation_state";import{decodeSmileIDToken as w}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 c),i(this._config)}theme(i){this._theme=new m,i(this._theme)}network(i){this._network=new d,i(this._network)}ml(i){this._ml||(this._ml=new l),i(this._ml)}screens(i){this._screens||(this._screens=new u),i(this._screens)}validate(){return t.invalid(this.verdict().issues)}get isDebugModeEnabled(){return this._config?.build().enableDebugMode===!0}validateConsent(i){return t.invalid(P(i))}validateEnhancedKYCParams(i){return t.invalid(v(i))}validateBiometricKYCParams(i){return t.invalid(f(i))}validateDocumentVerificationParams(i){return t.invalid(D(i))}validateEnhancedDocumentVerificationParams(i){return t.invalid(V(i))}validateResidencyDocumentVerificationParams(i){return t.invalid(_(i))}build(){const{configuration:i,issues:e}=this.verdict(),s=t.invalid(e);if(!s.valid)return{kind:"invalid",validation:s};for(const n of e)n.severity===h.warning&&(console.warn(`[UseSmileIDFlowBuilder] ${n.message}`),n.suggestedFix&&console.warn(`[UseSmileIDFlowBuilder] Suggested fix: ${n.suggestedFix}`));if(!i)throw new Error("UseSmileIDFlowBuilder: internal invariant violated \u2014 screens missing after validation passed.");return{kind:"success",configuration:i}}verdict(){const i=this._screens?.build(),e=this._network?.build(),s=this._ml?.build(),n=this._config?.build(),o=C(n?.allowOfflineMode,e),r=g({steps:i?.steps??[],network:e,ml:s,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:o,skipApiSubmission:n?.skipApiSubmission});return i?{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}:{},...n?{config:n}:{},...this._theme?{theme:this._theme.build()}:{},...e?{network:e}:{},...s?{ml:s}:{},screens:i,...this.localizations?{localizations:this.localizations}:{},onResult:this.onResult,...this.onAnalyticsEvent?{onAnalyticsEvent:this.onAnalyticsEvent}:{}}),issues:r}:{configuration:void 0,issues:r}}}function C(a,i){if(a)return;const e=i?.config?.token;if(!(!e||e.length===0))return w(e)?.tokenPayload}
|
|
@@ -9,8 +9,8 @@ export declare const FlowFact: {
|
|
|
9
9
|
};
|
|
10
10
|
/** A fact the flow's routing reads. */
|
|
11
11
|
export type FlowFact = (typeof FlowFact)[keyof typeof FlowFact];
|
|
12
|
-
/** The busy reason while a skip-submission hand-over
|
|
13
|
-
export declare const
|
|
12
|
+
/** The busy reason while a job is being submitted, or sealed for a skip-submission hand-over. */
|
|
13
|
+
export declare const SUBMITTING = "submitting";
|
|
14
14
|
/** How the flow's step kinds and facts are named on the wire. */
|
|
15
15
|
export declare const flowCodec: NjiaCodec<string, FlowFact>;
|
|
16
16
|
/** The flow's screens as a njia graph, with each step resolving back to the screen it came from. */
|
|
@@ -23,8 +23,8 @@ export type FlowGraph = {
|
|
|
23
23
|
* The screens as a graph. Steps are named `kind#ordinal`, so two equal screens, such as a document
|
|
24
24
|
* and a selfie `preview {}`, are two steps. A document capture, with the preview that follows it, is a
|
|
25
25
|
* segment that repeats while {@link FlowFact.supportingSidePending} holds, which captures and previews
|
|
26
|
-
* the back or visa; skipping the back leaves the segment. Back is refused while a
|
|
27
|
-
*
|
|
26
|
+
* the back or visa; skipping the back leaves the segment. Back is refused while a job is being
|
|
27
|
+
* submitted ({@link SUBMITTING}) and once the job is the partner's ({@link FlowFact.jobHandedOver}).
|
|
28
28
|
*/
|
|
29
29
|
export declare function toFlowGraph(screens: readonly ScreenSpec[]): FlowGraph;
|
|
30
30
|
/** The graph's step for the partner screen added with `id`: `partner.<id>`, outside the SDK's own steps. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{allOf as
|
|
1
|
+
"use strict";import{allOf as u,guard as c,njiaCodec as m,njiaGraph as w,njiaSegment as S,njiaStep as f,stringWire as b,wireOfStrings as j}from"@smileid/njia";import{CaptureType as k}from"../../core/models/ml_config";export const FlowFact={supportingSidePending:"supportingSidePending",jobHandedOver:"jobHandedOver"},SUBMITTING="submitting",flowCodec=m(b,j(Object.values(FlowFact)));export function toFlowGraph(e){const d=new Map,i=new Map,p=t=>{if(t.kind==="partner"){const a=f(partnerStepId(t.config.id),"partner",{partner:!0});return i.set(a.id,t),a}const n=v(t),r=d.get(n)??0;d.set(n,r+1);const o=f(`${n}#${r}`,n);return i.set(o.id,t),o},s=[];let l=0;for(let t=0;t<e.length;){const n=e[t];if(g(n)){const r=e[t+1]?.kind==="preview"?e[t+1]:void 0,o=[p(n),...r?[p(r)]:[]];s.push(S(`document#${l++}`,o,{repeatWhile:u(FlowFact.supportingSidePending)})),t+=o.length}else s.push(p(n)),t+=1}const h=[c("back","submission under way",{whenBusy:[SUBMITTING]}),c("back","the files are the partner's",{condition:u(FlowFact.jobHandedOver)})];return{graph:w(s,{guards:h}),screenOf:t=>{const n=i.get(t);if(n===void 0)throw new Error(`no screen for step ${t}`);return n}}}const g=e=>e.kind==="capture"&&e.config.captureType===k.document;function v(e){return e.kind==="capture"?g(e)?"capture.document":"capture.selfie":e.kind}export const partnerStepId=e=>`partner.${e}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{File as g}from"expo-file-system";import{deleteImportedDocument as f}from"../../camera/core/imported_document_folder";import{JobType as k}from"../../core/models/job_type";import{CaptureType as d}from"../../core/models/ml_config";import{defaultFrameAspectRatio as v}from"./config/document_type";import{UseSmileIDMetadataFactory as C}from"../../metadata/use_smile_id_metadata_factory";import{decodeSmileIDToken as S}from"../../data/util/use_smile_id_token_decoder";import{isTokenConsentComplete as y}from"../../data/model/token_payload";import{UseSmileIDCrashReporting as w}from"@smileid/usesmileid_bridge";import{answerOf as b,NjiaException as P,NjiaIntents as h,NjiaKit as T,top as c}from"@smileid/njia";import{getNextScreen as D,nextIntent as F,sendFromTop as p,supportingDocumentSide as I,documentPreviewFollowsCapture as j}from"./flow_navigation_manager_routing";import{flowCodec as E,partnerStepId as x,toFlowGraph as N}from"./flow_navigation_graph";import{NavigationFailedException as O}from"../../core/exception/navigation_failed_exception";import{flowNavigationModule as R}from"./flow_navigation_engine";import{currentDocumentCaptureSide as B,currentDocumentPreviewBorderFraction as G,currentDocumentPreviewPath as M,currentPreviewIsDocument as A,documentBorderFraction as W,documentCapturesBack as J,recordDocumentImageOrigin as L,retakeCurrentDocumentSide as Q,setCapturedBackDocument as V,setCapturedFrontDocument as U,setCapturedVisaDocument as K,skipDocumentBack as q}from"./flow_navigation_manager_documents";import{logAnalyticsEvent as z,logKind as a,recordDocumentCaptureTelemetry as H,recordDocumentQualityWaiver as X,recordSelfieCaptureTelemetry as Y,screenViewedEvent as l}from"./flow_navigation_manager_analytics";import{beginSubmissionIfNeeded as Z,retrySubmission as $,submit as ee}from"./flow_navigation_manager_submission";import{markCapturedTake as te}from"../components/helpers/captured_image_uri";function _(u){return[u.frontDocumentPath,u.backDocumentPath,u.visaDocumentPath].filter(e=>e!==void 0)}export class FlowNavigationManager{_navigationPath=[];_flowGraph;_engine;_navigationStartFailure;_captured={livenessPaths:[]};_documentBackSkipped=!1;_documentBorderFractions=new Map;_documentImageOrigins=new Map;_selfieCaptureTelemetry;_documentCaptureTelemetry=new Map;_documentQualityWaivers=new Map;_cameraName;_devicePositions=[];_deviceMovementDetected=null;_selfieCaptureTakes=1;_selfieRetakeCounted=!1;_selfieCaptureStarted=!1;_jobSubmissionState={kind:"notAttempted"};_listeners=new Set;_steps;_configuration;_repository;_metadataProvider;_onExit;_disposed=!1;_submissionResponse;_capturedJob;_flowEnded=!1;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=y(t?.tokenPayload?.consent)?i.filter(s=>s.kind!=="consent"):i,this._flowGraph=N(this._steps),this._engine=this._startEngine();const o=this._navigationPath[0];a(this,{kind:"flowStarted",jobType:this._jobType()}),o&&a(this,l(this,o))}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===d.document)return e.config.documentConfig?.documentType}get instructionsDocumentType(){const e=this.currentScreen,t=e?this._steps.indexOf(e)+1:0,i=this._steps.slice(t).find(o=>o.kind==="capture");return i?.kind==="capture"&&i.config.captureType===d.document?i.config.documentConfig?.documentType:void 0}get documentFrameAspectRatio(){for(const e of this._steps)if(e.kind==="capture"&&e.config.captureType===d.document){const t=e.config.documentConfig;return t?.resolvedAspectRatio??v(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(){return D(this)}navigateToNext(){this._moveOn(e=>F(this,e))}navigateToNextWith(e){this._moveOn(t=>h.answer(t.id,e))}partnerValue(e){const t=this._engine;return t?b(t.state,x(e)):void 0}_moveOn(e){if(this._disposed)return;const t=this._engine,i=t&&c(t.state)?p(this,e):void 0;if(t&&c(t.state)&&i===void 0)return;const o=i===void 0||i.effects.some(r=>r.kind==="pastEnd"),s=i?.effects.filter(r=>r.kind==="entered").at(-1);if(!o&&(s===void 0||!("entry"in s)))return;const n=s&&"entry"in s?this._flowGraph.screenOf(s.entry.step):void 0;if(!n){const r=this._jobSubmissionState;if(this._submissionResponse){this._finishFlow({kind:"completed",job:this._submissionResponse});return}if(r.kind==="failure"){this._finishFlow({kind:"failed",error:r.error});return}const m=r.kind==="success"?r.response:{userId:"",jobId:"",status:"",message:""};this._finishFlow({kind:"completed",job:m});return}a(this,l(this,n)),n.kind==="processing"&&this._jobSubmissionState.kind==="notAttempted"&&this._repository!==void 0&&(this._jobSubmissionState={kind:"loading"}),this._notify(),n.kind==="processing"&&this._jobSubmissionState.kind==="loading"&&this._repository!==void 0&&setTimeout(()=>{this._disposed||ee(this)},0)}navigateBack(){if(this._disposed)return;const e=this._engine;if(!e||!c(e.state)){this._finishFlow({kind:"cancelled"});return}const t=this.currentScreen,i=t?.kind,o=t?.kind==="capture"&&t.config.captureType===d.selfie&&this._selfieCaptureStarted,s=p(this,n=>h.back(n.id));if(s!==void 0){if(s.effects.some(n=>n.kind==="finished")){this._finishFlow({kind:"cancelled"});return}if(s.effects.some(n=>n.kind==="left")){(i==="capture"||i==="preview")&&a(this,{kind:"retakeRequested"});const n=this.currentScreen,r=n?.kind==="capture"&&n.config.captureType===d.selfie&&this._captured.selfiePath!==void 0;(o||r)&&(this._selfieCaptureTakes+=1),r&&(this._selfieRetakeCounted=!0),i==="capture"&&(this._selfieCaptureStarted=!1),this._notify()}}}navigateToStart(){if(this._disposed)return;const e=this._navigationPath;this._engine?.dispose(),this._engine=this._startEngine();const t=this._navigationPath;if(e.length!==t.length||e.some((i,o)=>i!==t[o])){const i=this._captured.selfiePath!==void 0;(this._selfieCaptureStarted||i&&!this._selfieRetakeCounted)&&(this._selfieCaptureTakes+=1),this._selfieRetakeCounted=i,this._selfieCaptureStarted=!1}this.deliverNavigationStartFailure(),this._notify()}_startEngine(){if(this._navigationStartFailure=void 0,this._navigationPath=[],this._flowGraph.graph.nodes.length===0)return;const e=R();let t;try{t=T.start(E,this._flowGraph.graph,e?{module:e}:{})}catch(i){if(!(i instanceof P))throw i;this._navigationStartFailure=i;return}return this._navigationPath=this._pathOf(t.state),t.addListener(i=>{this._navigationPath=this._pathOf(i.state)}),t}deliverNavigationStartFailure(){this._navigationStartFailure!==void 0&&this.failNavigation(this._navigationStartFailure)}failNavigation(e){if(!this._disposed){if(this._flowEnded){w.captureException(e);return}this._finishFlow({kind:"failed",error:new O({cause:e})})}}_pathOf(e){return e.entries.map(t=>this._flowGraph.screenOf(t.step))}exit(e={kind:"cancelled"}){this._disposed||e.kind!=="captured"&&this._finishFlow(e)}get captured(){return this._captured}get capturedJob(){return this._capturedJob}setCapturedSelfie(e,t){this._disposed||(te(e),this._captured={...this._captured,selfiePath:e,livenessPaths:Object.freeze([...t])},this._selfieRetakeCounted=!1,a(this,{kind:"selfieCaptured",livenessImageCount:t.length}),this._notify())}get selfieCaptureGeometry(){return this._selfieCaptureGeometry}_selfieCaptureGeometry;recordSelfieCaptureGeometry(e){e&&(this._selfieCaptureGeometry=e)}setCapturedFrontDocument(e,t=0){U(this,e,t)}setCapturedBackDocument(e,t=0){V(this,e,t)}supportingDocumentSide(){return I(this)}documentCapturesBack(){return J(this)}currentDocumentCaptureSide(){return B(this)}setCapturedVisaDocument(e,t=0){K(this,e,t)}documentPreviewFollowsCapture(){return j(this)}currentPreviewIsDocument(){return A(this)}currentDocumentPreviewPath(){return M(this)}documentBorderFraction(e){return W(this,e)}currentDocumentPreviewBorderFraction(){return G(this)}retakeCurrentDocumentSide(){Q(this)}skipDocumentBack(){q(this)}recordDocumentImageOrigin(e,t){L(this,e,t)}recordSelfieCaptureTelemetry(e){Y(this,e)}recordSelfieCaptureStarted(){this._disposed||(this._selfieCaptureStarted=!0)}recordSelfieUploadCheckRestart(){const e=this.currentScreen;this._disposed||e?.kind!=="capture"||e.config.captureType!==d.selfie||(this._selfieCaptureTakes+=1,this._selfieCaptureStarted=!1)}get selfieCaptureTakes(){return this._selfieCaptureTakes}recordDocumentCaptureTelemetry(e,t){H(this,e,t)}recordDocumentQualityWaiver(e,t){X(this,e,t)}documentQualityWaived(e){return this._documentQualityWaivers.get(e)}setConsent(e){this._disposed||(this._captured={...this._captured,consentInformation:e},a(this,{kind:"consentCaptured"}),this._notify())}get jobSubmissionState(){return this._jobSubmissionState}beginSubmissionIfNeeded(){Z(this)}retrySubmission(){$(this)}_setCaptured(e){const t=new Set(_(e));for(const i of _(this._captured))t.has(i)||f(i);this._captured=e}_deleteCapturedFiles(){if(this._configuration.config?.allowOfflineMode)return;const{selfiePath:e,livenessPaths:t,frontDocumentPath:i,backDocumentPath:o,visaDocumentPath:s}=this._captured;for(const n of[e,...t,i,o,s])if(n){try{const r=new g(n);r.exists&&r.delete()}catch{}f(n)}}_finishFlow(e){this._capturedJob!==void 0&&e.kind!=="captured"||(this._flowEnded=!0,e.kind!=="captured"&&this._deleteCapturedFiles(),e.kind==="completed"?a(this,e.job.jobId?{kind:"flowCompletedSuccess",jobId:e.job.jobId}:{kind:"flowCompletedSuccess"}):e.kind==="failed"&&a(this,{kind:"flowCompletedFailure",errorMessage:e.error instanceof Error?e.error.message:String(e.error)}),e.kind==="captured"&&a(this,{kind:"flowCompletedCaptured"}),this._onExit(e))}logAnalyticsEvent(e){z(this,e)}_jobType(){return this._configuration.network?.config?.jobType??k.unknown}subscribe(e){return this._listeners.add(e),()=>{this._listeners.delete(e)}}dispose(){this._disposed||(this._disposed=!0,this._listeners.clear(),this._engine?.dispose(),this._engine=void 0)}static _decodeToken(e){if(e.config?.allowOfflineMode)return;const t=e.network?.config?.token;if(!(!t||t.length===0))return S(t)}_notify(){for(const e of this._listeners)try{e()}catch{}}}
|
|
1
|
+
"use strict";import{File as k}from"expo-file-system";import{deleteImportedDocument as h}from"../../camera/core/imported_document_folder";import{JobType as v}from"../../core/models/job_type";import{CaptureType as u}from"../../core/models/ml_config";import{defaultFrameAspectRatio as C}from"./config/document_type";import{UseSmileIDMetadataFactory as S}from"../../metadata/use_smile_id_metadata_factory";import{decodeSmileIDToken as y}from"../../data/util/use_smile_id_token_decoder";import{isTokenConsentComplete as w}from"../../data/model/token_payload";import{UseSmileIDCrashReporting as b}from"@smileid/usesmileid_bridge";import{answerOf as P,NjiaException as T,NjiaIntents as p,NjiaKit as D,top as f}from"@smileid/njia";import{getNextScreen as F,nextIntent as I,sendFromTop as l,supportingDocumentSide as j,documentPreviewFollowsCapture as E}from"./flow_navigation_manager_routing";import{flowCodec as x,partnerStepId as N,toFlowGraph as O}from"./flow_navigation_graph";import{NavigationFailedException as R}from"../../core/exception/navigation_failed_exception";import{flowNavigationModule as B}from"./flow_navigation_engine";import{currentDocumentCaptureSide as G,currentDocumentPreviewBorderFraction as M,currentDocumentPreviewPath as A,currentPreviewIsDocument as W,documentBorderFraction as J,documentCapturesBack as L,recordDocumentImageOrigin as Q,retakeCurrentDocumentSide as V,setCapturedBackDocument as K,setCapturedFrontDocument as U,setCapturedVisaDocument as q,skipDocumentBack as z}from"./flow_navigation_manager_documents";import{logAnalyticsEvent as H,logKind as o,recordDocumentCaptureTelemetry as X,recordDocumentQualityWaiver as Y,recordSelfieCaptureTelemetry as Z,screenViewedEvent as m}from"./flow_navigation_manager_analytics";import{beginSubmissionIfNeeded as $,retrySubmission as ee,submit as te}from"./flow_navigation_manager_submission";import{markCapturedTake as ie}from"../components/helpers/captured_image_uri";function _(c){return[c.frontDocumentPath,c.backDocumentPath,c.visaDocumentPath].filter(e=>e!==void 0)}export class FlowNavigationManager{_navigationPath=[];_flowGraph;_engine;_navigationStartFailure;_captured={livenessPaths:[]};_documentBackSkipped=!1;_documentBorderFractions=new Map;_documentImageOrigins=new Map;_selfieCaptureTelemetry;_documentCaptureTelemetry=new Map;_documentQualityWaivers=new Map;_cameraName;_devicePositions=[];_deviceMovementDetected=null;_selfieCaptureTakes=1;_selfieRetakeCounted=!1;_selfieCaptureStarted=!1;_jobSubmissionState={kind:"notAttempted"};_listeners=new Set;_steps;_configuration;_repository;_metadataProvider;_onExit;_disposed=!1;_submissionResponse;_capturedJob;_flowEnded=!1;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 S,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=w(t?.tokenPayload?.consent)?i.filter(r=>r.kind!=="consent"):i,this._flowGraph=O(this._steps),this._engine=this._startEngine();const s=this._navigationPath[0];o(this,{kind:"flowStarted",jobType:this._jobType()}),s&&o(this,m(this,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===u.document)return e.config.documentConfig?.documentType}get instructionsDocumentType(){const e=this.currentScreen,t=e?this._steps.indexOf(e)+1:0,i=this._steps.slice(t).find(s=>s.kind==="capture");return i?.kind==="capture"&&i.config.captureType===u.document?i.config.documentConfig?.documentType:void 0}get documentFrameAspectRatio(){for(const e of this._steps)if(e.kind==="capture"&&e.config.captureType===u.document){const t=e.config.documentConfig;return t?.resolvedAspectRatio??C(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(){return F(this)}navigateToNext(){this._moveOn(e=>I(this,e))}navigateToNextWith(e){this._moveOn(t=>p.answer(t.id,e))}partnerValue(e){const t=this._engine;return t?P(t.state,N(e)):void 0}_moveOn(e){if(this._disposed)return;const t=this._engine,i=t&&f(t.state)?l(this,e):void 0;if(t&&f(t.state)&&i===void 0)return;const s=i===void 0||i.effects.some(d=>d.kind==="pastEnd"),r=i?.effects.filter(d=>d.kind==="entered").at(-1);if(!s&&(r===void 0||!("entry"in r)))return;const n=r&&"entry"in r?this._flowGraph.screenOf(r.entry.step):void 0;if(!n){const d=this._jobSubmissionState;if(this._submissionResponse){this._finishFlow({kind:"completed",job:this._submissionResponse});return}if(d.kind==="failure"){this._finishFlow({kind:"failed",error:d.error});return}const g=d.kind==="success"?d.response:{userId:"",jobId:"",status:"",message:""};this._finishFlow({kind:"completed",job:g});return}o(this,m(this,n));const a=this._jobSubmissionState.kind;n.kind==="processing"&&(a==="notAttempted"||a==="failure")&&this._repository!==void 0&&(this._jobSubmissionState={kind:"loading"}),this._notify(),n.kind==="processing"&&this._jobSubmissionState.kind==="loading"&&this._repository!==void 0&&setTimeout(()=>{this._disposed||te(this)},0)}navigateBack(){if(this._disposed)return;const e=this._engine;if(!e||!f(e.state)){this._finishFlow({kind:"cancelled"});return}const t=this.currentScreen,i=t?.kind,s=t?.kind==="capture"&&t.config.captureType===u.selfie&&this._selfieCaptureStarted,r=l(this,n=>p.back(n.id));if(r!==void 0){if(r.effects.some(n=>n.kind==="finished")){this._finishFlow({kind:"cancelled"});return}if(r.effects.some(n=>n.kind==="left")){(i==="capture"||i==="preview")&&o(this,{kind:"retakeRequested"});const n=this.currentScreen,a=n?.kind==="capture"&&n.config.captureType===u.selfie&&this._captured.selfiePath!==void 0;(s||a)&&(this._selfieCaptureTakes+=1),a&&(this._selfieRetakeCounted=!0),i==="capture"&&(this._selfieCaptureStarted=!1),this._notify()}}}navigateToStart(){if(this._disposed)return;const e=this._navigationPath;this._engine?.dispose(),this._engine=this._startEngine();const t=this._navigationPath;if(e.length!==t.length||e.some((i,s)=>i!==t[s])){const i=this._captured.selfiePath!==void 0;(this._selfieCaptureStarted||i&&!this._selfieRetakeCounted)&&(this._selfieCaptureTakes+=1),this._selfieRetakeCounted=i,this._selfieCaptureStarted=!1}this.deliverNavigationStartFailure(),this._notify()}_startEngine(){if(this._navigationStartFailure=void 0,this._navigationPath=[],this._flowGraph.graph.nodes.length===0)return;const e=B();let t;try{t=D.start(x,this._flowGraph.graph,e?{module:e}:{})}catch(i){if(!(i instanceof T))throw i;this._navigationStartFailure=i;return}return this._navigationPath=this._pathOf(t.state),t.addListener(i=>{this._navigationPath=this._pathOf(i.state)}),t}deliverNavigationStartFailure(){this._navigationStartFailure!==void 0&&this.failNavigation(this._navigationStartFailure)}failNavigation(e){if(!this._disposed){if(this._flowEnded){b.captureException(e);return}this._finishFlow({kind:"failed",error:new R({cause:e})})}}_pathOf(e){return e.entries.map(t=>this._flowGraph.screenOf(t.step))}exit(e={kind:"cancelled"}){this._disposed||e.kind!=="captured"&&this._finishFlow(e)}get captured(){return this._captured}get capturedJob(){return this._capturedJob}setCapturedSelfie(e,t){this._disposed||(ie(e),this._captured={...this._captured,selfiePath:e,livenessPaths:Object.freeze([...t])},this._selfieRetakeCounted=!1,o(this,{kind:"selfieCaptured",livenessImageCount:t.length}),this._notify())}get selfieCaptureGeometry(){return this._selfieCaptureGeometry}_selfieCaptureGeometry;recordSelfieCaptureGeometry(e){e&&(this._selfieCaptureGeometry=e)}setCapturedFrontDocument(e,t=0){U(this,e,t)}setCapturedBackDocument(e,t=0){K(this,e,t)}supportingDocumentSide(){return j(this)}documentCapturesBack(){return L(this)}currentDocumentCaptureSide(){return G(this)}setCapturedVisaDocument(e,t=0){q(this,e,t)}documentPreviewFollowsCapture(){return E(this)}currentPreviewIsDocument(){return W(this)}currentDocumentPreviewPath(){return A(this)}documentBorderFraction(e){return J(this,e)}currentDocumentPreviewBorderFraction(){return M(this)}retakeCurrentDocumentSide(){V(this)}skipDocumentBack(){z(this)}recordDocumentImageOrigin(e,t){Q(this,e,t)}recordSelfieCaptureTelemetry(e){Z(this,e)}recordSelfieCaptureStarted(){this._disposed||(this._selfieCaptureStarted=!0)}recordSelfieUploadCheckRestart(){const e=this.currentScreen;this._disposed||e?.kind!=="capture"||e.config.captureType!==u.selfie||(this._selfieCaptureTakes+=1,this._selfieCaptureStarted=!1)}get selfieCaptureTakes(){return this._selfieCaptureTakes}recordDocumentCaptureTelemetry(e,t){X(this,e,t)}recordDocumentQualityWaiver(e,t){Y(this,e,t)}documentQualityWaived(e){return this._documentQualityWaivers.get(e)}setConsent(e){this._disposed||(this._captured={...this._captured,consentInformation:e},o(this,{kind:"consentCaptured"}),this._notify())}get jobSubmissionState(){return this._jobSubmissionState}beginSubmissionIfNeeded(){$(this)}retrySubmission(){ee(this)}_setCaptured(e){const t=new Set(_(e));for(const i of _(this._captured))t.has(i)||h(i);this._captured=e}_deleteCapturedFiles(){if(this._configuration.config?.allowOfflineMode)return;const{selfiePath:e,livenessPaths:t,frontDocumentPath:i,backDocumentPath:s,visaDocumentPath:r}=this._captured;for(const n of[e,...t,i,s,r])if(n){try{const a=new k(n);a.exists&&a.delete()}catch{}h(n)}}_finishFlow(e){this._capturedJob!==void 0&&e.kind!=="captured"||(this._flowEnded=!0,e.kind!=="captured"&&this._deleteCapturedFiles(),e.kind==="completed"?o(this,e.job.jobId?{kind:"flowCompletedSuccess",jobId:e.job.jobId}:{kind:"flowCompletedSuccess"}):e.kind==="failed"&&o(this,{kind:"flowCompletedFailure",errorMessage:e.error instanceof Error?e.error.message:String(e.error)}),e.kind==="captured"&&o(this,{kind:"flowCompletedCaptured"}),this._onExit(e))}logAnalyticsEvent(e){H(this,e)}_jobType(){return this._configuration.network?.config?.jobType??v.unknown}subscribe(e){return this._listeners.add(e),()=>{this._listeners.delete(e)}}dispose(){this._disposed||(this._disposed=!0,this._listeners.clear(),this._engine?.dispose(),this._engine=void 0)}static _decodeToken(e){if(e.config?.allowOfflineMode)return;const t=e.network?.config?.token;if(!(!t||t.length===0))return y(t)}_notify(){for(const e of this._listeners)try{e()}catch{}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{isBusy as a,NjiaException as l,NjiaIntents as c,top as s}from"@smileid/njia";import{FlowFact as k,
|
|
1
|
+
"use strict";import{isBusy as a,NjiaException as l,NjiaIntents as c,top as s}from"@smileid/njia";import{FlowFact as k,SUBMITTING as m}from"./flow_navigation_graph";import{JobType as S}from"../../core/models/job_type";import{CaptureType as r}from"../../core/models/ml_config";import{DocumentSide as u}from"@smileid/usesmileid_bridge";export function getNextScreen(e){const t=e._engine,n=t?s(t.state):void 0;if(!t||!n)return;syncNavigationState(e);const i=t.preview(nextIntent(e,n)).effects.filter(o=>o.kind==="entered").at(-1);return i&&"entry"in i?e._flowGraph.screenOf(i.entry.step):void 0}export function sendFromTop(e,t){const n=e._engine,i=n?s(n.state):void 0;if(!(!n||!i))try{syncNavigationState(e);const o=s(n.state);return o?n.send(t(o)):void 0}catch(o){if(!(o instanceof l))throw o;e.failNavigation(o);return}}export function syncNavigationState(e){const t=e._engine;if(!t)return;const n=flowFacts(e),i=t.state.facts,o=[...n].filter(d=>!i.has(d)),f=[...i].filter(d=>!n.has(d));(o.length>0||f.length>0)&&t.send(c.facts(o,f));const p=e._jobSubmissionState.kind==="loading";p&&!a(t.state)&&t.send(c.busy(m)),!p&&a(t.state)&&t.send(c.idle())}export function nextIntent(e,t){const n=e._flowGraph.screenOf(t.step),i=n.kind==="capture"&&n.config.captureType===r.document;return e._documentBackSkipped&&i?c.skip(t.id):c.next(t.id)}export function flowFacts(e){const t=new Set;return e.supportingDocumentSide()!==void 0&&e._captured.frontDocumentPath!==void 0&&capturedSupportingDocument(e)===void 0&&!e._documentBackSkipped&&t.add(k.supportingSidePending),e._capturedJob!==void 0&&t.add(k.jobHandedOver),t}export function supportingDocumentSide(e){if(e._configuration.network?.config?.jobType===S.residencyDocumentVerification)return u.Visa;for(const t of e._steps)if(t.kind==="capture"&&t.config.captureType===r.document){const n=t.config.documentConfig;return n?.documentType?.hasBackSide===!0&&(n?.captureBothSides??!1)?u.Back:void 0}}export function capturedSupportingDocument(e){switch(e.supportingDocumentSide()){case u.Visa:return e._captured.visaDocumentPath;case u.Back:return e._captured.backDocumentPath;default:return}}export function documentPreviewFollowsCapture(e){const t=e._steps.findIndex(n=>n.kind==="capture"&&n.config.captureType===r.document);return t<0?!1:e._steps[t+1]?.kind==="preview"}export function precedingStepIsDocumentCapture(e,t){const n=e._steps[t-1];return n?.kind==="capture"&&n.config.captureType===r.document}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as
|
|
1
|
+
"use strict";import{jsx as T}from"react/jsx-runtime";import{useEffect as G,useState as R}from"react";import{StyleSheet as W,View as Q}from"react-native";import{AnalyzerNotFoundException as j,CaptureReadyEventKind as x,DeviceIntegrityProvider as J,DocumentSide as u,ImageType as A}from"@smileid/usesmileid_bridge";import{CameraLens as $}from"../../../camera/core/camera_lens";import{KameraConfigFactory as q}from"../../../camera/core/kamera_config_factory";import{CaptureType as n}from"../../../core/models/ml_config";import{defaultFrameAspectRatio as X,documentFrameOrientation as Y}from"../config/document_type";import{SmileExifTagger as B}from"@smileid/usesmileid_bridge/exif/smile_exif_tagger";import{resolveDeviceModel as Z}from"../../../metadata/use_smile_id_metadata_factory";import{claimAnalyzer as P,isMissingAnalyzerError as M,releaseAnalyzer as ee}from"./helpers/analyzer_lifecycle";import{resolveFaceSessionConfig as te}from"./helpers/face_session_config";import{useCaptureOrientationLock as oe}from"../../screen/helpers/use_capture_orientation_lock";import{localizedDocumentName as re}from"../../screen/helpers/instructions_screen_helpers";import{resolveJpegQuality as ie}from"../../../data/model/sdk_config_capture";import{AttributionResolver as ne,flowShowAttribution as z}from"../config/attribution_resolver";import{UseSmileIDCaptureScreen as se}from"../../screen/use_smile_id_capture_screen";import{cameraExceptionFromKameraError as ce}from"../../../camera/core/kamera_error_mapping";import{defaultLightTheme as ue}from"../../theme/theme";import{UseSmileIDDocumentBackInstructions as le}from"../../screen/use_smile_id_document_back_instructions";import{UseSmileIDInstructionsScreen as de}from"../../screen/use_smile_id_instructions_screen";import{DOCUMENT_HERO_VISA_ANIMATION as ae,documentGuidelines as me}from"../model/instructions_defaults";import{useUseSmileIDLocalizations as fe}from"../../../l10n/use_smile_id_localizations_provider";import{useFlowNavigation as pe}from"../flow_navigation_context";import{UseSmileIDOrchestratedScreenHost as ye,useWhileOnTop as Ce}from"./use_smile_id_orchestrated_screen_host";export function OrchestratedCaptureScreen({configuration:s}){const r=pe(),F=Ce(r),y=fe(),o=s.captureType,a=s.documentConfig,m=a?.documentType,h=r.supportingDocumentSide(),C=h!==void 0,g=a?.allowSkipBack??!1,E=r.documentPreviewFollowsCapture(),O=r.captured.frontDocumentPath!==void 0,[i,b]=R(C&&O&&h!==void 0?h:u.Front),[v,w]=R(C&&O),L=r.configuration.config?.exitOrientations,V=o===n.document&&!v?Y(m,a?.resolvedAspectRatio):"Portrait",K=oe(V,{...L!==void 0?{exitLock:L}:{}}),c=o===n.selfie?r.configuration.ml?.analyzers.selfie:r.configuration.ml?.analyzers.document;return G(()=>{if(!c)return;let e=!1;const D=t=>{e||r.exit({kind:"failed",error:M(t)?new j({captureType:c.kind,cause:t,suggestedFix:"This build leaves that analyzer out. Pick with UseSmileIDMobileServices.analyzerProvider(), or keep the analyzer in this store's flavor."}):t})};let k=0;if(o===n.selfie&&c.kind==="face"){const t=r.configuration.ml?.analyzers.selfieConfig??{analyzerFactoryKey:c.key};k=P(()=>c.configure(te(t,s.selfieConfig?.enableEnhancedLiveness??!1,r.resolvedSdkConfig)),D)}else if(o===n.document&&c.kind==="document"){const t=ie(r.resolvedSdkConfig?.document),l=q.create(n.document,$.back).analysis,I=s.documentConfig?.documentType?.kind,S=s.documentConfig?.resolvedAspectRatio,f=S!==void 0&&Number.isFinite(S)&&S>0?S:void 0;k=P(()=>c.configure({documentSide:i,...t!==void 0?{documentJpegQuality:t}:{},...I!==void 0?{documentKind:I}:{},...f!==void 0?{knownAspectRatio:f}:{},sessionFrameWidth:l.targetWidth,sessionFrameHeight:l.targetHeight}),D)}return()=>{e=!0,ee(k,c)}},[c,o,r,s,i]),T(ye,{content:({navigationManager:e,onBack:D})=>{if(!c)throw new Error(`OrchestratedCaptureScreen: no ${o} analyzer registered in the ML DSL`);if(o===n.document&&C&&v){const t=()=>{if(E){D();return}w(!1),b(u.Front)};return i===u.Visa?T(de,{titlePrefix:y.siInstructionsTitlePrefix,titleEmphasis:y.siInstructionsEmphasisVisa,heroSource:ae,guidelinesSectionTitle:y.siInstructionsCaptureGuidelines,guidelines:me(m),showAttribution:z(e.configuration),showHeroOval:!1,showHeroOvalProgress:!1,showCardBackground:!0,onContinue:()=>w(!1),onBack:t}):T(le,{documentTypeDisplayName:m?re(m,y):"",allowSkip:g,showAttribution:z(e.configuration),onCaptureBack:()=>w(!1),onSkip:()=>{F(()=>{e.skipDocumentBack(),e.navigateToNext()})},onBack:t})}const k=e.configuration.theme?.light??ue;return K?T(se,{captureType:o,analyzer:c,onAnalyticsEvent:t=>e.logAnalyticsEvent(t),...o===n.selfie?{onStartCapture:()=>e.recordSelfieCaptureStarted(),onUploadCheckRetake:()=>e.recordSelfieUploadCheckRestart()}:{},theme:k,enableEnhancedLiveness:s.selfieConfig?.enableEnhancedLiveness??!1,allowAgentMode:s.selfieConfig?.allowAgentMode??!1,showAttribution:ne.resolveForCapture({captureType:o,showAttribution:z(e.configuration)}),...o===n.document&&a?{knownIdAspectRatio:a.resolvedAspectRatio??X(m?.orientation??"Landscape"),captureMode:a.captureMode,allowGalleryUpload:a.allowGalleryUpload,documentSide:i,...m?.kind?{documentTypeKind:m.kind}:{},onDocumentOrigin:t=>e.recordDocumentImageOrigin(i,t),onClose:()=>e.exit()}:{},...s.startButton!==void 0?{startButton:s.startButton}:{},onBack:()=>{if(o===n.document&&C&&i!==u.Front){w(!0);return}D()},onCameraError:(t,l)=>{e.exit({kind:"failed",error:ce(t,l)})},onCaptureComplete:(t,l,I,S)=>{const f=I??0,p=ke(t,o);if(!p)return;const _=e.partnerId,H=o===n.selfie?A.Selfie:i===u.Visa?A.DocumentVisa:i===u.Back?A.DocumentBack:A.DocumentFront,U=t.filter(d=>d.kind===x.Liveness).map(d=>d.url),N=Z();J.signals().then(({fingerprint:d})=>B.setContext({partnerId:_,...N?{deviceModel:N}:{},...d?{fingerprint:d}:{}})).then(async()=>{await B.finalize(p,H),await Promise.all(U.map(d=>B.finalize(d,A.Liveness)))}).catch(()=>{}).finally(()=>F(()=>{if(o===n.selfie){e.setCapturedSelfie(p,U),e.recordSelfieCaptureGeometry(S),e.recordSelfieCaptureTelemetry(l),e.navigateToNext();return}if(e.recordDocumentCaptureTelemetry(i,l),e.recordDocumentQualityWaiver(i,l.qualityWaived),C&&h!==void 0&&i===u.Front){e.setCapturedFrontDocument(p,f),E?e.navigateToNext():(b(h),w(!0));return}i===u.Visa?e.setCapturedVisaDocument(p,f):C&&i===u.Back?e.setCapturedBackDocument(p,f):e.setCapturedFrontDocument(p,f),e.navigateToNext()}))}},o===n.document?`document-capture-${i}`:"capture"):T(Q,{style:[W.absoluteFill,{backgroundColor:k.colors.offBlack}],testID:"si_capture_orientation_pending"})}})}function ke(s,r){const F=r===n.selfie?x.Selfie:x.Document;return s.find(y=>y.kind===F)?.url}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as n}from"react/jsx-runtime";import{UseSmileIDCrashReporting as
|
|
1
|
+
"use strict";import{jsx as n}from"react/jsx-runtime";import{UseSmileIDCrashReporting as m}from"@smileid/usesmileid_bridge";import{ConsentDeniedException as s}from"../../../core/exception";import{GlobalPrivacyControl as i}from"../config/global_privacy_control";import{flowShowAttribution as c}from"../config/attribution_resolver";import{UseSmileIDConsentScreen as a}from"../../screen/use_smile_id_consent_screen";import{useFlowNavigation as d}from"../flow_navigation_context";import{UseSmileIDOrchestratedScreenHost as y,useWhileOnTop as w}from"./use_smile_id_orchestrated_screen_host";import{alwaysEnabledSlot as u}from"./helpers/always_enabled_slot";export function OrchestratedConsentScreen({configuration:e}){const r=u(e.denyButton),l=w(d());return n(y,{content:({navigationManager:t})=>n(a,{partnerName:e.partnerName,globalPrivacyControl:t.configuration.config?.globalPrivacyControl??i.OptOut,...e.partnerIcon!==void 0?{partnerIcon:e.partnerIcon}:{},...e.partnerPrivacyPolicyUrl!==void 0?{partnerPrivacyPolicyUrl:e.partnerPrivacyPolicyUrl}:{},showAttribution:c(t.configuration),...e.allowButton?{allowButton:e.allowButton}:{},...r!==void 0?{denyButton:r}:{},onAllow:o=>l(()=>{t.setConsent(o);try{e.onConsentGranted?.(o)}catch(p){m.captureException(p)}t.navigateToNext()}),onDeny:()=>{t.exit({kind:"failed",error:new s({timestamp:Date.now()})})}})})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{Fragment as
|
|
1
|
+
"use strict";import{Fragment as c,jsx as s}from"react/jsx-runtime";import{useMemo as u}from"react";import{useFlowNavigation as f}from"../flow_navigation_context";import{useWhileOnTop as p}from"./use_smile_id_orchestrated_screen_host";export function OrchestratedPartnerScreen({configuration:a}){const e=f(),t=p(e),o=u(()=>{function d(r,n){if(r!==void 0&&n===void 0)throw new TypeError("next takes a value and its format together");t(()=>{n===void 0?e.navigateToNext():e.navigateToNextWith(n.encode(r))})}return Object.freeze({stepId:a.id,next:d,value:(r,n)=>{const i=e.partnerValue(r);return i===void 0?void 0:n.decode(i)},back:()=>t(()=>e.navigateBack()),cancel:()=>t(()=>e.exit({kind:"cancelled"}))})},[a.id,e,t]);return s(c,{children:a.render(o)})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as r}from"react/jsx-runtime";import{flowShowAttribution as
|
|
1
|
+
"use strict";import{jsx as r}from"react/jsx-runtime";import{flowShowAttribution as p}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{useFlowNavigation as f}from"../flow_navigation_context";import{UseSmileIDOrchestratedScreenHost as d,useWhileOnTop as w}from"./use_smile_id_orchestrated_screen_host";import{defaultFrameAspectRatio as h}from"../config/document_type";export function OrchestratedPreviewScreen({configuration:t}){const m=w(f());return r(d,{content:({navigationManager:e,onBack:s,onNavigateNext:o})=>{const c=p(e.configuration);if(e.currentPreviewIsDocument()){const u=e.currentDocumentPreviewPath();return r(l,{documentAspectRatio:e.documentFrameAspectRatio??h("Landscape"),showAttribution:c,...u!==void 0?{capturedDocumentPath:u,capturedDocumentBorderFraction:e.currentDocumentPreviewBorderFraction()}:{},onAccept:o,onReject:()=>{t.allowRetake&&m(()=>{e.retakeCurrentDocumentSide(),e.navigateBack()})},onClose:()=>e.exit()})}return r(i,{allowRetake:t.allowRetake,showAttribution:c,...e.selfieCaptureGeometry?{selfieCaptureGeometry:e.selfieCaptureGeometry}:{},...e.captured.selfiePath!==void 0?{capturedSelfiePath:e.captured.selfiePath}:{},onAccept:o,onReject:()=>{t.allowRetake&&s()}})}})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{jsx as
|
|
1
|
+
"use strict";import{jsx as t}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 p}from"./use_smile_id_orchestrated_screen_host";import{ProcessingCancelledException as g,UseSmileIDException as l}from"../../../core/exception";import{alwaysEnabledSlot as i}from"./helpers/always_enabled_slot";import{resolveFailureMessage as S}from"./use_smile_id_error_code_failure_message";import{useUseSmileIDLocalizations as b}from"../../../l10n/use_smile_id_localizations_provider";import{JobType as P,requiresDocument as c,requiresSelfie as h}from"../../../core/models/job_type";export function OrchestratedProcessingScreen({configuration:n}){return t(p,{content:({navigationManager:e,onNavigateNext:o})=>t(y,{processingConfig:n,navigationManager:e,onContinue:o})})}function y(n){const{navigationManager:e}=n;a(()=>{e.beginSubmissionIfNeeded()},[e]);const o=e.configuration.network?.config?.jobType??P.unknown,r=c(o)?e.captured.frontDocumentPath:void 0,s=e.documentFrameAspectRatio,u=b();return t(m,{state:x(e.jobSubmissionState,u),showFaceOval:h(o)||c(o),...e.selfieCaptureGeometry?{selfieCaptureGeometry:e.selfieCaptureGeometry}:{},showProgressPercentage:n.processingConfig.showProgressPercentage,showAttribution:f(e.configuration),...r!==void 0?{documentImageUri:r,documentBorderFraction:e.documentBorderFraction(d.Front)}:{},...r!==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:C(e.jobSubmissionState)})})}function w(n){const e=i(n.continueButton),o=i(n.retryButton),r=i(n.exitButton);return{...e!==void 0?{continueButton:e}:{},...o!==void 0?{retryButton:o}:{},...r!==void 0?{exitButton:r}:{}}}function C(n){return n.kind==="failure"?n.error:new g}function x(n,e){switch(n.kind){case"notAttempted":case"loading":case"skipped":return{kind:"loading"};case"success":return{kind:"success",message:n.response.message};case"failure":return{kind:"failed",errorMessage:S(n.error instanceof l?n.error.errorCode:void 0,e)}}}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { UseSmileIDErrorCode } from "../../../core/exception";
|
|
2
|
+
import type { UseSmileIDLocalizations } from "../../../l10n/use_smile_id_localizations";
|
|
3
|
+
/**
|
|
4
|
+
* The processing screen's failure copy, keyed off the error code and never off the exception's
|
|
5
|
+
* message, which also feeds `onResult` and analytics. Only an unreachable server or a timeout
|
|
6
|
+
* reads as a lost connection; a code the table doesn't name gets the generic copy.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveFailureMessage(code: UseSmileIDErrorCode | undefined, localizations: UseSmileIDLocalizations): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import{UseSmileIDErrorCode as i,isNetworkErrorCode as s,isRetryableErrorCode as n}from"../../../core/exception";export function resolveFailureMessage(r,e){return r===i.Network.RequestFailed?e.siProcessingErrorGeneric:r===i.Camera.PermissionDenied?e.siProcessingErrorCameraPermission:r===i.Network.RateLimit||r===i.Network.ServerError?e.siProcessingErrorRetryLater:r!==void 0&&s(r)&&!n(r)?e.siProcessingErrorUnavailable:r===i.Network.ConnectionFailed||r===i.Network.Timeout?e.siProcessingErrorNoConnection:e.siProcessingErrorGeneric}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
2
|
import type { FlowNavigationManager } from "../flow_navigation_manager";
|
|
3
3
|
export type OrchestratedScreenHostContent = (args: {
|
|
4
4
|
readonly navigationManager: FlowNavigationManager;
|
|
@@ -18,5 +18,18 @@ export type UseSmileIDOrchestratedScreenHostProps = {
|
|
|
18
18
|
* `useFlowNavigation()` call throws a loud "called outside
|
|
19
19
|
* `<UseSmileIDBuilder>`" error rather than silently no-op-ing on
|
|
20
20
|
* `navigateToNext()` / `navigateBack()`.
|
|
21
|
+
*
|
|
22
|
+
* Each move acts only while this screen is the one on top
|
|
23
|
+
* ({@link useWhileOnTop}).
|
|
21
24
|
*/
|
|
22
25
|
export declare function UseSmileIDOrchestratedScreenHost({ content, }: UseSmileIDOrchestratedScreenHostProps): import("react").JSX.Element;
|
|
26
|
+
/**
|
|
27
|
+
* Runs the calling screen's work only while it is the one on top. A tap that
|
|
28
|
+
* lands after the flow moved on (a double tap, a tap during the transition, a
|
|
29
|
+
* Continue whose camera permission prompt resolved late) would move it again
|
|
30
|
+
* from the next screen and skip it, so the screen's depth is pinned when it
|
|
31
|
+
* mounts and checked when the work runs. A screen that records something as
|
|
32
|
+
* it moves (a consent, a side cleared to retake, a capture) does both inside
|
|
33
|
+
* it, so a dropped move records nothing either.
|
|
34
|
+
*/
|
|
35
|
+
export declare function useWhileOnTop(navigationManager: FlowNavigationManager): (work: () => void) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{Fragment as
|
|
1
|
+
"use strict";import{Fragment as n,jsx as o}from"react/jsx-runtime";import{useCallback as c,useRef as s}from"react";import{useFlowNavigation as a}from"../flow_navigation_context";export function UseSmileIDOrchestratedScreenHost({content:t}){const e=a(),r=useWhileOnTop(e);return o(n,{children:t({navigationManager:e,onBack:()=>r(()=>e.navigateBack()),onNavigateNext:()=>r(()=>e.navigateToNext())})})}export function useWhileOnTop(t){const e=s(t.currentDepth).current;return c(r=>{t.currentDepth===e&&r()},[t,e])}
|
|
@@ -3,23 +3,22 @@ import type { NetworkConfig } from "../../../core/models/network_config";
|
|
|
3
3
|
import type { BiometricKYCParams, ConsentInformation, DocumentVerificationParams, EnhancedDocumentVerificationParams, EnhancedKYCParams, ResidencyDocumentVerificationParams, UserDetails } from "../../../core/models/params";
|
|
4
4
|
import type { ScreenSpec } from "../dsl/screen_builders";
|
|
5
5
|
import { type TokenPayload } from "../../../data/model/token_payload";
|
|
6
|
-
import {
|
|
6
|
+
import type { ValidationIssue } from "./validation_state";
|
|
7
7
|
/**
|
|
8
8
|
* Pure flow validator — returns typed `UseSmileIDValidationException`
|
|
9
|
-
* instances.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* a thrown error.
|
|
9
|
+
* instances. `b.build()` and `b.validate()` both read
|
|
10
|
+
* `runFlowValidation`, so partner regex-matchers and `instanceof` checks
|
|
11
|
+
* see the same objects whether they're inspecting validate-state or the
|
|
12
|
+
* `BuilderValidationException` delivered through `onResult`.
|
|
14
13
|
*
|
|
15
14
|
* `runFlowValidation` owns only the builder / job-agnostic concerns:
|
|
16
|
-
* empty screens, per-screen capture / consent config,
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* ordering, duplicate screen types, preview placement,
|
|
20
|
-
* dependencies, and per-job field requirements — is
|
|
21
|
-
* per-`JobType` validators in `job_type_validator.ts`,
|
|
22
|
-
* `collectJobTypeIssues`.
|
|
15
|
+
* empty screens, per-screen capture / consent config, partner screens and
|
|
16
|
+
* the consent mutex before the dispatch, then the preset consent and the
|
|
17
|
+
* standalone param validators. Every flow-composition rule — screen
|
|
18
|
+
* ordering, duplicate screen types, preview placement, ML / Network
|
|
19
|
+
* dependencies, a missing job type, and per-job field requirements — is
|
|
20
|
+
* owned by the per-`JobType` validators in `job_type_validator.ts`,
|
|
21
|
+
* dispatched via `collectJobTypeIssues`.
|
|
23
22
|
*
|
|
24
23
|
* Rule order, message wording, severity, and `errorCode` values are
|
|
25
24
|
* the contract surface partners see. Don't reword existing rules
|
|
@@ -46,8 +45,6 @@ type FlowValidatorInput = {
|
|
|
46
45
|
readonly tokenPayload?: TokenPayload | undefined;
|
|
47
46
|
readonly skipApiSubmission?: boolean | undefined;
|
|
48
47
|
};
|
|
49
|
-
/** `b.validate()`'s pre-flight: screens and their own settings, not job-level rules. */
|
|
50
|
-
export declare function runBuilderValidation(steps: readonly ScreenSpec[]): ValidationIssue[];
|
|
51
48
|
/**
|
|
52
49
|
* Run every flow-level validation rule and return the collected
|
|
53
50
|
* issues. Empty array means valid.
|
|
@@ -81,7 +78,8 @@ export declare function runDocumentVerificationParamsValidation(params: Document
|
|
|
81
78
|
export declare function runEnhancedDocumentVerificationParamsValidation(params: EnhancedDocumentVerificationParams, tokenPayload?: TokenPayload): ValidationIssue[];
|
|
82
79
|
/**
|
|
83
80
|
* Validate a `ResidencyDocumentVerificationParams` payload in isolation.
|
|
84
|
-
* Both `country` and `idType` are required and must be
|
|
81
|
+
* Both `country` and `idType` are required, and `idType` must be
|
|
82
|
+
* `"PASSPORT"`, the one value the backend accepts, unless the token binds it.
|
|
85
83
|
*/
|
|
86
84
|
export declare function runResidencyDocumentVerificationParamsValidation(params: ResidencyDocumentVerificationParams, tokenPayload?: TokenPayload): ValidationIssue[];
|
|
87
85
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{partnerScreenIssues as
|
|
1
|
+
"use strict";import{partnerScreenIssues as p}from"./partner_screen_validation";import{CaptureType as f}from"../../../core/models/ml_config";import{InvalidConsentConfigException as t,InvalidConsentInformationException as d}from"../../../core/exception/consent_validation_exceptions";import{InvalidFieldValueException as s}from"../../../core/exception/job_validation_exceptions";import{AgentModeWithEnhancedLivenessException as l,InvalidDocumentCaptureConfigException as a,InvalidMissingCaptureTypeException as y}from"../../../core/exception/capture_validation_exceptions";import{EmptyScreensBlockException as h}from"../../../core/exception/screen_config_validation_exceptions";import{isIdFieldBoundByToken as r}from"../../../data/model/token_payload";import{collectJobTypeIssues as P,hasNonPassportIdType as I,residencyIdTypeIssue as w}from"./job_type_validator";import{hasConsentScreen as g}from"../config/flow_configuration_screens";function C(e){return e?.allowAgentMode&&e.enableEnhancedLiveness?[new l]:[]}function T(e){if(e===void 0)return[new a({reason:"document is required when mode is Document"})];if(e.documentType===void 0)return[new a({reason:"documentType is required for document capture"})];const n=e.documentType.knownAspectRatio;return n!==void 0&&n<=0?[new a({reason:"documentType.knownAspectRatio must be > 0 if provided"})]:e.knownIdAspectRatio!==void 0&&e.knownIdAspectRatio<=0?[new a({reason:"knownIdAspectRatio must be > 0 if provided"})]:[]}function b(e){const n=[];for(const o of e)o.kind==="capture"&&(o.config.captureType===void 0&&n.push(new y),o.config.captureType===f.selfie&&n.push(...C(o.config.selfieConfig)),o.config.captureType===f.document&&n.push(...T(o.config.documentConfig))),o.kind==="consent"&&n.push(...k(o));return n}function k(e){const n=[];return(!e.config.partnerName||!e.config.partnerName.trim())&&n.push(new t({reason:"partnerName cannot be empty"})),e.config.partnerIcon===void 0&&n.push(new t({reason:"partnerIcon is required for the consent screen"})),(e.config.partnerPrivacyPolicyUrl===void 0||!e.config.partnerPrivacyPolicyUrl.trim())&&n.push(new t({reason:"partnerPrivacyPolicyUrl is required for the consent screen"})),n}export function runFlowValidation(e){const n=[];return e.steps.length===0?(n.push(new h),n):(n.push(...b(e.steps)),n.push(...p(e.steps)),g(e.steps)&&e.consentInformation!==void 0&&n.push(new t({reason:"Exactly one form of consent is required \u2014 remove the consent { } screen if using consentInformation, or clear consentInformation if using the consent screen"})),P(e,n),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(...u(e.residencyDocumentVerificationParams,e.tokenPayload)),n)}export function runEnhancedKYCParamsValidation(e,n){return m(e,n)}export function runBiometricKYCParamsValidation(e,n){return m(e,n)}export function runDocumentVerificationParamsValidation(e,n){const o=[];return!r(n,"country")&&!e.country?.trim()&&o.push(new s({fieldName:"country",reason:"must not be blank"})),!r(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){return u(e,n)}export function runResidencyDocumentVerificationParamsValidation(e,n){const o=u(e,n);return!r(n,"idType")&&I(e)&&o.push(w()),o}function u(e,n){const o=[];for(const i of["country","idType"]){const c=e[i];!r(n,i)&&!c?.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 d({detail:"granted must be true"}));for(const o of["grantedAt","noticeLanguage","noticePrivacyPolicyUrl"])(!e[o]||!e[o].trim())&&n.push(new d({detail:`${o} must not be blank`}));return n}function m(e,n){const o=[];for(const i of["idType","idNumber","country"]){const c=e[i];!r(n,i)&&!c?.trim()&&o.push(new s({fieldName:i,reason:"must not be blank"}))}return o}
|
|
@@ -14,8 +14,8 @@ import type { ValidationIssue } from "./validation_state";
|
|
|
14
14
|
* screen ordering, preview placement, ML analyzer dependencies, consent
|
|
15
15
|
* presence, capture counts, processing, and per-job field requirements.
|
|
16
16
|
* `FlowValidator` only owns the builder / job-agnostic concerns (empty
|
|
17
|
-
* screens, per-screen config,
|
|
18
|
-
*
|
|
17
|
+
* screens, per-screen config, the consent mutex, the preset consent and
|
|
18
|
+
* the params payloads) around its dispatch to the matching validator.
|
|
19
19
|
*
|
|
20
20
|
* Adding a new job type requires only a new entry in
|
|
21
21
|
* `FlowValidator`'s `jobTypeValidators` registry — `FlowValidator`
|
|
@@ -106,13 +106,12 @@ export declare class NonCaptureJobTypeValidator implements JobTypeValidator {
|
|
|
106
106
|
validate(input: JobTypeValidatorInput, issues: ValidationIssue[]): void;
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* single Preview following a Capture is permitted by default.
|
|
109
|
+
* Validates a flow whose `JobType` doesn't resolve. It is always invalid,
|
|
110
|
+
* and still gets the structural rules and the missing-block warnings, so a
|
|
111
|
+
* partner sees every problem at once. The preview cap is the document bound
|
|
112
|
+
* of two, since the per-job caps apply only once the job type resolves.
|
|
114
113
|
*/
|
|
115
|
-
export declare class
|
|
114
|
+
export declare class UnknownJobTypeValidator implements JobTypeValidator {
|
|
116
115
|
validate(input: JobTypeValidatorInput, issues: ValidationIssue[]): void;
|
|
117
116
|
}
|
|
118
117
|
/**
|
|
@@ -122,17 +121,13 @@ export declare class DefaultStructuralValidator implements JobTypeValidator {
|
|
|
122
121
|
*/
|
|
123
122
|
export declare const jobTypeValidators: ReadonlyMap<JobType, JobTypeValidator>;
|
|
124
123
|
/**
|
|
125
|
-
* Resolve the validator for the configured job type, falling back to
|
|
126
|
-
*
|
|
127
|
-
* unregistered. Always returns a validator so the generic structural
|
|
128
|
-
* rules run regardless of dispatch.
|
|
124
|
+
* Resolve the validator for the configured job type, falling back to
|
|
125
|
+
* `UnknownJobTypeValidator` when the job type is unknown or unregistered.
|
|
129
126
|
*/
|
|
130
127
|
export declare function resolveJobTypeValidator(jobType: JobType | undefined): JobTypeValidator;
|
|
131
128
|
/**
|
|
132
129
|
* Run the job-type-specific rules against the flow and append any issues
|
|
133
|
-
* found. Called by `FlowValidator` after its
|
|
134
|
-
* matching validator is resolved from the configured `jobType`, with the
|
|
135
|
-
* generic structural validator as the fallback for unknown job types.
|
|
130
|
+
* found. Called by `FlowValidator` after its per-screen pass.
|
|
136
131
|
*/
|
|
137
132
|
export declare function collectJobTypeIssues(input: JobTypeValidatorInput, issues: ValidationIssue[]): void;
|
|
138
133
|
/**
|
|
@@ -141,3 +136,8 @@ export declare function collectJobTypeIssues(input: JobTypeValidatorInput, issue
|
|
|
141
136
|
* standalone params pre-check report it identically.
|
|
142
137
|
*/
|
|
143
138
|
export declare function residencyIdTypeIssue(): InvalidJobTypeScreenConfigException;
|
|
139
|
+
/**
|
|
140
|
+
* A set `idType` other than {@link RESIDENCY_ID_TYPE}; a blank one is the
|
|
141
|
+
* presence rule's to report.
|
|
142
|
+
*/
|
|
143
|
+
export declare function hasNonPassportIdType(params: ResidencyDocumentVerificationParams): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{RESIDENCY_ID_TYPE as h}from"../../../core/models/params";import{JobType as i}from"../../../core/models/job_type";import{CaptureType as y}from"../dsl/screen_builders";import{InvalidJobTypeScreenConfigException as a}from"../../../core/exception/job_validation_exceptions";import{JOB_PARAMS_OWNERS as C,UnusedJobParamsException as x}from"../../../core/exception/unused_job_params_exception";import{DuplicateScreenTypeException as V,InvalidScreenOrderException as p,MissingCaptureForPreviewException as D}from"../../../core/exception/screen_config_validation_exceptions";import{IssueSeverity as u}from"../../../core/exception/issue_severity";import{tokenConsentMissingSubfields as P}from"../../../data/model/token_payload";const E=new Map([[i.biometricKyc,"BiometricKyc"],[i.smartSelfieAuthentication,"SmartSelfieAuthentication"],[i.smartSelfieEnrollment,"SmartSelfieEnrollment"],[i.enhancedKyc,"EnhancedKyc"],[i.documentVerification,"DocumentVerification"],[i.bvn,"BVN"],[i.enhancedDocumentVerification,"EnhancedDocumentVerification"],[i.residencyDocumentVerification,"ResidencyDocumentVerification"],[i.unknown,"Unknown"]]);function j(o){return E.get(o)??"Unknown"}function b(o,e,n){const r=e.steps.some(s=>s.kind==="consent"),t=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}e.skipApiSubmission!==!0&&!r&&!t&&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,residencyDocumentVerificationParams:o=>o.residencyDocumentVerificationParams};function T(o,e,n){for(const{fieldName:r,owner:t}of C)t!==o&&JOB_PARAMS_SUPPLIERS[r]?.(e)!==void 0&&n.push(new x({jobType:o,fieldName:r}))}function v(o){return o.filter(e=>e.kind!=="partner")}function f(o,e){const n=new Map;let r=0;o.forEach((t,c)=>{if(t.kind==="capture"){r++;return}if(t.kind==="preview"||t.kind==="partner")return;const s=t.kind==="instructions"?`${t.kind}:${r}`:t.kind,d=n.get(s)??{kind:t.kind,indices:[]};d.indices.push(c),n.set(s,d)});for(const{kind:t,indices:c}of n.values())c.length>1&&e.push(new V({screenType:t,indices:c}))}function m(o,e){v(o).findIndex(c=>c.kind==="consent")>0&&e.push(new p({description:"Consent screen must be the first screen in the flow",severity:u.error}));const r=o.findIndex(c=>c.kind==="processing");r!==-1&&r!==o.length-1&&e.push(new p({description:"Processing screen must be the last screen in the flow",severity:u.error}));const t=o.some(c=>c.kind==="capture");o.forEach((c,s)=>{const d=o[s+1]?.kind;c.kind==="instructions"&&t&&d!=="capture"&&d!=="instructions"&&e.push(new p({description:`Instructions screen at index ${s} must be followed by a Capture screen`,severity:u.error}))})}function l(o,e,n){let r=!1,t=0;o.forEach((c,s)=>{c.kind==="capture"?r=!0:c.kind==="preview"&&(t+=1,r?t>e&&n.push(new p({description:`Preview screen at index ${s} exceeds the maximum of ${e} preview screen(s) allowed for this flow`,severity:u.error})):n.push(new D({previewIndex:s})))})}function g(o,e,n){e.steps.some(t=>t.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 k(o,e,n){const r=e.steps.filter(t=>t.kind==="capture"&&t.config.captureType===y.selfie);r.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"})):r.length>1&&n.push(a.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 w(o,e,n,r){e.ml?e.ml.analyzers.selfie||r.push(a.forJob({jobType:o,reason:"ml { } block must register analyzers for CaptureType.SELFIE",suggestedFix:"Add forCaptureType(CaptureType.SELFIE) { } inside the ml { } block"})):r.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),m(e.steps,n),l(e.steps,1,n),b(this.jobType,e,n),T(this.jobType,e,n),k(this.jobType,e,n),e.steps.some(t=>t.kind==="capture"&&t.config.captureType===y.document)&&n.push(a.forJob({jobType:this.jobType,reason:"must not include a Document capture screen",suggestedFix:`Remove the Document capture screen \u2014 ${j(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 ${j(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"}))}}export class DocumentJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){f(e.steps,n),m(e.steps,n),l(e.steps,2,n),b(this.jobType,e,n),T(this.jobType,e,n),k(this.jobType,e,n);const r=e.steps.filter(t=>t.kind==="capture"&&t.config.captureType===y.document);if(r.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"})):r.length>1&&n.push(a.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"})),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&&r.some(t=>t.kind==="capture"&&t.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"})),this.jobType===i.residencyDocumentVerification)for(const t of r){if(t.kind!=="capture")continue;const c=t.config.documentConfig,s=c?.documentType;s?.kind!=="passport"&&n.push(a.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"})),c&&c.allowSkipBack===!0&&n.push(a.forJob({jobType:this.jobType,reason:"allowSkipBack must be false \u2014 the visa capture is mandatory",suggestedFix:"Remove allowSkipBack from the document capture screen"}))}}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"})),this.jobType===i.residencyDocumentVerification&&e.tokenPayload?.hasIdType!==!0&&e.residencyDocumentVerificationParams!==void 0&&e.residencyDocumentVerificationParams.idType!==h&&n.push(residencyIdTypeIssue()),this.jobType===i.residencyDocumentVerification&&e.residencyDocumentVerificationParams===void 0&&n.push(a.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){f(e.steps,n),m(e.steps,n),l(e.steps,0,n),b(this.jobType,e,n),T(this.jobType,e,n),g(this.jobType,e,n);const r=e.steps.filter(t=>t.kind!=="consent"&&t.kind!=="processing"&&t.kind!=="partner");if(r.length>0){const t=[...new Set(r.map(c=>c.kind))].join(", ");n.push(a.forJob({jobType:this.jobType,reason:`must only contain consent and processing screens, but found: ${t}`,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"}))}}export class DefaultStructuralValidator{validate(e,n){f(e.steps,n),m(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.residencyDocumentVerification,new DocumentJobTypeValidator(i.residencyDocumentVerification)],[i.enhancedKyc,new NonCaptureJobTypeValidator(i.enhancedKyc)],[i.bvn,new NonCaptureJobTypeValidator(i.bvn)]]);const S=new DefaultStructuralValidator;export function resolveJobTypeValidator(o){return o===void 0||o===i.unknown?S:jobTypeValidators.get(o)??S}export function collectJobTypeIssues(o,e){const n=o.network?.config?.jobType;resolveJobTypeValidator(n).validate(o,e)}export function residencyIdTypeIssue(){return a.forJob({jobType:i.residencyDocumentVerification,reason:`idType must be "${h}" \u2014 the backend rejects any other value for this job type`,suggestedFix:`Set builder.residencyDocumentVerificationParams with idType: "${h}"`})}
|
|
1
|
+
"use strict";import{RESIDENCY_ID_TYPE as y}from"../../../core/models/params";import{JobType as t}from"../../../core/models/job_type";import{InvalidJobTypeScreenConfigException as a,MissingJobTypeException as V,MissingMLConfigurationBlockException as E,MissingNetworkConfigurationBlockException as D}from"../../../core/exception/job_validation_exceptions";import{JOB_PARAMS_OWNERS as P,UnusedJobParamsException as v}from"../../../core/exception/unused_job_params_exception";import{DuplicateScreenTypeException as J,InvalidScreenOrderException as p,MissingCaptureForPreviewException as I}from"../../../core/exception/screen_config_validation_exceptions";import{IssueSeverity as u}from"../../../core/exception/issue_severity";import{isIdFieldBoundByToken as F,tokenConsentMissingSubfields as K}from"../../../data/model/token_payload";import{hasCaptureScreen as g,hasConsentScreen as R,hasDocumentCaptureScreen as A,hasProcessingScreen as w,resolveDocumentCaptures as B,resolveSdkScreens as j,resolveSelfieCaptures as M}from"../config/flow_configuration_screens";const N=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 S(o){return N.get(o)??"Unknown"}function h(o,e,n){const i=e.consentInformation!==void 0,r=e.tokenPayload?.consent;if(r!==void 0){const c=K(r);c.length>0&&n.push(a.forJob({jobType:o,reason:`the token's consent binding is incomplete \u2014 missing: ${c.join(", ")}`,suggestedFix:"Bind all four consent fields when minting the token at /v3/token, or bind none and collect consent in the app"}));return}e.skipApiSubmission!==!0&&!R(e.steps)&&!i&&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,residencyDocumentVerificationParams:o=>o.residencyDocumentVerificationParams};function b(o,e,n){for(const{fieldName:i,owner:r}of P)r!==o&&JOB_PARAMS_SUPPLIERS[i]?.(e)!==void 0&&n.push(new v({jobType:o,fieldName:i}))}function f(o,e){const n=new Map;let i=0;o.forEach((r,c)=>{if(r.kind==="capture"){i++;return}if(r.kind==="preview"||r.kind==="partner")return;const s=r.kind==="instructions"?`${r.kind}:${i}`:r.kind,d=n.get(s)??{kind:r.kind,indices:[]};d.indices.push(c),n.set(s,d)});for(const{kind:r,indices:c}of n.values())c.length>1&&e.push(new J({screenType:r,indices:c}))}function m(o,e){j(o).findIndex(c=>c.kind==="consent")>0&&e.push(new p({description:"Consent screen must be the first screen in the flow",severity:u.error}));const i=o.findIndex(c=>c.kind==="processing");i!==-1&&i!==o.length-1&&e.push(new p({description:"Processing screen must be the last screen in the flow",severity:u.error}));const r=g(o);o.forEach((c,s)=>{const d=o[s+1]?.kind;c.kind==="instructions"&&r&&d!=="capture"&&d!=="instructions"&&e.push(new p({description:`Instructions screen at index ${s} must be followed by a Capture screen`,severity:u.error}))})}function l(o,e,n){let i=!1,r=0;o.forEach((c,s)=>{c.kind==="capture"?i=!0:c.kind==="preview"&&(r+=1,i?r>e&&n.push(new p({description:`Preview screen at index ${s} exceeds the maximum of ${e} preview screen(s) allowed for this flow`,severity:u.error})):n.push(new I({previewIndex:s})))})}function T(o,e,n){w(e.steps)||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 k(o,e,n){const i=M(e.steps);i.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"})):i.length>1&&n.push(a.forJob({jobType:o,reason:`must include exactly one Capture screen with captureType = SELFIE, but found ${i.length}`,suggestedFix:"Remove the extra selfie capture screens \u2014 only one is allowed"}))}function C(o,e,n,i){e.ml?e.ml.analyzers.selfie||i.push(a.forJob({jobType:o,reason:"ml { } block must register analyzers for CaptureType.SELFIE",suggestedFix:"Add forCaptureType(CaptureType.SELFIE) { } inside the ml { } block"})):i.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),m(e.steps,n),l(e.steps,1,n),h(this.jobType,e,n),b(this.jobType,e,n),k(this.jobType,e,n),A(e.steps)&&n.push(a.forJob({jobType:this.jobType,reason:"must not include a Document capture screen",suggestedFix:`Remove the Document capture screen \u2014 ${S(this.jobType)} requires selfie capture only`})),T(this.jobType,e,n),C(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 ${S(this.jobType)} does not use document capture`})),this.jobType===t.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===t.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"}))}}export class DocumentJobTypeValidator{jobType;constructor(e){this.jobType=e}validate(e,n){f(e.steps,n),m(e.steps,n),l(e.steps,2,n),h(this.jobType,e,n),b(this.jobType,e,n),k(this.jobType,e,n);const i=B(e.steps);if(i.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"})):i.length>1&&n.push(a.forJob({jobType:this.jobType,reason:`must include exactly one Capture screen with captureType = DOCUMENT, but found ${i.length}`,suggestedFix:"Remove the extra document capture screens \u2014 only one is allowed"})),T(this.jobType,e,n),C(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&&i.some(r=>r.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"})),this.jobType===t.residencyDocumentVerification)for(const r of i){const c=r.documentConfig,s=c?.documentType;s?.kind!=="passport"&&n.push(a.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"})),c&&c.allowSkipBack===!0&&n.push(a.forJob({jobType:this.jobType,reason:"allowSkipBack must be false \u2014 the visa capture is mandatory",suggestedFix:"Remove allowSkipBack from the document capture screen"}))}}appendParamsRule(e,n){this.jobType===t.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===t.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"})),this.jobType===t.residencyDocumentVerification&&!F(e.tokenPayload,"idType")&&e.residencyDocumentVerificationParams!==void 0&&hasNonPassportIdType(e.residencyDocumentVerificationParams)&&n.push(residencyIdTypeIssue()),this.jobType===t.residencyDocumentVerification&&e.residencyDocumentVerificationParams===void 0&&n.push(a.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){f(e.steps,n),m(e.steps,n),l(e.steps,0,n),h(this.jobType,e,n),b(this.jobType,e,n),T(this.jobType,e,n);const i=j(e.steps).filter(r=>r.kind!=="consent"&&r.kind!=="processing");if(i.length>0){const r=[...new Set(i.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===t.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"}))}}export class UnknownJobTypeValidator{validate(e,n){f(e.steps,n),m(e.steps,n),l(e.steps,2,n),g(e.steps)&&!e.ml&&n.push(new E),w(e.steps)&&!e.network&&n.push(new D),n.push(new V)}}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 x=new UnknownJobTypeValidator;export function resolveJobTypeValidator(o){return o===void 0?x:jobTypeValidators.get(o)??x}export function collectJobTypeIssues(o,e){const n=o.network?.config?.jobType;resolveJobTypeValidator(n).validate(o,e)}export function residencyIdTypeIssue(){return a.forJob({jobType:t.residencyDocumentVerification,reason:`idType must be "${y}" \u2014 the backend rejects any other value for this job type`,suggestedFix:`Set builder.residencyDocumentVerificationParams with idType: "${y}"`})}export function hasNonPassportIdType(o){return(o.idType?.trim()??"")!==""&&o.idType!==y}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smileid/usesmileid",
|
|
3
|
-
"version": "12.3.1-SNAPSHOT.
|
|
3
|
+
"version": "12.3.1-SNAPSHOT.71",
|
|
4
4
|
"description": "Official Smile ID React Native (Expo) SDK for identity verification: selfie capture, liveness checks, and document verification powered by on-device ML.",
|
|
5
5
|
"homepage": "https://usesmileid.com",
|
|
6
6
|
"repository": {
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"CHANGELOG.md"
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@smileid/kamera": "1.2.1-SNAPSHOT.
|
|
52
|
-
"@smileid/njia": "0.0.1-SNAPSHOT.
|
|
51
|
+
"@smileid/kamera": "1.2.1-SNAPSHOT.34",
|
|
52
|
+
"@smileid/njia": "0.0.1-SNAPSHOT.11",
|
|
53
53
|
"zod": "^4.6.7",
|
|
54
|
-
"@smileid/
|
|
55
|
-
"@smileid/
|
|
54
|
+
"@smileid/usesmileid_bridge": "12.3.1-SNAPSHOT.71",
|
|
55
|
+
"@smileid/usesmileid_platform_interface": "12.3.1-SNAPSHOT.71"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"@react-native-async-storage/async-storage": ">=1.21.0",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
}
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
|
-
"@smileid/njia-testing": "0.0.1-SNAPSHOT.
|
|
105
|
+
"@smileid/njia-testing": "0.0.1-SNAPSHOT.11",
|
|
106
106
|
"@types/react": "^19.2.18",
|
|
107
107
|
"expo": "~57.0.26",
|
|
108
108
|
"expo-brightness": "~57.0.2",
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { UseSmileIDLocalizations } from "../../../../l10n/use_smile_id_localizations";
|
|
2
|
-
/** Failure copy resolved from `errorCode`; `.message` and `errorCode` are left untouched — they are the `onResult` payload and the analytics field. */
|
|
3
|
-
export declare function resolveFailureMessage(error: unknown, localizations: UseSmileIDLocalizations): string;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";import{UseSmileIDErrorCode as i,UseSmileIDException as o,isNetworkErrorCode as n,isRetryableErrorCode as t}from"../../../../core/exception";export function resolveFailureMessage(s,r){if(!(s instanceof o))return r.siProcessingErrorGeneric;const e=s.errorCode;return e===i.Camera.PermissionDenied?r.siProcessingErrorCameraPermission:n(e)?e===i.Network.RequestFailed?r.siProcessingErrorGeneric:e===i.Network.RateLimit||e===i.Network.ServerError?r.siProcessingErrorRetryLater:t(e)?r.siProcessingErrorNoConnection:r.siProcessingErrorUnavailable:r.siProcessingErrorGeneric}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { UseSmileIDOverlayState as OverlayState } from "../../state/use_smile_id_overlay_state";
|
|
2
|
-
/**
|
|
3
|
-
* Pure-logic helpers for `UseSmileIDProcessingScreen`. The visual
|
|
4
|
-
* branches by `state.kind`; this maps each kind to the matching
|
|
5
|
-
* overlay-border state so the face oval colour transitions cleanly:
|
|
6
|
-
*
|
|
7
|
-
* loading → CAPTURING (blue)
|
|
8
|
-
* success → READY (green)
|
|
9
|
-
* failure → IDLE (default)
|
|
10
|
-
*/
|
|
11
|
-
export type ProcessingStateKind = "loading" | "success" | "failed";
|
|
12
|
-
export declare function resolveOverlayStateForProcessing(kind: ProcessingStateKind): OverlayState;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";import{UseSmileIDOverlayState as e}from"../../state/use_smile_id_overlay_state";export function resolveOverlayStateForProcessing(r){switch(r){case"loading":return e.PROCESSING;case"success":return e.READY;case"failed":return e.IDLE}}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { UseSmileIDTheme } from "../theme/theme";
|
|
2
|
-
export type UseSmileIDPermissionScreenProps = {
|
|
3
|
-
readonly cameraRationale?: string;
|
|
4
|
-
readonly onGranted?: () => void;
|
|
5
|
-
readonly onDenied?: () => void;
|
|
6
|
-
readonly theme?: UseSmileIDTheme;
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* Permission screen — requests camera (and optionally microphone)
|
|
10
|
-
* at the OS level.
|
|
11
|
-
*
|
|
12
|
-
* **STUB.** Visual implementation pending — see
|
|
13
|
-
* `react-native-expo/docs/Architecture.md § Rollout`.
|
|
14
|
-
*/
|
|
15
|
-
export declare function UseSmileIDPermissionScreen(props: UseSmileIDPermissionScreenProps): import("react").JSX.Element;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";import{jsx as o,jsxs as i}from"react/jsx-runtime";import{Text as t,View as n}from"react-native";import{useUseSmileIDTheme as c}from"../theme/theme_context";export function UseSmileIDPermissionScreen(r){const s=c(),e=r.theme??s;return i(n,{style:{flex:1,alignItems:"center",justifyContent:"center",backgroundColor:e.colors.background,padding:e.spacing.large},children:[o(t,{style:{...e.typography.sectionHeading,color:e.colors.titleText},children:"UseSmileIDPermissionScreen"}),o(t,{style:{...e.typography.cardSubTitle,color:e.colors.cardText},children:r.cameraRationale??"Camera permission required \u2014 stub"})]})}
|