@smileid/usesmileid 12.3.1-SNAPSHOT.69 → 12.3.1-SNAPSHOT.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/core/exception/invalid_job_type_screen_config_exception.js +1 -1
  2. package/dist/core/models/job_type.d.ts +8 -0
  3. package/dist/core/models/job_type.js +1 -1
  4. package/dist/data/api/bridge_submissions.js +1 -1
  5. package/dist/data/api/smile_id_api.d.ts +9 -1
  6. package/dist/data/api/smile_id_api.js +1 -1
  7. package/dist/data/compression/payload_compression_runner.d.ts +3 -1
  8. package/dist/data/compression/payload_compression_runner.js +1 -1
  9. package/dist/data/compression/uri_compressible_asset.d.ts +30 -4
  10. package/dist/data/compression/uri_compressible_asset.js +1 -1
  11. package/dist/data/factory/networking_factory.d.ts +23 -11
  12. package/dist/data/factory/networking_factory.js +1 -1
  13. package/dist/data/model/captured_job.d.ts +3 -1
  14. package/dist/data/model/captured_job.js +1 -1
  15. package/dist/data/model/job_request.d.ts +18 -1
  16. package/dist/data/repository/use_smile_id_repository_impl.js +1 -1
  17. package/dist/data/util/scratch_directory.d.ts +12 -0
  18. package/dist/data/util/scratch_directory.js +1 -0
  19. package/dist/metadata/use_smile_id_metadata.d.ts +2 -0
  20. package/dist/metadata/use_smile_id_metadata.js +1 -1
  21. package/dist/metadata/use_smile_id_metadata_factory.d.ts +15 -4
  22. package/dist/metadata/use_smile_id_metadata_factory.js +1 -1
  23. package/dist/presentation/flow/config/flow_configuration_camera.d.ts +0 -2
  24. package/dist/presentation/flow/config/flow_configuration_camera.js +1 -1
  25. package/dist/presentation/flow/config/flow_configuration_screens.d.ts +10 -0
  26. package/dist/presentation/flow/config/flow_configuration_screens.js +1 -0
  27. package/dist/presentation/flow/dsl/use_smile_id_builder.js +1 -1
  28. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.d.ts +18 -36
  29. package/dist/presentation/flow/dsl/use_smile_id_flow_builder.js +1 -1
  30. package/dist/presentation/flow/flow_navigation_graph.d.ts +4 -4
  31. package/dist/presentation/flow/flow_navigation_graph.js +1 -1
  32. package/dist/presentation/flow/flow_navigation_manager.js +1 -1
  33. package/dist/presentation/flow/flow_navigation_manager_job_request.js +1 -1
  34. package/dist/presentation/flow/flow_navigation_manager_routing.js +1 -1
  35. package/dist/presentation/flow/screen/orchestrated_capture_screen.js +1 -1
  36. package/dist/presentation/flow/screen/orchestrated_consent_screen.js +1 -1
  37. package/dist/presentation/flow/screen/orchestrated_partner_screen.js +1 -1
  38. package/dist/presentation/flow/screen/orchestrated_preview_screen.js +1 -1
  39. package/dist/presentation/flow/screen/orchestrated_processing_screen.js +1 -1
  40. package/dist/presentation/flow/screen/use_smile_id_error_code_failure_message.d.ts +8 -0
  41. package/dist/presentation/flow/screen/use_smile_id_error_code_failure_message.js +1 -0
  42. package/dist/presentation/flow/screen/use_smile_id_orchestrated_screen_host.d.ts +14 -1
  43. package/dist/presentation/flow/screen/use_smile_id_orchestrated_screen_host.js +1 -1
  44. package/dist/presentation/flow/validation/flow_validator.d.ts +14 -16
  45. package/dist/presentation/flow/validation/flow_validator.js +1 -1
  46. package/dist/presentation/flow/validation/job_type_validator.d.ts +28 -15
  47. package/dist/presentation/flow/validation/job_type_validator.js +1 -1
  48. package/package.json +6 -6
  49. package/dist/presentation/flow/screen/helpers/failure_message.d.ts +0 -3
  50. package/dist/presentation/flow/screen/helpers/failure_message.js +0 -1
  51. package/dist/presentation/screen/helpers/processing_screen_helpers.d.ts +0 -12
  52. package/dist/presentation/screen/helpers/processing_screen_helpers.js +0 -1
  53. package/dist/presentation/screen/use_smile_id_permission_screen.d.ts +0 -15
  54. package/dist/presentation/screen/use_smile_id_permission_screen.js +0 -1
@@ -1 +1 @@
1
- "use strict";import{JobType as e}from"../../core/models/job_type";import{UseSmileIDBuilderException as i}from"./use_smile_id_builder_exception";import{UseSmileIDErrorCode as r}from"./use_smile_id_error_code";export class InvalidJobTypeScreenConfigException extends i{errorCode=r.Builder.InvalidJobTypeScreenConfig;constructor(n){super({message:n.message,suggestedFix:n.suggestedFix??"Adjust the screens block to match the configured jobType."})}static forJob(n){const t=jobTypeName(n.jobType);return new InvalidJobTypeScreenConfigException({message:`Invalid configuration for ${t}: ${n.reason}`,suggestedFix:n.suggestedFix})}}const c=new Map([[e.biometricKyc,"BiometricKyc"],[e.smartSelfieAuthentication,"SmartSelfieAuthentication"],[e.smartSelfieEnrollment,"SmartSelfieEnrollment"],[e.enhancedKyc,"EnhancedKyc"],[e.documentVerification,"DocumentVerification"],[e.bvn,"BVN"],[e.enhancedDocumentVerification,"EnhancedDocumentVerification"],[e.residencyDocumentVerification,"ResidencyDocumentVerification"],[e.unknown,"Unknown"]]);export function jobTypeName(o){return c.get(o)??"Unknown"}
1
+ "use strict";import{JobType as e}from"../../core/models/job_type";import{UseSmileIDBuilderException as i}from"./use_smile_id_builder_exception";import{UseSmileIDErrorCode as r}from"./use_smile_id_error_code";export class InvalidJobTypeScreenConfigException extends i{errorCode=r.Builder.InvalidJobTypeScreenConfig;constructor(n){super({message:n.message,suggestedFix:n.suggestedFix??"Adjust the screens block to match the configured jobType."})}static forJob(n){const t=jobTypeName(n.jobType);return new InvalidJobTypeScreenConfigException({message:`Invalid configuration for ${t}: ${n.reason}`,suggestedFix:n.suggestedFix})}}const c=new Map([[e.biometricKyc,"BiometricKyc"],[e.smartSelfieAuthentication,"SmartSelfieAuthentication"],[e.smartSelfieEnrollment,"SmartSelfieEnrollment"],[e.enhancedKyc,"EnhancedKyc"],[e.documentVerification,"DocumentVerification"],[e.bvn,"BVN"],[e.enhancedDocumentVerification,"EnhancedDocumentVerification"],[e.residencyDocumentVerification,"ResidencyDocumentVerification"],[e.unknownProduct,"UnknownProduct"],[e.unknown,"Unknown"]]);export function jobTypeName(o){return c.get(o)??"Unknown"}
@@ -18,6 +18,14 @@ export declare const JobType: {
18
18
  * a mandatory supporting visa capture, submitted as one job.
19
19
  */
20
20
  readonly residencyDocumentVerification: 16;
21
+ /**
22
+ * A job whose product your backend chooses. The flow captures whatever
23
+ * its screens configure, with none of a product's rules, and hands the job
24
+ * back for you to forward to that product's endpoint. Valid only with
25
+ * `config.skipApiSubmission = true`: Smile ID has no endpoint for it, so the
26
+ * SDK never submits it.
27
+ */
28
+ readonly unknownProduct: -2;
21
29
  /**
22
30
  * Sentinel used when no job type has been configured, or when the
23
31
  * server returns a value not recognised by this SDK version. Update
@@ -1 +1 @@
1
- "use strict";export const JobType={biometricKyc:1,smartSelfieAuthentication:2,smartSelfieEnrollment:4,enhancedKyc:5,documentVerification:6,bvn:7,enhancedDocumentVerification:11,residencyDocumentVerification:16,unknown:-1};export function jobTypeFromValue(e){return Object.values(JobType).find(n=>n===e)??JobType.unknown}export function requiresSelfie(e){return e===JobType.biometricKyc||e===JobType.smartSelfieAuthentication||e===JobType.smartSelfieEnrollment}export function requiresDocument(e){return e===JobType.biometricKyc||e===JobType.documentVerification||e===JobType.enhancedDocumentVerification||e===JobType.residencyDocumentVerification}
1
+ "use strict";export const JobType={biometricKyc:1,smartSelfieAuthentication:2,smartSelfieEnrollment:4,enhancedKyc:5,documentVerification:6,bvn:7,enhancedDocumentVerification:11,residencyDocumentVerification:16,unknownProduct:-2,unknown:-1};export function jobTypeFromValue(e){return Object.values(JobType).find(n=>n===e)??JobType.unknown}export function requiresSelfie(e){return e===JobType.biometricKyc||e===JobType.smartSelfieAuthentication||e===JobType.smartSelfieEnrollment}export function requiresDocument(e){return e===JobType.biometricKyc||e===JobType.documentVerification||e===JobType.enhancedDocumentVerification||e===JobType.residencyDocumentVerification}
@@ -1 +1 @@
1
- "use strict";import{Directory as S,File as w,Paths as x}from"expo-file-system";import{filePath as k}from"../util/file_path";import{sourceSdkIdentifier as b}from"@smileid/usesmileid_bridge";import{NativeNetworkClient as y,NativeNetworkError as E,toNativeNetworkError as D}from"@smileid/usesmileid_bridge/internal";import{usesSandbox as C}from"./environment";import{testRoutePort as M}from"./test_route";import{LogLevel as d,RetryStrategy as m}from"../../core/models/network_config";import{decodeSecurityPolicyBitCode as _}from"../util/use_smile_id_token_decoder";import{NetworkServerException as T,NetworkTimeoutException as v,NetworkTransportException as I}from"../../core/exception/use_smile_id_network_exception";import{UseSmileIDErrorCode as c}from"../../core/exception/use_smile_id_error_code";const O="12.3.0";export class BridgeSubmissions{_client;_routed;constructor(t,n){this._client=t,this._routed=n}static configure(t,n=y.shared(),i=M(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:p(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:p(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 D(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:b(),sourceSdkVersion:O,policy:_(i)??null,timeouts:{connectSeconds:a?.connect??60,readSeconds:a?.read??60,writeSeconds:a?.write??60,callSeconds:a?.call??120},retry:j(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 g(i)}}async seal(t,n){const i=new S(x.cache,`si_sealed_${Date.now()}_${Math.random().toString(36).slice(2)}`);i.create({intermediates:!0});try{const a=new w(i,"package.json");try{await this._client.seal({path:t,parts:n.toWire()},k(a.uri))}catch(s){throw g(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}}finally{i.exists&&i.delete()}}}function g(e){if(!(e instanceof E))return e;const t=R.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 v({message:e.message,cause:e}):new I({message:e.message,cause:e,errorCode:t??(e.retryable===!0?c.Network.ConnectionFailed:c.Network.RequestFailed)})}const R=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 p(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 j(e){if(e===void 0||e.strategy===m.none)return{strategy:"none"};const t=Number.isNaN(e.maxAttempts)?1:Math.min(A,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:P(e),maxDelaySeconds:f(e),jitter:Number.isNaN(e.jitter)?0:Math.min(1,Math.max(0,e.jitter))}}const A=2**31-1,F=86400;function f(e){return e.maxDelay===Number.POSITIVE_INFINITY?F:u(e.maxDelay)}function u(e){return Number.isFinite(e)?Math.max(0,e):0}function P(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}
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}
@@ -1,5 +1,5 @@
1
1
  import type { PartnerConfig } from "../../core/models/network_config";
2
- import type { BiometricKycRequest, DocumentVerificationRequest, EnhancedDocumentVerificationRequest, ResidencyDocumentVerificationRequest, EnhancedKycRequest, SmartSelfieAuthenticationRequest, SmartSelfieEnrollmentRequest } from "../model/job_request";
2
+ import type { BiometricKycRequest, DocumentVerificationRequest, EnhancedDocumentVerificationRequest, ResidencyDocumentVerificationRequest, EnhancedKycRequest, SmartSelfieAuthenticationRequest, SmartSelfieEnrollmentRequest, UnknownProductRequest } from "../model/job_request";
3
3
  import type { JobSubmissionResponse } from "../model/job_submission_response";
4
4
  import { type BridgeSubmissions } from "./bridge_submissions";
5
5
  /**
@@ -50,6 +50,14 @@ export declare class SmileIDAPI {
50
50
  documentVerification(request: DocumentVerificationRequest): Promise<JobSubmissionResponse>;
51
51
  enhancedDocumentVerification(request: EnhancedDocumentVerificationRequest): Promise<JobSubmissionResponse>;
52
52
  residencyDocumentVerification(request: ResidencyDocumentVerificationRequest): Promise<JobSubmissionResponse>;
53
+ /**
54
+ * An unknown product's captures. Only the sealing client takes them, and
55
+ * the client that sends refuses them: the job only runs with
56
+ * `skipApiSubmission`, and the partner forwards it to the product's own
57
+ * endpoint. Its path is never requested, and neither the request signature
58
+ * nor the package covers the path.
59
+ */
60
+ unknownProduct(request: UnknownProductRequest): Promise<JobSubmissionResponse>;
53
61
  private _postDocumentVerification;
54
62
  /**
55
63
  * Build the selfie + liveness portion of a multipart form. No
@@ -1 +1 @@
1
- "use strict";import{isEmptyConsent as f}from"../../core/models/params";import{userDetailsPayloadToWire as l}from"../model/user_details_payload";import{NetworkResponseParseException as s}from"../../core/exception";import{LivenessFrameCountException as p}from"../../core/exception";import{JobForm as d}from"../model/job_form";import{BridgeSealed as m}from"./bridge_submissions";export class SmileIDAPI{_partnerConfig;_bridge;_sealing;_warmed=!1;constructor(e,i,n=!1){this._partnerConfig=e,this._bridge=i,this._sealing=n}warmUpConnection(){if(!this._warmed){this._warmed=!0;try{this._bridge.warm()}catch{}}}async smartSelfieEnrollment(e){const i=this._buildSelfieForm(e);return a(i,e),this._appendPartnerConfigParts(i),this._appendConsentAndMetadata(i,e.consent,e.metadata),this._post("/v3/registration",i)}async smartSelfieAuthentication(e){const i=this._buildSelfieForm(e);return i.field("user_id",e.userId),a(i,e),this._appendPartnerConfigParts(i),this._appendConsentAndMetadata(i,e.consent,e.metadata),this._post("/v3/authentication",i)}async enhancedKyc(e){const i=new d;return this._appendIdFields(i,e.params),a(i,e),e.params.bankCode&&i.field("bank_code",e.params.bankCode),e.params.mobileOperator&&i.field("operator",e.params.mobileOperator),this._appendPartnerConfigParts(i),this._appendConsentAndMetadata(i,e.consent,e.metadata),this._post("/v3/enhanced_kyc",i)}async biometricKyc(e){const i=e.params.useEnrolledImage,n=this._buildSelfieForm(e);return this._appendIdFields(n,e.params),a(n,e),i&&n.field("use_enrolled_image","true"),this._appendPartnerConfigParts(n),this._appendConsentAndMetadata(n,e.consent,e.metadata),this._post("/v3/biometric_kyc",n)}async documentVerification(e){return this._postDocumentVerification(e,"/v3/document_verification")}async enhancedDocumentVerification(e){return this._postDocumentVerification(e,"/v3/enhanced_document_verification")}async residencyDocumentVerification(e){return this._postDocumentVerification(e,"/v3/residency_document_verification")}async _postDocumentVerification(e,i){const n=this._buildSelfieForm(e);return n.file("document",e.frontDocument),"backDocument"in e&&e.backDocument&&n.file("document_back",e.backDocument),"visaDocument"in e&&n.file("visa",e.visaDocument),e.country&&n.field("country",e.country),e.idType&&n.field("id_type",e.idType),a(n,e),this._appendPartnerConfigParts(n),this._appendConsentAndMetadata(n,e.consent,e.metadata),this._post(i,n)}_buildSelfieForm(e){SmileIDAPI._validateLivenessCount(e.livenessImages,e.expectedLivenessImageCount);const i=new d;i.file("selfie_image",e.selfieImage);for(const n of e.livenessImages)i.file("liveness_images",n);return i}static _validateLivenessCount(e,i){if(e.length!==i)throw new p({expected:i,actual:e.length})}_appendIdFields(e,i){i.idType&&e.field("id_type",i.idType),i.idNumber&&e.field("id_number",i.idNumber),i.country&&e.field("country",i.country)}_appendPartnerConfigParts(e){const i=this._partnerConfig.callbackUrl;i&&e.field("callback_url",i);const n=this._partnerConfig.partnerParams;n&&Object.keys(n).length>0&&e.json("partner_params",n)}_appendConsentAndMetadata(e,i,n){i!==void 0&&f(i)?e.json("consent",{}):i!==void 0&&e.json("consent",{granted:i.granted,granted_at:i.grantedAt,notice_language:i.noticeLanguage,notice_privacy_policy_url:i.noticePrivacyPolicyUrl}),e.json("metadata",n.toEntries())}async _post(e,i){if(this._sealing)throw new m(await this._bridge.seal(e,i));return _(await this._bridge.submit(e,i))}}function _(t){if(t==null||typeof t!="object"||Array.isArray(t))throw new s({message:"SmileIDAPI: server returned an empty or non-object response body."});const e=t,i=(r,c)=>{if(typeof r!="string")throw new s({message:`SmileIDAPI: response is missing the required string field '${c}'.`});return r},n={jobId:i(e.job_id??e.jobId,"job_id"),userId:i(e.user_id??e.userId,"user_id"),status:i(e.status,"status"),message:i(e.message,"message")},o=e.created_at??e.createdAt;return typeof o=="string"&&o.length>0?{...n,createdAt:o}:n}function a(t,e){const i=e.userDetails;i!==void 0&&t.json("user_details",l(i))}
1
+ "use strict";import{isEmptyConsent as f}from"../../core/models/params";import{userDetailsPayloadToWire as l}from"../model/user_details_payload";import{NetworkResponseParseException as d}from"../../core/exception";import{LivenessFrameCountException as m}from"../../core/exception";import{InvalidFieldValueException as p}from"../../core/exception";import{JobForm as s}from"../model/job_form";import{BridgeSealed as _}from"./bridge_submissions";export class SmileIDAPI{_partnerConfig;_bridge;_sealing;_warmed=!1;constructor(e,n,i=!1){this._partnerConfig=e,this._bridge=n,this._sealing=i}warmUpConnection(){if(!this._warmed){this._warmed=!0;try{this._bridge.warm()}catch{}}}async smartSelfieEnrollment(e){const n=this._buildSelfieForm(e);return a(n,e),this._appendPartnerConfigParts(n),this._appendConsentAndMetadata(n,e.consent,e.metadata),this._post("/v3/registration",n)}async smartSelfieAuthentication(e){const n=this._buildSelfieForm(e);return n.field("user_id",e.userId),a(n,e),this._appendPartnerConfigParts(n),this._appendConsentAndMetadata(n,e.consent,e.metadata),this._post("/v3/authentication",n)}async enhancedKyc(e){const n=new s;return this._appendIdFields(n,e.params),a(n,e),e.params.bankCode&&n.field("bank_code",e.params.bankCode),e.params.mobileOperator&&n.field("operator",e.params.mobileOperator),this._appendPartnerConfigParts(n),this._appendConsentAndMetadata(n,e.consent,e.metadata),this._post("/v3/enhanced_kyc",n)}async biometricKyc(e){const n=e.params.useEnrolledImage,i=this._buildSelfieForm(e);return this._appendIdFields(i,e.params),a(i,e),n&&i.field("use_enrolled_image","true"),this._appendPartnerConfigParts(i),this._appendConsentAndMetadata(i,e.consent,e.metadata),this._post("/v3/biometric_kyc",i)}async documentVerification(e){return this._postDocumentVerification(e,"/v3/document_verification")}async enhancedDocumentVerification(e){return this._postDocumentVerification(e,"/v3/enhanced_document_verification")}async residencyDocumentVerification(e){return this._postDocumentVerification(e,"/v3/residency_document_verification")}async unknownProduct(e){if(!this._sealing)throw new p({fieldName:"jobType",reason:"an unknown product is only sealed, never submitted"});e.expectedLivenessImageCount!==void 0&&SmileIDAPI._validateLivenessCount(e.livenessImages,e.expectedLivenessImageCount);const n=new s;e.selfieImage&&n.file("selfie_image",e.selfieImage);for(const i of e.livenessImages)n.file("liveness_images",i);return e.frontDocument&&n.file("document",e.frontDocument),e.backDocument&&n.file("document_back",e.backDocument),a(n,e),this._appendPartnerConfigParts(n),this._appendConsentAndMetadata(n,e.consent,e.metadata),this._post(g,n)}async _postDocumentVerification(e,n){const i=this._buildSelfieForm(e);return i.file("document",e.frontDocument),"backDocument"in e&&e.backDocument&&i.file("document_back",e.backDocument),"visaDocument"in e&&i.file("visa",e.visaDocument),e.country&&i.field("country",e.country),e.idType&&i.field("id_type",e.idType),a(i,e),this._appendPartnerConfigParts(i),this._appendConsentAndMetadata(i,e.consent,e.metadata),this._post(n,i)}_buildSelfieForm(e){SmileIDAPI._validateLivenessCount(e.livenessImages,e.expectedLivenessImageCount);const n=new s;n.file("selfie_image",e.selfieImage);for(const i of e.livenessImages)n.file("liveness_images",i);return n}static _validateLivenessCount(e,n){if(e.length!==n)throw new m({expected:n,actual:e.length})}_appendIdFields(e,n){n.idType&&e.field("id_type",n.idType),n.idNumber&&e.field("id_number",n.idNumber),n.country&&e.field("country",n.country)}_appendPartnerConfigParts(e){const n=this._partnerConfig.callbackUrl;n&&e.field("callback_url",n);const i=this._partnerConfig.partnerParams;i&&Object.keys(i).length>0&&e.json("partner_params",i)}_appendConsentAndMetadata(e,n,i){n!==void 0&&f(n)?e.json("consent",{}):n!==void 0&&e.json("consent",{granted:n.granted,granted_at:n.grantedAt,notice_language:n.noticeLanguage,notice_privacy_policy_url:n.noticePrivacyPolicyUrl}),e.json("metadata",i.toEntries())}async _post(e,n){if(this._sealing)throw new _(await this._bridge.seal(e,n));return u(await this._bridge.submit(e,n))}}const g="/v3/unknown_product";function u(t){if(t==null||typeof t!="object"||Array.isArray(t))throw new d({message:"SmileIDAPI: server returned an empty or non-object response body."});const e=t,n=(r,c)=>{if(typeof r!="string")throw new d({message:`SmileIDAPI: response is missing the required string field '${c}'.`});return r},i={jobId:n(e.job_id??e.jobId,"job_id"),userId:n(e.user_id??e.userId,"user_id"),status:n(e.status,"status"),message:n(e.message,"message")},o=e.created_at??e.createdAt;return typeof o=="string"&&o.length>0?{...i,createdAt:o}:i}function a(t,e){const n=e.userDetails;n!==void 0&&t.json("user_details",l(n))}
@@ -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 a}from"expo-file-system";import{ImageType as n}from"@smileid/usesmileid_bridge";import{resolveCaptureQuality as i}from"../model/sdk_config_capture";import{CompressibleAssets as d}from"./compressible_asset";import{PayloadCompressor as r}from"./payload_compressor";import{UriCompressibleAsset as t}from"./uri_compressible_asset";const b=1024*1024;function o(e){if(e===void 0)return;const m=new a(e).size??0;return m>0?m/b:void 0}export const PayloadCompressionRunner={async compress(e){const m=o(e.selfieImage),s=o(e.frontDocument),c=o(e.backDocument),f=o(e.visaDocument),u=new d({selfie:e.selfieImage?new t(n.Selfie,e.selfieImage,i(e.sdkConfig,n.Selfie)):void 0,liveness:(e.livenessImages??[]).map(l=>new t(n.Liveness,l,i(e.sdkConfig,n.Liveness))),documentFront:e.frontDocument?new t(n.DocumentFront,e.frontDocument,i(e.sdkConfig,n.DocumentFront)):void 0,documentBack:e.backDocument?new t(n.DocumentBack,e.backDocument,i(e.sdkConfig,n.DocumentBack)):void 0,documentVisa:e.visaDocument?new t(n.DocumentVisa,e.visaDocument,i(e.sdkConfig,n.DocumentVisa)):void 0});return await r.prepareBundleForTransmission(u,e.metadataBytes),{selfieOriginalMb:m,selfieFinalMb:o(e.selfieImage),frontOriginalMb:s,frontFinalMb:o(e.frontDocument),backOriginalMb:c,backFinalMb:o(e.backDocument),visaOriginalMb:f,visaFinalMb:o(e.visaDocument)}}};
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 original bytes are preserved once on the first `recompress`
9
- * so every step re-encodes from source (no generational stacking; deterministic retries).
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 _sourceUri;
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 e}from"expo-file-system";import{ImageManipulator as n,SaveFormat as c}from"expo-image-manipulator";import{SmileExifTagger as o}from"@smileid/usesmileid_bridge/exif/smile_exif_tagger";export class UriCompressibleAsset{imageType;uri;_quality;_size;_sourceUri;constructor(t,i,r){this.imageType=t,this.uri=i,this._quality=Math.min(Math.max(r,0),1),this._size=UriCompressibleAsset._fileSize(i)}encodedSize(){return this._size}quality(){return this._quality}async recompress(t){const i=new e(this.uri);if(!i.exists)return;if(this._sourceUri===void 0){const s=`${this.uri}.orig`;try{i.copy(new e(s)),this._sourceUri=s}catch{this._sourceUri=this.uri}}const r=Math.min(Math.max(t,0),1),a=await(await n.manipulate(this._sourceUri).renderAsync()).saveAsync({compress:r,format:c.JPEG});try{i.exists&&i.delete(),new e(a.uri).move(i)}catch{}await o.finalize(this.uri,this.imageType),this._quality=t,this._size=UriCompressibleAsset._fileSize(this.uri)}static _fileSize(t){try{return new e(t).size??0}catch{return 0}}}
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 l}from"../api/smile_id_api";import{Attestation as m}from"@smileid/usesmileid_bridge";import{BridgeSubmissions as f}from"../api/bridge_submissions";import{testRoutePort as p}from"../api/test_route";import{MissingPartnerConfigException as n}from"../exception/missing_partner_config_exception";import{UseSmileIDMetadataFactory as h}from"../../metadata/use_smile_id_metadata_factory";export function makeNetworkingFactory(t,o={}){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 e=o.capturing===!0,a=new h,c=p(t)??null,s=o.bridge??f.configure(t,o.nativeClient,c),u=new l(r,s,e);return e||m.warmUp().catch(()=>{}),{api:u,metadata:a,bridge:s}}const d=/^callback_[0-9a-hjkmnp-tv-z]{26}$/;export function isResolvableCallbackUrl(t){if(t===""||d.test(t))return!0;const o=t.indexOf("://");if(o<=0)return!1;const r=t.slice(0,o).toLowerCase();if(r!=="http"&&r!=="https")return!1;const e=((t.slice(o+3).split(/[/?#]/)[0]??"").split("@").pop()??"").split(":")[0]??"";return e!==""&&!/[\s\u0000-\u001F\u007F-\u009F]/.test(e)}
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)}
@@ -6,7 +6,9 @@ import type { SealedJobRequest } from "./sealed_job_request";
6
6
  * own backend: send the job type's `/v3` request with `headers` as request
7
7
  * headers, the images as their usual form parts, and `encryptedPackage`,
8
8
  * `packageNonce` and `packageTag` as the `encrypted_package`, `package_nonce`
9
- * and `package_tag` form fields.
9
+ * and `package_tag` form fields. A `JobType.unknownProduct` job goes to the
10
+ * endpoint of the product you choose, with that product's own fields, such as
11
+ * `country` and `id_type`, sent beside the package.
10
12
  *
11
13
  * Everything else the SDK would have sent (the consent record, the metadata,
12
14
  * the signature and the other `smileid-*` headers) travels inside
@@ -1 +1 @@
1
- "use strict";import{JobType as t}from"../../core/models/job_type";export function capturedJobFrom(e,a,c){const n=m(e),i=r([n.selfieImage,...n.livenessImages,n.frontDocument,n.backDocument,n.visaDocument]);return Object.freeze({jobType:e.jobType,sessionId:a,...i!==void 0?{sessionDirectory:i}:{},...n.selfieImage!==void 0?{selfieImage:n.selfieImage}:{},livenessImages:Object.freeze([...n.livenessImages]),...n.frontDocument!==void 0?{frontDocument:n.frontDocument}:{},...n.backDocument!==void 0?{backDocument:n.backDocument}:{},...n.visaDocument!==void 0?{visaDocument:n.visaDocument}:{},headers:c.headers,encryptedPackage:c.encryptedPackage,packageNonce:c.packageNonce,packageTag:c.packageTag})}function m(e){switch(e.jobType){case t.smartSelfieEnrollment:case t.smartSelfieAuthentication:case t.biometricKyc:return{selfieImage:e.selfieImage,livenessImages:e.livenessImages};case t.documentVerification:case t.enhancedDocumentVerification:return{selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,...e.backDocument!==void 0?{backDocument:e.backDocument}:{}};case t.residencyDocumentVerification:return{selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,visaDocument:e.visaDocument};case t.enhancedKyc:case t.bvn:return{livenessImages:[]}}}function o(e){const a=e.lastIndexOf("/");if(a<=0)return;const c=e.slice(0,a);return/^[a-z][a-z0-9+.-]*:\/*$/i.test(c)?void 0:c}const f="UseSmileID";function r(e){const a=new Set(e.filter(s=>s!==void 0).map(o));if(a.size!==1)return;const[c]=a;if(c===void 0)return;const n=o(c);return n?.slice(n.lastIndexOf("/")+1)===f?c:void 0}
1
+ "use strict";import{JobType as a}from"../../core/models/job_type";export function capturedJobFrom(e,o,c){const n=m(e),i=r([n.selfieImage,...n.livenessImages,n.frontDocument,n.backDocument,n.visaDocument]);return Object.freeze({jobType:e.jobType,sessionId:o,...i!==void 0?{sessionDirectory:i}:{},...n.selfieImage!==void 0?{selfieImage:n.selfieImage}:{},livenessImages:Object.freeze([...n.livenessImages]),...n.frontDocument!==void 0?{frontDocument:n.frontDocument}:{},...n.backDocument!==void 0?{backDocument:n.backDocument}:{},...n.visaDocument!==void 0?{visaDocument:n.visaDocument}:{},headers:c.headers,encryptedPackage:c.encryptedPackage,packageNonce:c.packageNonce,packageTag:c.packageTag})}function m(e){switch(e.jobType){case a.smartSelfieEnrollment:case a.smartSelfieAuthentication:case a.biometricKyc:return{selfieImage:e.selfieImage,livenessImages:e.livenessImages};case a.documentVerification:case a.enhancedDocumentVerification:return{selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,...e.backDocument!==void 0?{backDocument:e.backDocument}:{}};case a.residencyDocumentVerification:return{selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,visaDocument:e.visaDocument};case a.unknownProduct:return{...e.selfieImage!==void 0?{selfieImage:e.selfieImage}:{},livenessImages:e.livenessImages,...e.frontDocument!==void 0?{frontDocument:e.frontDocument}:{},...e.backDocument!==void 0?{backDocument:e.backDocument}:{}};case a.enhancedKyc:case a.bvn:return{livenessImages:[]}}}function t(e){const o=e.lastIndexOf("/");if(o<=0)return;const c=e.slice(0,o);return/^[a-z][a-z0-9+.-]*:\/*$/i.test(c)?void 0:c}const f="UseSmileID";function r(e){const o=new Set(e.filter(s=>s!==void 0).map(t));if(o.size!==1)return;const[c]=o;if(c===void 0)return;const n=t(c);return n?.slice(n.lastIndexOf("/")+1)===f?c:void 0}
@@ -160,4 +160,21 @@ export type ResidencyDocumentVerificationRequest = BaseJobRequest & {
160
160
  /** See `SmartSelfieEnrollmentRequest.expectedLivenessImageCount`. */
161
161
  readonly expectedLivenessImageCount: number;
162
162
  };
163
- export type JobRequest = SmartSelfieEnrollmentRequest | SmartSelfieAuthenticationRequest | EnhancedKycRequest | BvnRequest | BiometricKycRequest | DocumentVerificationRequest | EnhancedDocumentVerificationRequest | ResidencyDocumentVerificationRequest;
163
+ /**
164
+ * Whatever the flow's screens captured, for the partner's backend to forward
165
+ * to the product it chooses: a selfie with its liveness stills, a document's
166
+ * sides, or both. It carries none of a product's own fields; the partner adds
167
+ * those when forwarding. Only ever sealed, never submitted.
168
+ */
169
+ export type UnknownProductRequest = BaseJobRequest & {
170
+ readonly jobType: typeof JobType.unknownProduct;
171
+ readonly selfieImage?: string;
172
+ readonly livenessImages: readonly string[];
173
+ readonly frontDocument?: string;
174
+ readonly backDocument?: string;
175
+ /** Omitted when the token binds every required field, or reduced to the unbound remainder. */
176
+ readonly userDetails?: UserDetailsPayload;
177
+ /** See `SmartSelfieEnrollmentRequest.expectedLivenessImageCount`; absent when no selfie was captured. */
178
+ readonly expectedLivenessImageCount?: number;
179
+ };
180
+ export type JobRequest = SmartSelfieEnrollmentRequest | SmartSelfieAuthenticationRequest | EnhancedKycRequest | BvnRequest | BiometricKycRequest | DocumentVerificationRequest | EnhancedDocumentVerificationRequest | ResidencyDocumentVerificationRequest | UnknownProductRequest;
@@ -1 +1 @@
1
- "use strict";import{failure as s,success as m}from"../../core/models/result";import{JobType as t}from"../../core/models/job_type";import{InvalidFieldValueException as l}from"../../core/exception/job_validation_exceptions";import{UseSmileIDException as u}from"../../core/exception/use_smile_id_exception";import{NetworkUnknownException as d}from"../../core/exception/use_smile_id_network_exception";import{PayloadCompressionRunner as o}from"../compression/payload_compression_runner";import{BridgeSealed as _}from"../api/bridge_submissions";function p(i){const e=[],n=(r,a)=>{a!==void 0&&a>0&&e.push({name:r,value:String(a)})};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 s(new l({fieldName:"jobType",reason:"BVN submission is not yet supported"}));const r=this._captureApi;if(!r)return s(new Error("No capture client to seal the request through"));const a=await this._compressed(e,n);try{return await UseSmileIDRepositoryImpl._send(r,a),s(new Error("The capture client sent the request instead of sealing it"))}catch(c){return c instanceof _?m(c.sealed):s(f(c))}}async submit(e,n){if(e.jobType===t.bvn)return s(new l({fieldName:"jobType",reason:"BVN submission is not yet supported"}));const r=await this._compressed(e,n);try{return m(await UseSmileIDRepositoryImpl._send(this._api,r))}catch(a){return s(f(a))}}async _compressed(e,n){try{const r=await this._compressPayload(e,n),a=r?p(r):[];if(a.length>0)return{...e,metadata:e.metadata.withAdditionalEntries(a)}}catch{}return e}async _compressPayload(e,n){const r=JSON.stringify(e.metadata.toEntries()).length;switch(e.jobType){case t.smartSelfieEnrollment:case t.smartSelfieAuthentication:case t.biometricKyc:return o.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,metadataBytes:r,sdkConfig:n});case t.documentVerification:case t.enhancedDocumentVerification:return o.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,backDocument:e.backDocument,metadataBytes:r,sdkConfig:n});case t.residencyDocumentVerification:return o.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,visaDocument:e.visaDocument,metadataBytes:r,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 f(i){return i instanceof u?i:new d({message:i instanceof Error?i.message:String(i),cause:i})}
1
+ "use strict";import{failure as c,success as l}from"../../core/models/result";import{JobType as i}from"../../core/models/job_type";import{InvalidFieldValueException as m}from"../../core/exception/job_validation_exceptions";import{UseSmileIDException as d}from"../../core/exception/use_smile_id_exception";import{NetworkUnknownException as u}from"../../core/exception/use_smile_id_network_exception";import{PayloadCompressionRunner as s}from"../compression/payload_compression_runner";import{BridgeSealed as p}from"../api/bridge_submissions";function _(t){const e=[],n=(a,o)=>{o!==void 0&&o>0&&e.push({name:a,value:String(o)})};return n("selfie_original_size",t.selfieOriginalMb),n("selfie_final_compressed_size",t.selfieFinalMb),n("document_front_original_size",t.frontOriginalMb),n("document_front_final_compressed_size",t.frontFinalMb),n("document_back_original_size",t.backOriginalMb),n("document_back_final_compressed_size",t.backFinalMb),n("document_visa_original_size",t.visaOriginalMb),n("document_visa_final_compressed_size",t.visaFinalMb),e}export class UseSmileIDRepositoryImpl{_api;_captureApi;constructor(e,n){this._api=e,this._captureApi=n}async seal(e,n){if(e.jobType===i.bvn)return c(new m({fieldName:"jobType",reason:"BVN submission is not yet supported"}));const a=this._captureApi;if(!a)return c(new u({message:"No capture client to seal the request through"}));const o=await this._compressed(e,n);try{return await UseSmileIDRepositoryImpl._send(a,o),c(new u({message:"The capture client sent the request instead of sealing it"}))}catch(r){return r instanceof p?l(r.sealed):c(f(r))}}async submit(e,n){if(e.jobType===i.bvn)return c(new m({fieldName:"jobType",reason:"BVN submission is not yet supported"}));if(e.jobType===i.unknownProduct)return c(new m({fieldName:"jobType",reason:"an unknown product is only sealed, never submitted"}));const a=await this._compressed(e,n);try{return l(await UseSmileIDRepositoryImpl._send(this._api,a))}catch(o){return c(f(o))}}async _compressed(e,n){try{const a=await this._compressPayload(e,n),o=a?_(a):[];if(o.length>0)return{...e,metadata:e.metadata.withAdditionalEntries(o)}}catch{}return e}async _compressPayload(e,n){const a=JSON.stringify(e.metadata.toEntries()).length;switch(e.jobType){case i.smartSelfieEnrollment:case i.smartSelfieAuthentication:case i.biometricKyc:return s.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,metadataBytes:a,sdkConfig:n});case i.documentVerification:case i.enhancedDocumentVerification:return s.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,backDocument:e.backDocument,metadataBytes:a,sdkConfig:n});case i.residencyDocumentVerification:return s.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,visaDocument:e.visaDocument,metadataBytes:a,sdkConfig:n});case i.unknownProduct:return s.compress({selfieImage:e.selfieImage,livenessImages:e.livenessImages,frontDocument:e.frontDocument,backDocument:e.backDocument,metadataBytes:a,sdkConfig:n});default:return}}static _send(e,n){switch(n.jobType){case i.smartSelfieEnrollment:return e.smartSelfieEnrollment(n);case i.smartSelfieAuthentication:return e.smartSelfieAuthentication(n);case i.enhancedKyc:return e.enhancedKyc(n);case i.biometricKyc:return e.biometricKyc(n);case i.documentVerification:return e.documentVerification(n);case i.enhancedDocumentVerification:return e.enhancedDocumentVerification(n);case i.residencyDocumentVerification:return e.residencyDocumentVerification(n);case i.unknownProduct:return e.unknownProduct(n)}}}function f(t){return t instanceof d?t:new u({message:t instanceof Error?t.message:String(t),cause:t})}
@@ -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)}),n(i,"client_ip",e.clientIp),n(i,"network_connection",e.networkConnection),n(i,"carrier_info",e.carrierInfo),n(i,"timezone",e.timezone),n(i,"locale",e.locale),n(i,"screen_resolution",e.screenResolution),n(i,"device_orientation",e.deviceOrientation),n(i,"system_architecture",e.systemArchitecture),n(i,"host_application",e.hostApplication),a(i,"memory_info",e.memoryInfoMb),a(i,"number_of_cameras",e.numberOfCameras),n(i,"device_tier",e.deviceTier??""),n(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)}),n(i,"build_brand",e.buildBrand??""),n(i,"build_device",e.buildDevice??""),n(i,"build_fingerprint",e.buildFingerprint??""),n(i,"build_product",e.buildProduct??""),n(i,"build_hardware",e.buildHardware??""),n(i,"build_source",e.buildSource??""),n(i,"package_name",e.packageName??""),n(i,"fingerprint",e.fingerprint??""),n(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)}),n(i,"wrapper_name",e.wrapperName??""),n(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}),i.push(...this._extraEntries),Object.freeze(i)}}function n(t,e,i){i&&i.length>0&&t.push({name:e,value:i})}function a(t,e,i){i!==0&&t.push({name:e,value:String(i)})}
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
- * Resolve the async `expo-network` fields into the cache. Called
77
- * from the constructor as fire-and-forget; partners can also
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{UseSmileIDMetadata as D}from"./use_smile_id_metadata";import{loadAsyncStorage as O,loadExpoBattery as C,loadExpoCellular as P,loadExpoDevice as l,loadExpoNetwork as M}from"./optional_native_modules";import{resolveGeolocation as U}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",k="12.0.0",N="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;_deviceIntegrity;_clientIp="";_networkConnection="";_hardwareAttestation;_deviceProfile;constructor(e={}){this._sessionId=generateSessionId(),this._deviceOs=x(),this._appName=e.appName??"",this._hostApplication=e.hostApplication??"";const t=l();t?(this._deviceModel=resolveDeviceModel(t),this._systemArchitecture=t.supportedCpuArchitectures?.[0]?.trim()??"",this._memoryInfoMb=typeof t.totalMemory=="number"&&t.totalMemory>0?Math.round(t.totalMemory/(1024*1024)):0,this._buildBrand=t.brand?.trim()??"",this._buildDevice=(r.OS==="ios"?t.modelId:t.designName)?.trim()??"",this._buildFingerprint=t.osBuildFingerprint?.trim()??"",this._buildProduct=t.productName?.trim()??""):(this._deviceModel="",this._systemArchitecture="",this._memoryInfoMb=0,this._buildBrand="",this._buildDevice="",this._buildFingerprint="",this._buildProduct=""),this._carrierInfo=P()?.carrier?.trim()??"",this._warmupAsync(),this._warmUpHardwareAttestation(),this._warmUpDeviceProfile()}async _warmUpDeviceProfile(){try{this._deviceProfile=await I.deviceProfile()}catch{}}get sessionId(){return this._sessionId}async warmup(){return this._warmupAsync()}async _warmupAsync(){const e=M();if(!e)return;const t=e.getNetworkStateAsync().then(s=>{this._networkConnection=(s.type??"").toString()}).catch(()=>{}),n=e.getIpAddressAsync?e.getIpAddressAsync().then(s=>{this._clientIp=s??""}).catch(()=>{}):Promise.resolve();await Promise.all([t,n,this._warmUpBatteryStatus(),this._warmUpLaunchCount(),this._warmUpGeolocation(),this._warmUpPrivileges(),this._warmUpDeviceIntegrity()])}async _warmUpGeolocation(){const e=await U();e&&(this._geolocation=e)}async _warmUpPrivileges(){const e=await A.privileges();e.length>0&&(this._privileges=e)}async _warmUpDeviceIntegrity(){this._deviceIntegrity=await S.signals()}async _warmUpLaunchCount(){const e=O();if(e)try{const n=(Number(await e.getItem(p))||0)+1;await e.setItem(p,String(n)),this._sdkLaunchCount=n}catch{}}async _warmUpBatteryStatus(){const e=C();if(e)try{switch(await e.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(e){const t=this._hardwareAttestation,n=r.OS==="android"&&t?{hardwareAttestationSecurityLevel:t.securityLevel,...t.certificateChain&&t.certificateChain.length>0?{attestationCertificateChain:t.certificateChain}:{}}:{},s=r.OS==="ios"&&t?{supportsAppAttest:t.supportsAppAttest}:{},a=u(e?.selfie),_=e!==void 0?h.sdkConfig:h.defaultSource,f=a?.width??o.selfieJpegTargetWidth,g=a?.height??o.selfieJpegTargetHeight,c=u(e?.liveness),v=c?.width??o.selfieJpegTargetWidth,y=c?.height??o.selfieJpegTargetHeight,w={sdkVersion:m,sdk:N,deviceOs:this._deviceOs,deviceModel:this._deviceModel,appName:this._appName,sessionId:this._sessionId,clientIp:this._clientIp,networkConnection:this._networkConnection,carrierInfo:this._carrierInfo,timezone:L(),timezoneOffsetMinutes:B(),locale:E(),screenResolution:H(),memoryInfoMb:this._memoryInfoMb,deviceOrientation:F(),systemArchitecture:this._systemArchitecture,numberOfCameras:0,hostApplication:this._hostApplication,securityPolicyVersion:k,...n,...s,sdkConfigSource:_,imageWidth:f,imageHeight:g,livenessImageWidth:v,livenessImageHeight:y,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:R(),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._deviceProfile!==void 0?{deviceTier:this._deviceProfile.tier,deviceProfile:deviceProfileMetadataJson(this._deviceProfile)}:{}};return new D(w)}}export function deviceProfileMetadataJson(i){const e={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(e).filter(([,t])=>t!==null)))}export function resolveDeviceModel(i=l()){if(!i)return"";const e=i.brand?.trim()??"",t=i.modelName?.trim()??"";return[e,t].filter(Boolean).join(" ")}function x(){const i=r.OS==="ios"?"iOS":r.OS==="android"?"Android":r.OS,e=T();return e?`${i} ${e}`: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 R(){try{return new Date().toISOString()}catch{return""}}function L(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone??""}catch{return""}}function B(){return-new Date().getTimezoneOffset()}function E(){try{return Intl.DateTimeFormat().resolvedOptions().locale??""}catch{return""}}function H(){try{const{width:i,height:e,scale:t}=d.get("screen");if(!i||!e)return"";const n=Math.round(i*(t||1)),s=Math.round(e*(t||1));return`${n} \xD7 ${s}`}catch{return""}}function F(){try{const{width:i,height:e}=d.get("window");return!i||!e?"":e>=i?"Portrait":"Landscape"}catch{return""}}export function generateSessionId(){const i=globalThis.crypto;if(i&&typeof i.randomUUID=="function")return i.randomUUID();const e=()=>typeof i?.getRandomValues=="function"?i.getRandomValues(new Uint16Array(1))[0]??0:Math.floor(Math.random()*65536),t=()=>e().toString(16).padStart(4,"0");return[t()+t(),t(),"4"+t().slice(1),(parseInt(t(),16)&16383|32768).toString(16),t()+t()+t()].join("-")}
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 n}from"@smileid/kamera";export function opensCamera(e){return e.screens.steps.some(r=>r.kind==="capture")}export function prewarmCameraIfNeeded(e,r=()=>n.prewarm()){opensCamera(e)&&r()}
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 n}from"react";import{FlowInvalidDispatcher as t,FlowSuccessRenderer as s}from"../use_smile_id_flow";import{UseSmileIDFlowBuilder as u}from"./use_smile_id_flow_builder";export function UseSmileIDBuilder({builder:i}){const{result:e,invalidMeta:l}=n(()=>{const o=new u;return i(o),{result:o.build(),invalidMeta:{onResult:o.onResult,enableDebugMode:o._config?.build().enableDebugMode===!0}}},[]);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 u;return i(e),e.build()}export{UseSmileIDFlowBuilder}from"./use_smile_id_flow_builder";
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
- * Runs a pre-flight check of the current builder configuration and
150
- * returns a `ValidationState` describing any blocking issues, without
151
- * throwing. Checks the screens and their own settings; job-level rules
152
- * run only in `build()`.
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
- validate(): ValidationState;
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 every rule passed, or `{kind: 'invalid', ...}` carrying the
198
- * issues, the partner's `onResult` callback, and the resolved
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()`.